]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java
Disable the edit button in the edit bookmark folder dialog unless some informaiton...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / helpers / BookmarksDatabaseHelper.java
1 /*
2  * Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
6  * Privacy Browser is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.helpers;
21
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.database.Cursor;
25 import android.database.DatabaseUtils;
26 import android.database.sqlite.SQLiteDatabase;
27 import android.database.sqlite.SQLiteOpenHelper;
28
29 public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
30     private static final int SCHEMA_VERSION = 1;
31     private static final String BOOKMARKS_DATABASE = "bookmarks.db";
32     private static final String BOOKMARKS_TABLE = "bookmarks";
33
34     public static final String _ID = "_id";
35     public static final String DISPLAY_ORDER = "displayorder";
36     public static final String BOOKMARK_NAME = "bookmarkname";
37     public static final String BOOKMARK_URL = "bookmarkurl";
38     public static final String PARENT_FOLDER = "parentfolder";
39     public static final String IS_FOLDER = "isfolder";
40     public static final String FAVORITE_ICON = "favoriteicon";
41
42     // Initialize the database.  The lint warnings for the unused parameters are suppressed.
43     public BookmarksDatabaseHelper(Context context, @SuppressWarnings("UnusedParameters") String name, SQLiteDatabase.CursorFactory cursorFactory, @SuppressWarnings("UnusedParameters") int version) {
44         super(context, BOOKMARKS_DATABASE, cursorFactory, SCHEMA_VERSION);
45     }
46
47     @Override
48     public void onCreate(SQLiteDatabase bookmarksDatabase) {
49         // Setup the SQL string to create the `bookmarks` table.
50         final String CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" +
51                 _ID + " integer primary key, " +
52                 DISPLAY_ORDER + " integer, " +
53                 BOOKMARK_NAME + " text, " +
54                 BOOKMARK_URL + " text, " +
55                 PARENT_FOLDER + " text, " +
56                 IS_FOLDER + " boolean, " +
57                 FAVORITE_ICON + " blob);";
58
59         // Create the `bookmarks` table.
60         bookmarksDatabase.execSQL(CREATE_BOOKMARKS_TABLE);
61     }
62
63     @Override
64     public void onUpgrade(SQLiteDatabase bookmarksDatabase, int oldVersion, int newVersion) {
65         // Code for upgrading the database will be added here when the schema version > 1.
66     }
67
68     // Create a bookmark.
69     public void createBookmark(String bookmarkName, String bookmarkURL, int displayOrder, String parentFolder, byte[] favoriteIcon) {
70         // We need to store the bookmark data in a `ContentValues`.
71         ContentValues bookmarkContentValues = new ContentValues();
72
73         // ID is created automatically.
74         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
75         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
76         bookmarkContentValues.put(BOOKMARK_URL, bookmarkURL);
77         bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
78         bookmarkContentValues.put(IS_FOLDER, false);
79         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
80
81         // Get a writable database handle.
82         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
83
84         // Insert a new row.  The second argument is `null`, which makes it so that a completely null row cannot be created.
85         bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkContentValues);
86
87         // Close the database handle.
88         bookmarksDatabase.close();
89     }
90
91     // Create a folder.
92     public void createFolder(String folderName, int displayOrder, String parentFolder, byte[] favoriteIcon) {
93         ContentValues bookmarkContentValues = new ContentValues();
94
95         // ID is created automatically.
96         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
97         bookmarkContentValues.put(BOOKMARK_NAME, folderName);
98         bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
99         bookmarkContentValues.put(IS_FOLDER, true);
100         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
101
102         // Get a writable database handle.
103         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
104
105         // The second argument is `null`, which makes it so that completely null rows cannot be created.  Not a problem in our case.
106         bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkContentValues);
107
108         // Close the database handle.
109         bookmarksDatabase.close();
110     }
111
112     // Get a `Cursor` for the bookmark with the specified database ID.
113     public Cursor getBookmarkCursor(int databaseId) {
114         // Get a readable database handle.
115         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
116
117         // Prepare the SQL statement to get the `Cursor` for `databaseId`
118         final String GET_ONE_BOOKMARK = "SELECT * FROM " + BOOKMARKS_TABLE +
119                 " WHERE " + _ID + " = " + databaseId;
120
121         // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.  We can't close the `Cursor` because we need to use it in the parent activity.
122         return bookmarksDatabase.rawQuery(GET_ONE_BOOKMARK, null);
123     }
124
125     // Get the folder name for the specified database ID.
126     public String getFolderName (int databaseId) {
127         // Get a readable database handle.
128         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
129
130         // Prepare the SQL statement to get the `Cursor` for the folder.
131         final String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
132                 " WHERE " + _ID + " = " + databaseId;
133
134         // Get `folderCursor`.  The second argument is `null` because there are no `selectionArgs`.
135         Cursor folderCursor = bookmarksDatabase.rawQuery(GET_FOLDER, null);
136
137         // Get `folderName`.
138         folderCursor.moveToFirst();
139         String folderName = folderCursor.getString(folderCursor.getColumnIndex(BOOKMARK_NAME));
140
141         // Close the cursor and the database handle.
142         folderCursor.close();
143         bookmarksDatabase.close();
144
145         // Return the folder name.
146         return folderName;
147     }
148
149     // Get a `Cursor` for the specified folder name.
150     public Cursor getFolderCursor(String folderName) {
151         // Get a readable database handle.
152         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
153
154         // SQL escape `folderName`.
155         folderName = DatabaseUtils.sqlEscapeString(folderName);
156
157         // Prepare the SQL statement to get the `Cursor` for the folder.
158         final String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
159                 " WHERE " + BOOKMARK_NAME + " = " + folderName +
160                 " AND " + IS_FOLDER + " = " + 1;
161
162         // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
163         // We can't close the `Cursor` because we need to use it in the parent activity.
164         return bookmarksDatabase.rawQuery(GET_FOLDER, null);
165     }
166
167     // Get a `Cursor` of all the folders except those specified.
168     public Cursor getFoldersCursorExcept(String exceptFolders) {
169         // Get a readable database handle.
170         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
171
172         // Prepare the SQL statement to get the `Cursor` for the folders.
173         final String GET_FOLDERS_EXCEPT = "SELECT * FROM " + BOOKMARKS_TABLE +
174                 " WHERE " + IS_FOLDER + " = " + 1 +
175                 " AND " + BOOKMARK_NAME + " NOT IN (" + exceptFolders +
176                 ") ORDER BY " + BOOKMARK_NAME + " ASC";
177
178         // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
179         // We can't close the `Cursor` because we need to use it in the parent activity.
180         return bookmarksDatabase.rawQuery(GET_FOLDERS_EXCEPT, null);
181     }
182
183     // Get a `Cursor` with all the subfolders of the specified folder.
184     public Cursor getSubfoldersCursor(String currentFolder) {
185         // Get a readable database handle.
186         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
187
188         // SQL escape `currentFolder.
189         currentFolder = DatabaseUtils.sqlEscapeString(currentFolder);
190
191         // Prepare the SQL statement to get the `Cursor` for the subfolders.
192         final String GET_SUBFOLDERS = "SELECT * FROM " + BOOKMARKS_TABLE +
193                 " WHERE " + PARENT_FOLDER + " = " + currentFolder +
194                 " AND " + IS_FOLDER + " = " + 1;
195
196         // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
197         // We can't close the `Cursor` because we need to use it in the parent activity.
198         return bookmarksDatabase.rawQuery(GET_SUBFOLDERS, null);
199     }
200
201     // Get a `String` with the name of the parent folder.
202     public String getParentFolder(String currentFolder) {
203         // Get a readable database handle.
204         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
205
206         // SQL escape `currentFolder`.
207         currentFolder = DatabaseUtils.sqlEscapeString(currentFolder);
208
209         // Prepare the SQL statement to get the parent folder.
210         final String GET_PARENT_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
211                 " WHERE " + IS_FOLDER + " = " + 1 +
212                 " AND " + BOOKMARK_NAME + " = " + currentFolder;
213
214         // The second argument is `null` because there are no `selectionArgs`.
215         Cursor bookmarkCursor = bookmarksDatabase.rawQuery(GET_PARENT_FOLDER, null);
216         bookmarkCursor.moveToFirst();
217
218         // Store the name of the parent folder.
219         String parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(PARENT_FOLDER));
220
221         // Close the `Cursor`.
222         bookmarkCursor.close();
223
224         return parentFolder;
225     }
226
227     // Get a `Cursor` of all the folders.
228     public Cursor getAllFoldersCursor() {
229         // Get a readable database handle.
230         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
231
232         // Prepare the SQL statement to get the `Cursor` for all the folders.
233         final String GET_ALL_FOLDERS = "SELECT * FROM " + BOOKMARKS_TABLE +
234                 " WHERE " + IS_FOLDER + " = " + 1;
235
236         // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
237         // We can't close the `Cursor` because we need to use it in the parent activity.
238         return bookmarksDatabase.rawQuery(GET_ALL_FOLDERS, null);
239     }
240
241     // Get a `Cursor` for all bookmarks and folders.
242     public Cursor getAllBookmarksCursor() {
243         // Get a readable database handle.
244         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
245
246         // Get everything in `BOOKMARKS_TABLE`.
247         final String GET_ALL_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE;
248
249         // Return the results as a Cursor.  The second argument is `null` because there are no selectionArgs.
250         // We can't close the Cursor because we need to use it in the parent activity.
251         return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS, null);
252     }
253
254     // Get a `Cursor` for all bookmarks and folders in the specified folder ordered by display order.
255     public Cursor getAllBookmarksCursorByDisplayOrder(String folderName) {
256         // Get a readable database handle.
257         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
258
259         // SQL escape `folderName`.
260         folderName = DatabaseUtils.sqlEscapeString(folderName);
261
262         // Get everything in the `BOOKMARKS_TABLE` with `folderName` as the `PARENT_FOLDER`.
263         final String GET_ALL_BOOKMARKS = "SELECT * FROM " + BOOKMARKS_TABLE +
264                 " WHERE " + PARENT_FOLDER + " = " + folderName +
265                 " ORDER BY " + DISPLAY_ORDER + " ASC";
266
267         // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.  We can't close the `Cursor` because we need to use it in the parent activity.
268         return bookmarksDatabase.rawQuery(GET_ALL_BOOKMARKS, null);
269     }
270
271     // Get a `Cursor` for all bookmarks and folders in the specified folder except for a specific list of IDs.
272     public Cursor getBookmarksCursorExcept(long[] exceptIdLongArray, String folderName) {
273         // Get a readable database handle.
274         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
275
276         // Prepare a string that contains the comma-separated list of IDs not to get.
277         String doNotGetIdsString = "";
278         // Extract the array to `doNotGetIdsString`.
279         for (long databaseIdLong : exceptIdLongArray) {
280             // If this is the first number, only add the number.
281             if (doNotGetIdsString.isEmpty()) {
282                 doNotGetIdsString = String.valueOf(databaseIdLong);
283             } else {  // If there already is a number in the string, place a `,` before the number.
284                 doNotGetIdsString = doNotGetIdsString + "," + databaseIdLong;
285             }
286         }
287
288         // SQL escape `folderName`.
289         folderName = DatabaseUtils.sqlEscapeString(folderName);
290
291         // Prepare the SQL statement to select all items except those with the specified IDs.
292         final String GET_All_BOOKMARKS_EXCEPT_SPECIFIED = "SELECT * FROM " + BOOKMARKS_TABLE +
293                 " WHERE " + PARENT_FOLDER + " = " + folderName +
294                 " AND " + _ID + " NOT IN (" + doNotGetIdsString +
295                 ") ORDER BY " + DISPLAY_ORDER + " ASC";
296
297         // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
298         // We can't close the `Cursor` because we need to use it in the parent activity.
299         return bookmarksDatabase.rawQuery(GET_All_BOOKMARKS_EXCEPT_SPECIFIED, null);
300     }
301
302     // Check if a database ID is a folder.
303     public boolean isFolder(int databaseId) {
304         // Get a readable database handle.
305         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
306
307         // Prepare the SQL statement to determine if `databaseId` is a folder.
308         final String CHECK_IF_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
309                 " WHERE " + _ID + " = " + databaseId;
310
311         // Populate folderCursor.  The second argument is `null` because there are no `selectionArgs`.
312         Cursor folderCursor = bookmarksDatabase.rawQuery(CHECK_IF_FOLDER, null);
313
314         // Ascertain if this database ID is a folder.
315         folderCursor.moveToFirst();
316         boolean isFolder = (folderCursor.getInt(folderCursor.getColumnIndex(IS_FOLDER)) == 1);
317
318         // Close the `Cursor` and the database handle.
319         folderCursor.close();
320         bookmarksDatabase.close();
321
322         return isFolder;
323     }
324
325     // Update the bookmark name and URL.
326     public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl) {
327         // Store the updated values in `bookmarkContentValues`.
328         ContentValues bookmarkContentValues = new ContentValues();
329
330         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
331         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
332
333         // Get a writable database handle.
334         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
335
336         // Update the bookmark.  The last argument is `null` because there are no `whereArgs`.
337         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
338
339         // Close the database handle.
340         bookmarksDatabase.close();
341     }
342
343     // Update the bookmark name, URL, and favorite icon.
344     public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl, byte[] favoriteIcon) {
345         // Store the updated values in `bookmarkContentValues`.
346         ContentValues bookmarkContentValues = new ContentValues();
347
348         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
349         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
350         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
351
352         // Get a writable database handle.
353         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
354
355         // Update the bookmark.  The last argument is `null` because there are no `whereArgs`.
356         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
357
358         // Close the database handle.
359         bookmarksDatabase.close();
360     }
361
362     // Update the folder name.
363     public void updateFolder(int databaseId, String oldFolderName, String newFolderName) {
364         // Get a writable database handle.
365         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
366
367         // Update the folder first.  Store the new folder name in `folderContentValues`.
368         ContentValues folderContentValues = new ContentValues();
369         folderContentValues.put(BOOKMARK_NAME, newFolderName);
370
371         // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
372         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
373
374         // Update the bookmarks inside the folder.  Store the new parent folder name in `bookmarkContentValues`.
375         ContentValues bookmarkContentValues = new ContentValues();
376         bookmarkContentValues.put(PARENT_FOLDER, newFolderName);
377
378         // SQL escape `oldFolderName`.
379         oldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName);
380
381         // Run the update on all the bookmarks that currently list `oldFolderName` as their parent folder.  The last argument is `null` because there are no `whereArgs`.
382         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, PARENT_FOLDER + " = " + oldFolderName, null);
383
384         // Close the database handle.
385         bookmarksDatabase.close();
386     }
387
388     // Update the folder icon.
389     public void updateFolder(int databaseId, byte[] folderIcon) {
390         // Get a writable database handle.
391         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
392
393         // Store the updated icon in `folderContentValues`.
394         ContentValues folderContentValues = new ContentValues();
395         folderContentValues.put(FAVORITE_ICON, folderIcon);
396
397         // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
398         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
399
400         // Close the database handle.
401         bookmarksDatabase.close();
402     }
403
404     // Update the folder name and icon.
405     public void updateFolder(int databaseId, String oldFolderName, String newFolderName, byte[] folderIcon) {
406         // Get a writable database handle.
407         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
408
409         // Update the folder first.  Store the updated values in `folderContentValues`.
410         ContentValues folderContentValues = new ContentValues();
411         folderContentValues.put(BOOKMARK_NAME, newFolderName);
412         folderContentValues.put(FAVORITE_ICON, folderIcon);
413
414         // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
415         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
416
417         // Update the bookmarks inside the folder.  Store the new parent folder name in `bookmarkContentValues`.
418         ContentValues bookmarkContentValues = new ContentValues();
419         bookmarkContentValues.put(PARENT_FOLDER, newFolderName);
420
421         // SQL escape `oldFolderName`.
422         oldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName);
423
424         // Run the update on all the bookmarks that currently list `oldFolderName` as their parent folder.  The last argument is `null` because there are no `whereArgs`.
425         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, PARENT_FOLDER + " = " + oldFolderName, null);
426
427         // Close the database handle.
428         bookmarksDatabase.close();
429     }
430
431     // Update the display order for one bookmark or folder.
432     public void updateDisplayOrder(int databaseId, int displayOrder) {
433         // Get a writable database handle.
434         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
435
436         // Store the new display order in `bookmarkContentValues`.
437         ContentValues bookmarkContentValues = new ContentValues();
438         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
439
440         // Update the database.  The last argument is `null` because there are no `whereArgs`.
441         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
442
443         // Close the database handle.
444         bookmarksDatabase.close();
445     }
446
447     // Move one bookmark or folder to a new folder.
448     public void moveToFolder(int databaseId, String newFolder) {
449         // Get a writable database handle.
450         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
451
452         // SQL escape the new folder name.
453         String newFolderSqlEscaped = DatabaseUtils.sqlEscapeString(newFolder);
454
455         // Prepare a SQL query to select all the bookmarks in the new folder.
456         final String NEW_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
457                 " WHERE " + PARENT_FOLDER + " = " + newFolderSqlEscaped +
458                 " ORDER BY " + DISPLAY_ORDER + " ASC";
459
460         // Get a cursor for all the bookmarks in the new folder.  The second argument is `null` because there are no `selectionArgs`.
461         Cursor newFolderCursor = bookmarksDatabase.rawQuery(NEW_FOLDER, null);
462
463         // Instantiate a variable to store the display order after the move.
464         int displayOrder;
465
466         // Set the new display order.
467         if (newFolderCursor.getCount() > 0) {  // There are already bookmarks in the folder.
468             // Move to the last bookmark.
469             newFolderCursor.moveToLast();
470
471             // Set the display order to be one greater that the last bookmark.
472             displayOrder = newFolderCursor.getInt(newFolderCursor.getColumnIndex(DISPLAY_ORDER)) + 1;
473         } else {  // There are no bookmarks in the new folder.
474             // Set the display order to be `0`.
475             displayOrder = 0;
476         }
477
478         // Close the new folder `Cursor`.
479         newFolderCursor.close();
480
481         // Store the new values in `bookmarkContentValues`.
482         ContentValues bookmarkContentValues = new ContentValues();
483         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
484         bookmarkContentValues.put(PARENT_FOLDER, newFolder);
485
486         // Update the database.  The last argument is `null` because there are no `whereArgs`.
487         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
488
489         // Close the database handle.
490         bookmarksDatabase.close();
491     }
492
493     // Delete one bookmark.
494     public void deleteBookmark(int databaseId) {
495         // Get a writable database handle.
496         SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
497
498         // Deletes the row with the given `databaseId`.  The last argument is `null` because we don't need additional parameters.
499         bookmarksDatabase.delete(BOOKMARKS_TABLE, _ID + " = " + databaseId, null);
500
501         // Close the database handle.
502         bookmarksDatabase.close();
503     }
504 }