]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt
Detect the Google Play flavor of I2P. https://redmine.stoutner.com/issues/895
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / helpers / BookmarksDatabaseHelper.kt
1 /*
2  * Copyright © 2016-2022 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
5  *
6  * Privacy Browser Android 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 Android 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 Android.  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 // Define the class constants.
30 private const val SCHEMA_VERSION = 1
31
32 class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOKMARKS_DATABASE, null, SCHEMA_VERSION) {
33     // Define the public companion object constants.  These can be moved to public class constants once the entire project has migrated to Kotlin.
34     companion object {
35         // Define the public database constants.
36         const val BOOKMARKS_DATABASE = "bookmarks.db"
37         const val BOOKMARKS_TABLE = "bookmarks"
38
39         // Define the public schema constants.
40         const val ID = "_id"
41         const val BOOKMARK_NAME = "bookmarkname"
42         const val BOOKMARK_URL = "bookmarkurl"
43         const val PARENT_FOLDER = "parentfolder"
44         const val DISPLAY_ORDER = "displayorder"
45         const val IS_FOLDER = "isfolder"
46         const val FAVORITE_ICON = "favoriteicon"
47
48         // Define the public table creation constant.
49         const val CREATE_BOOKMARKS_TABLE = "CREATE TABLE $BOOKMARKS_TABLE (" +
50                 "$ID INTEGER PRIMARY KEY, " +
51                 "$BOOKMARK_NAME TEXT, " +
52                 "$BOOKMARK_URL TEXT, " +
53                 "$PARENT_FOLDER TEXT, " +
54                 "$DISPLAY_ORDER INTEGER, " +
55                 "$IS_FOLDER BOOLEAN, " +
56                 "$FAVORITE_ICON BLOB)"
57     }
58
59     override fun onCreate(bookmarksDatabase: SQLiteDatabase) {
60         // Create the bookmarks table.
61         bookmarksDatabase.execSQL(CREATE_BOOKMARKS_TABLE)
62     }
63
64     override fun onUpgrade(bookmarksDatabase: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
65         // Code for upgrading the database will be added here when the schema version > 1.
66     }
67
68     // Create a bookmark.
69     fun createBookmark(bookmarkName: String, bookmarkURL: String, parentFolder: String, displayOrder: Int, favoriteIcon: ByteArray) {
70         // Store the bookmark data in a content values.
71         val bookmarkContentValues = ContentValues()
72
73         // The ID is created automatically.
74         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
75         bookmarkContentValues.put(BOOKMARK_URL, bookmarkURL)
76         bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
77         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
78         bookmarkContentValues.put(IS_FOLDER, false)
79         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon)
80
81         // Get a writable database handle.
82         val bookmarksDatabase = this.writableDatabase
83
84         // Insert a new row.
85         bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkContentValues)
86
87         // Close the database handle.
88         bookmarksDatabase.close()
89     }
90
91     // Create a bookmark from content values.
92     fun createBookmark(contentValues: ContentValues) {
93         // Get a writable database.
94         val bookmarksDatabase = this.writableDatabase
95
96         // Insert a new row.
97         bookmarksDatabase.insert(BOOKMARKS_TABLE, null, contentValues)
98
99         // Close the database handle.
100         bookmarksDatabase.close()
101     }
102
103     // Create a folder.
104     fun createFolder(folderName: String, parentFolder: String, favoriteIcon: ByteArray) {
105         // Store the bookmark folder data in a content values.
106         val bookmarkFolderContentValues = ContentValues()
107
108         // The ID is created automatically.  Folders are always created at the top of the list.
109         bookmarkFolderContentValues.put(BOOKMARK_NAME, folderName)
110         bookmarkFolderContentValues.put(PARENT_FOLDER, parentFolder)
111         bookmarkFolderContentValues.put(DISPLAY_ORDER, 0)
112         bookmarkFolderContentValues.put(IS_FOLDER, true)
113         bookmarkFolderContentValues.put(FAVORITE_ICON, favoriteIcon)
114
115         // Get a writable database handle.
116         val bookmarksDatabase = this.writableDatabase
117
118         // Insert the new folder.
119         bookmarksDatabase.insert(BOOKMARKS_TABLE, null, bookmarkFolderContentValues)
120
121         // Close the database handle.
122         bookmarksDatabase.close()
123     }
124
125     // Get a cursor for the bookmark with the specified database ID.
126     fun getBookmark(databaseId: Int): Cursor {
127         // Get a readable database handle.
128         val bookmarksDatabase = this.readableDatabase
129
130         // Return the cursor for the database ID.  The cursor can't be closed because it is used in the parent activity.
131         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
132     }
133
134     // Get the folder name for the specified database ID.
135     fun getFolderName(databaseId: Int): String {
136         // Get a readable database handle.
137         val bookmarksDatabase = this.readableDatabase
138
139         // Get the cursor for the folder with the specified database ID.
140         val folderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
141
142         // Move to the first record.
143         folderCursor.moveToFirst()
144
145         // Get the folder name.
146         val folderName = folderCursor.getString(folderCursor.getColumnIndexOrThrow(BOOKMARK_NAME))
147
148         // Close the cursor and the database handle.
149         folderCursor.close()
150         bookmarksDatabase.close()
151
152         // Return the folder name.
153         return folderName
154     }
155
156     // Get the database ID for the specified folder name.
157     fun getFolderDatabaseId(folderName: String): Int {
158         // Get a readable database handle.
159         val bookmarksDatabase = this.readableDatabase
160
161         // SQL escape the folder name.
162         val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
163
164         // Get the cursor for the folder with the specified name.
165         val folderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $BOOKMARK_NAME = $sqlEscapedFolderName AND $IS_FOLDER = 1", null)
166
167         // Move to the first record.
168         folderCursor.moveToFirst()
169
170         // Get the database ID.
171         val databaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(ID))
172
173         // Close the cursor and the database handle.
174         folderCursor.close()
175         bookmarksDatabase.close()
176
177         // Return the database ID.
178         return databaseId
179     }
180
181     // Get a cursor for the specified folder name.
182     fun getFolder(folderName: String): Cursor {
183         // Get a readable database handle.
184         val bookmarksDatabase = this.readableDatabase
185
186         // SQL escape the folder name.
187         val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
188
189         // Return the cursor for the specified folder.  The cursor can't be closed because it is used in the parent activity.
190         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $BOOKMARK_NAME = $sqlEscapedFolderName AND $IS_FOLDER = 1", null)
191     }
192
193     // Get a cursor of all the folders except those specified.
194     fun getFoldersExcept(exceptFolders: String): Cursor {
195         // Get a readable database handle.
196         val bookmarksDatabase = this.readableDatabase
197
198         // Return the cursor of all folders except those specified.  Each individual folder in the list has already been SQL escaped.  The cursor can't be closed because it is used in the parent activity.
199         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 AND $BOOKMARK_NAME NOT IN ($exceptFolders) ORDER BY $BOOKMARK_NAME ASC", null)
200     }
201
202     // Get a cursor with all the subfolders of the specified folder.
203     fun getSubfolders(currentFolder: String): Cursor {
204         // Get a readable database handle.
205         val bookmarksDatabase = this.readableDatabase
206
207         // SQL escape the current folder.
208         val sqlEscapedCurrentFolder = DatabaseUtils.sqlEscapeString(currentFolder)
209
210         // Return the cursor with the subfolders.  The cursor can't be closed because it is used in the parent activity.
211         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedCurrentFolder AND $IS_FOLDER = 1", null)
212     }
213
214     // Get the name of the parent folder.
215     fun getParentFolderName(currentFolder: String): String {
216         // Get a readable database handle.
217         val bookmarksDatabase = this.readableDatabase
218
219         // SQL escape the current folder.
220         val sqlEscapedCurrentFolder = DatabaseUtils.sqlEscapeString(currentFolder)
221
222         // Get a cursor for the current folder.
223         val bookmarkCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 AND $BOOKMARK_NAME = $sqlEscapedCurrentFolder", null)
224
225         // Move to the first record.
226         bookmarkCursor.moveToFirst()
227
228         // Store the name of the parent folder.
229         val parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER))
230
231         // Close the cursor and the database.
232         bookmarkCursor.close()
233         bookmarksDatabase.close()
234
235         // Return the parent folder string.
236         return parentFolder
237     }
238
239     // Get the name of the parent folder.
240     fun getParentFolderName(databaseId: Int): String {
241         // Get a readable database handle.
242         val bookmarksDatabase = this.readableDatabase
243
244         // Get a cursor for the specified database ID.
245         val bookmarkCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
246
247         // Move to the first record.
248         bookmarkCursor.moveToFirst()
249
250         // Store the name of the parent folder.
251         val parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER))
252
253         // Close the cursor and the database.
254         bookmarkCursor.close()
255         bookmarksDatabase.close()
256
257         // Return the parent folder string.
258         return parentFolder
259     }
260
261     // Get a cursor of all the folders.
262     val allFolders: Cursor
263         get() {
264             // Get a readable database handle.
265             val bookmarksDatabase = this.readableDatabase
266
267             // Return the cursor with the all the folders.  The cursor cannot be closed because it is used in the parent activity.
268             return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 ORDER BY $BOOKMARK_NAME ASC", null)
269         }
270
271     // Get a cursor for all bookmarks and folders.
272     val allBookmarks: Cursor
273         get() {
274             // Get a readable database handle.
275             val bookmarksDatabase = this.readableDatabase
276
277             // Return a cursor with the entire contents of the bookmarks table.  The cursor cannot be closed because it is used in the parent activity.
278             return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE", null)
279         }
280
281     // Get a cursor for all bookmarks and folders ordered by display order.
282     val allBookmarksByDisplayOrder: Cursor
283         get() {
284             // Get a readable database handle.
285             val bookmarksDatabase = this.readableDatabase
286
287             // Return a cursor with the entire contents of the bookmarks table ordered by the display order.  The cursor cannot be closed because it is used in the parent activity.
288             return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE ORDER BY $DISPLAY_ORDER ASC", null)
289         }
290
291     // Get a cursor for all bookmarks and folders except those with the specified IDs.
292     fun getAllBookmarksExcept(exceptIdLongArray: LongArray): Cursor {
293         // Get a readable database handle.
294         val bookmarksDatabase = this.readableDatabase
295
296         // Prepare a string builder to contain the comma-separated list of IDs not to get.
297         val idsNotToGetStringBuilder = StringBuilder()
298
299         // Extract the array of IDs not to get to the string builder.
300         for (databaseIdLong in exceptIdLongArray) {
301             // Check to see if there is already a number in the builder.
302             if (idsNotToGetStringBuilder.isNotEmpty()) {
303                 // This is not the first number, so place a `,` before the new number.
304                 idsNotToGetStringBuilder.append(",")
305             }
306
307             // Add the new number to the builder.
308             idsNotToGetStringBuilder.append(databaseIdLong)
309         }
310
311         // Return a cursor with all the bookmarks except those specified.  The cursor cannot be closed because it is used in the parent activity.
312         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder)", null)
313     }
314
315     // Get a cursor for all bookmarks and folders by display order except those with the specified IDs.
316     fun getAllBookmarksByDisplayOrderExcept(exceptIdLongArray: LongArray): Cursor {
317         // Get a readable database handle.
318         val bookmarksDatabase = this.readableDatabase
319
320         // Prepare a string builder to contain the comma-separated list of IDs not to get.
321         val idsNotToGetStringBuilder = StringBuilder()
322
323         // Extract the array of IDs not to get to the string builder.
324         for (databaseIdLong in exceptIdLongArray) {
325             // Check to see if there is already a number in the builder.
326             if (idsNotToGetStringBuilder.isNotEmpty()) {
327                 // This is not the first number, so place a `,` before the new number.
328                 idsNotToGetStringBuilder.append(",")
329             }
330
331             // Add the new number to the builder.
332             idsNotToGetStringBuilder.append(databaseIdLong)
333         }
334
335         // Return a cursor with all the bookmarks except those specified ordered by display order.  The cursor cannot be closed because it is used in the parent activity.
336         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC", null)
337     }
338
339     // Get a cursor for bookmarks and folders in the specified folder.
340     fun getBookmarks(folderName: String): Cursor {
341         // Get a readable database handle.
342         val bookmarksDatabase = this.readableDatabase
343
344         // SQL escape the folder name.
345         val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
346
347         // Return a cursor with all the bookmarks in a specified folder.  The cursor cannot be closed because it is used in the parent activity.
348         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName", null)
349     }
350
351     // Get a cursor for bookmarks and folders in the specified folder ordered by display order.
352     fun getBookmarksByDisplayOrder(folderName: String): Cursor {
353         // Get a readable database handle.
354         val bookmarksDatabase = this.readableDatabase
355
356         // SQL escape the folder name.
357         val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
358
359         // Return a cursor with all the bookmarks in the specified folder ordered by display order.  The cursor cannot be closed because it is used in the parent activity.
360         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName ORDER BY $DISPLAY_ORDER ASC", null)
361     }
362
363     // Get a cursor with just database ID of bookmarks and folders in the specified folder.  This is useful for deleting folders with bookmarks that have favorite icons too large to fit in a cursor.
364     fun getBookmarkIds(folderName: String): Cursor {
365         // Get a readable database handle.
366         val bookmarksDatabase = this.readableDatabase
367
368         // SQL escape the folder name.
369         val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
370
371         // Return a cursor with all the database IDs.  The cursor cannot be closed because it is used in the parent activity.
372         return bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName", null)
373     }
374
375     // Get a cursor for bookmarks and folders in the specified folder except those with the specified IDs.
376     fun getBookmarksExcept(exceptIdLongArray: LongArray, folderName: String): Cursor {
377         // Get a readable database handle.
378         val bookmarksDatabase = this.readableDatabase
379
380         // Prepare a string builder to contain the comma-separated list of IDs not to get.
381         val idsNotToGetStringBuilder = StringBuilder()
382
383         // Extract the array of IDs not to get to the string builder.
384         for (databaseIdLong in exceptIdLongArray) {
385             // Check to see if there is already a number in the builder.
386             if (idsNotToGetStringBuilder.isNotEmpty()) {
387                 // This is not the first number, so place a `,` before the new number.
388                 idsNotToGetStringBuilder.append(",")
389             }
390
391             // Add the new number to the builder.
392             idsNotToGetStringBuilder.append(databaseIdLong)
393         }
394
395         // SQL escape the folder name.
396         val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
397
398         // Return a cursor with all the bookmarks in the specified folder except for those database IDs specified.  The cursor cannot be closed because it is used in the parent activity.
399         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName AND $ID NOT IN ($idsNotToGetStringBuilder)", null)
400     }
401
402     // Get a cursor for bookmarks and folders in the specified folder by display order except those with the specified IDs.
403     fun getBookmarksByDisplayOrderExcept(exceptIdLongArray: LongArray, folderName: String): Cursor {
404         // Get a readable database handle.
405         val bookmarksDatabase = this.readableDatabase
406
407         // Prepare a string builder to contain the comma-separated list of IDs not to get.
408         val idsNotToGetStringBuilder = StringBuilder()
409
410         // Extract the array of IDs not to get to the string builder.
411         for (databaseIdLong in exceptIdLongArray) {
412             // Check to see if there is already a number in the builder.
413             if (idsNotToGetStringBuilder.isNotEmpty()) {
414                 // This is not the first number, so place a `,` before the new number.
415                 idsNotToGetStringBuilder.append(",")
416             }
417
418             // Add the new number to the builder.
419             idsNotToGetStringBuilder.append(databaseIdLong)
420         }
421
422         // SQL escape the folder name.
423         val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
424
425         // Return a cursor with all the bookmarks in the specified folder except for those database IDs specified ordered by display order.
426         // The cursor cannot be closed because it will be used in the parent activity.
427         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName AND $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC",
428             null)
429     }
430
431     // Check if a database ID is a folder.
432     fun isFolder(databaseId: Int): Boolean {
433         // Get a readable database handle.
434         val bookmarksDatabase = this.readableDatabase
435
436         // Get a cursor with the is folder field for the specified database ID.
437         val folderCursor = bookmarksDatabase.rawQuery("SELECT $IS_FOLDER FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
438
439         // Move to the first record.
440         folderCursor.moveToFirst()
441
442         // Ascertain if this database ID is a folder.
443         val isFolder = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1
444
445         // Close the cursor and the database handle.
446         folderCursor.close()
447         bookmarksDatabase.close()
448
449         // Return the folder status.
450         return isFolder
451     }
452
453     // Update the bookmark name and URL.
454     fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String) {
455         // Initialize a content values.
456         val bookmarkContentValues = ContentValues()
457
458         // Store the updated values.
459         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
460         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
461
462         // Get a writable database handle.
463         val bookmarksDatabase = this.writableDatabase
464
465         // Update the bookmark.
466         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
467
468         // Close the database handle.
469         bookmarksDatabase.close()
470     }
471
472     // Update the bookmark name, URL, parent folder, and display order.
473     fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, parentFolder: String, displayOrder: Int) {
474         // Initialize a content values.
475         val bookmarkContentValues = ContentValues()
476
477         // Store the updated values.
478         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
479         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
480         bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
481         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
482
483         // Get a writable database handle.
484         val bookmarksDatabase = this.writableDatabase
485
486         // Update the bookmark.
487         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
488
489         // Close the database handle.
490         bookmarksDatabase.close()
491     }
492
493     // Update the bookmark name, URL, and favorite icon.
494     fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, favoriteIcon: ByteArray) {
495         // Initialize a content values.
496         val bookmarkContentValues = ContentValues()
497
498         // Store the updated values.
499         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
500         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
501         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon)
502
503         // Get a writable database handle.
504         val bookmarksDatabase = this.writableDatabase
505
506         // Update the bookmark.
507         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
508
509         // Close the database handle.
510         bookmarksDatabase.close()
511     }
512
513     // Update the bookmark name, URL, parent folder, display order, and favorite icon.
514     fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, parentFolder: String, displayOrder: Int, favoriteIcon: ByteArray) {
515         // Initialize a content values.
516         val bookmarkContentValues = ContentValues()
517
518         // Store the updated values.
519         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
520         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
521         bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
522         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
523         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon)
524
525         // Get a writable database handle.
526         val bookmarksDatabase = this.writableDatabase
527
528         // Update the bookmark.
529         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
530
531         // Close the database handle.
532         bookmarksDatabase.close()
533     }
534
535     // Update the folder name.
536     fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String) {
537         // Get a writable database handle.
538         val bookmarksDatabase = this.writableDatabase
539
540         // Create a folder content values.
541         val folderContentValues = ContentValues()
542
543         // Store the new folder name.
544         folderContentValues.put(BOOKMARK_NAME, newFolderName)
545
546         // Run the update on the folder.
547         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
548
549         // Create a bookmark content values.
550         val bookmarkContentValues = ContentValues()
551
552         // Store the new parent folder name.
553         bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
554
555         // SQL escape the old folder name.
556         val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
557
558         // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
559         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
560
561         // Close the database handle.
562         bookmarksDatabase.close()
563     }
564
565     // Update the folder icon.
566     fun updateFolder(databaseId: Int, folderIcon: ByteArray) {
567         // Get a writable database handle.
568         val bookmarksDatabase = this.writableDatabase
569
570         // Create a content values.
571         val folderContentValues = ContentValues()
572
573         // Store the updated icon.
574         folderContentValues.put(FAVORITE_ICON, folderIcon)
575
576         // Run the update on the folder.
577         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
578
579         // Close the database handle.
580         bookmarksDatabase.close()
581     }
582
583     // Update the folder name, parent folder, and display order.
584     fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, parentFolder: String, displayOrder: Int) {
585         // Get a writable database handle.
586         val bookmarksDatabase = this.writableDatabase
587
588         // Create a folder content values.
589         val folderContentValues = ContentValues()
590
591         // Store the new folder values.
592         folderContentValues.put(BOOKMARK_NAME, newFolderName)
593         folderContentValues.put(PARENT_FOLDER, parentFolder)
594         folderContentValues.put(DISPLAY_ORDER, displayOrder)
595
596         // Run the update on the folder.
597         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
598
599         // Create a bookmark content values.
600         val bookmarkContentValues = ContentValues()
601
602         // Store the new parent folder name.
603         bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
604
605         // SQL escape the old folder name.
606         val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
607
608         // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
609         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
610
611         // Close the database handle.
612         bookmarksDatabase.close()
613     }
614
615     // Update the folder name and icon.
616     fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, folderIcon: ByteArray) {
617         // Get a writable database handle.
618         val bookmarksDatabase = this.writableDatabase
619
620         // Create a folder content values.
621         val folderContentValues = ContentValues()
622
623         // Store the updated values.
624         folderContentValues.put(BOOKMARK_NAME, newFolderName)
625         folderContentValues.put(FAVORITE_ICON, folderIcon)
626
627         // Run the update on the folder.
628         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
629
630         // Create a bookmark content values.
631         val bookmarkContentValues = ContentValues()
632
633         // Store the new parent folder name.
634         bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
635
636         // SQL escape the old folder name.
637         val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
638
639         // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
640         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
641
642         // Close the database handle.
643         bookmarksDatabase.close()
644     }
645
646     // Update the folder name and icon.
647     fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, parentFolder: String, displayOrder: Int, folderIcon: ByteArray) {
648         // Get a writable database handle.
649         val bookmarksDatabase = this.writableDatabase
650
651         // Create a folder content values.
652         val folderContentValues = ContentValues()
653
654         // Store the updated values.
655         folderContentValues.put(BOOKMARK_NAME, newFolderName)
656         folderContentValues.put(PARENT_FOLDER, parentFolder)
657         folderContentValues.put(DISPLAY_ORDER, displayOrder)
658         folderContentValues.put(FAVORITE_ICON, folderIcon)
659
660         // Run the update on the folder.
661         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
662
663         // Create a bookmark content values.
664         val bookmarkContentValues = ContentValues()
665
666         // Store the new parent folder name.
667         bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
668
669         // SQL escape the old folder name.
670         val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
671
672         // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
673         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
674
675         // Close the database handle.
676         bookmarksDatabase.close()
677     }
678
679     // Update the display order for one bookmark or folder.
680     fun updateDisplayOrder(databaseId: Int, displayOrder: Int) {
681         // Get a writable database handle.
682         val bookmarksDatabase = this.writableDatabase
683
684         // Create a content values.
685         val bookmarkContentValues = ContentValues()
686
687         // Store the new display order.
688         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
689
690         // Update the database.
691         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
692
693         // Close the database handle.
694         bookmarksDatabase.close()
695     }
696
697     // Move one bookmark or folder to a new folder.
698     fun moveToFolder(databaseId: Int, newFolder: String) {
699         // Get a writable database handle.
700         val bookmarksDatabase = this.writableDatabase
701
702         // SQL escape the new folder name.
703         val sqlEscapedNewFolder = DatabaseUtils.sqlEscapeString(newFolder)
704
705         // Get a cursor for all the bookmarks in the new folder ordered by display order.
706         val newFolderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedNewFolder ORDER BY $DISPLAY_ORDER ASC", null)
707
708         // Set the new display order.
709         val displayOrder: Int = if (newFolderCursor.count > 0) {  // There are already bookmarks in the folder.
710             // Move to the last bookmark.
711             newFolderCursor.moveToLast()
712
713             // Set the display order to be one greater that the last bookmark.
714             newFolderCursor.getInt(newFolderCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) + 1
715         } else {  // There are no bookmarks in the new folder.
716             // Set the display order to be `0`.
717             0
718         }
719
720         // Close the cursor.
721         newFolderCursor.close()
722
723         // Create a content values.
724         val bookmarkContentValues = ContentValues()
725
726         // Store the new values.
727         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
728         bookmarkContentValues.put(PARENT_FOLDER, newFolder)
729
730         // Update the database.
731         bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
732
733         // Close the database handle.
734         bookmarksDatabase.close()
735     }
736
737     // Delete one bookmark.
738     fun deleteBookmark(databaseId: Int) {
739         // Get a writable database handle.
740         val bookmarksDatabase = this.writableDatabase
741
742         // Deletes the row with the given database ID.
743         bookmarksDatabase.delete(BOOKMARKS_TABLE, "$ID = $databaseId", null)
744
745         // Close the database handle.
746         bookmarksDatabase.close()
747     }
748 }