]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.kt
First wrong button text in View Headers in night theme. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / helpers / BookmarksDatabaseHelper.kt
index 12753e1652d5947eb2f0e2d07719dee11f136d76..1d1988408d03abfc7a0c202fb97949f86a966c14 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016-2022 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
  *
@@ -23,57 +23,184 @@ import android.content.ContentValues
 import android.content.Context
 import android.database.Cursor
 import android.database.DatabaseUtils
+import android.database.MatrixCursor
+import android.database.MergeCursor
 import android.database.sqlite.SQLiteDatabase
 import android.database.sqlite.SQLiteOpenHelper
 
-// The private constants.
-private const val SCHEMA_VERSION = 1
+import com.stoutner.privacybrowser.activities.HOME_FOLDER_ID
+
+import java.util.Date
+
+// Define the class constants.
+private const val SCHEMA_VERSION = 2
+
+// Define the public database constants.
+const val BOOKMARKS_DATABASE = "bookmarks.db"
+const val BOOKMARKS_TABLE = "bookmarks"
+
+// Define the public schema constants.
+const val BOOKMARK_NAME = "bookmarkname"
+const val BOOKMARK_URL = "bookmarkurl"
+const val DISPLAY_ORDER = "displayorder"
+const val FAVORITE_ICON = "favoriteicon"
+const val FOLDER_ID = "folder_id"
+const val IS_FOLDER = "isfolder"
+const val PARENT_FOLDER_ID = "parent_folder_id"
+
+// Define the public table creation constant.
+const val CREATE_BOOKMARKS_TABLE = "CREATE TABLE $BOOKMARKS_TABLE (" +
+        "$ID INTEGER PRIMARY KEY, " +
+        "$BOOKMARK_NAME TEXT, " +
+        "$BOOKMARK_URL TEXT, " +
+        "$PARENT_FOLDER_ID INTEGER, " +
+        "$DISPLAY_ORDER INTEGER, " +
+        "$IS_FOLDER BOOLEAN, " +
+        "$FOLDER_ID INTEGER, " +
+        "$FAVORITE_ICON BLOB)"
 
 class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOKMARKS_DATABASE, null, SCHEMA_VERSION) {
-    // Define the public companion object constants.  These can be moved to public class constants once the entire project has migrated to Kotlin.
-    companion object {
-        // The database constants.
-        const val BOOKMARKS_DATABASE = "bookmarks.db"
-        const val BOOKMARKS_TABLE = "bookmarks"
-
-        // The schema constants.
-        const val ID = "_id"
-        const val BOOKMARK_NAME = "bookmarkname"
-        const val BOOKMARK_URL = "bookmarkurl"
-        const val PARENT_FOLDER = "parentfolder"
-        const val DISPLAY_ORDER = "displayorder"
-        const val IS_FOLDER = "isfolder"
-        const val FAVORITE_ICON = "favoriteicon"
-
-        // The table creation constant.
-        const val CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" +
-                ID + " INTEGER PRIMARY KEY, " +
-                BOOKMARK_NAME + " TEXT, " +
-                BOOKMARK_URL + " TEXT, " +
-                PARENT_FOLDER + " TEXT, " +
-                DISPLAY_ORDER + " INTEGER, " +
-                IS_FOLDER + " BOOLEAN, " +
-                FAVORITE_ICON + " BLOB)"
-    }
-
     override fun onCreate(bookmarksDatabase: SQLiteDatabase) {
         // Create the bookmarks table.
         bookmarksDatabase.execSQL(CREATE_BOOKMARKS_TABLE)
     }
 
     override fun onUpgrade(bookmarksDatabase: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
-        // Code for upgrading the database will be added here when the schema version > 1.
+        // Upgrade from schema version 1, first used in Privacy Browser 1.8, to schema version 2, first used in Privacy Browser 3.15.
+        if (oldVersion < 2) {
+            // Add the folder ID column.
+            bookmarksDatabase.execSQL("ALTER TABLE $BOOKMARKS_TABLE ADD COLUMN $FOLDER_ID INTEGER")
+
+            // Get a cursor with all the folders.
+            val foldersCursor = bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1", null)
+
+            // Get the folders cursor ID column index.
+            val foldersCursorIdColumnIndex = foldersCursor.getColumnIndexOrThrow(ID)
+
+            // Add a folder ID to each folder.
+            while(foldersCursor.moveToNext()) {
+                // Get the current folder database ID.
+                val databaseId = foldersCursor.getInt(foldersCursorIdColumnIndex)
+
+                // Generate a folder ID.
+                val folderId = Date().time
+
+                // Create a folder content values.
+                val folderContentValues = ContentValues()
+
+                // Store the new folder ID in the content values.
+                folderContentValues.put(FOLDER_ID, folderId)
+
+                // Update the folder with the new folder ID.
+                bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
+
+                // Wait 2 milliseconds to ensure that the next folder ID is unique.
+                Thread.sleep(2)
+            }
+
+            // Close the folders cursor.
+            foldersCursor.close()
+
+
+            // Add the parent folder ID column.
+            bookmarksDatabase.execSQL("ALTER TABLE $BOOKMARKS_TABLE ADD COLUMN $PARENT_FOLDER_ID INTEGER")
+
+            // Get a cursor with all the bookmarks.
+            val bookmarksCursor = bookmarksDatabase.rawQuery("SELECT $ID, parentfolder FROM $BOOKMARKS_TABLE", null)
+
+            // Get the bookmarks cursor ID column index.
+            val bookmarksCursorIdColumnIndex = bookmarksCursor.getColumnIndexOrThrow(ID)
+            val bookmarksCursorParentFolderColumnIndex = bookmarksCursor.getColumnIndexOrThrow("parentfolder")
+
+            // Populate the parent folder ID for each bookmark.
+            while(bookmarksCursor.moveToNext()) {
+                // Get the information from the cursor.
+                val databaseId = bookmarksCursor.getInt(bookmarksCursorIdColumnIndex)
+                val oldParentFolderString = bookmarksCursor.getString(bookmarksCursorParentFolderColumnIndex)
+
+                // Initialize the new parent folder ID.
+                var newParentFolderId = HOME_FOLDER_ID
+
+                // Get the parent folder ID if the bookmark is not in the home folder.
+                if (oldParentFolderString.isNotEmpty()) {
+                    // SQL escape the old parent folder string.
+                    val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(oldParentFolderString)
+
+                    // Get the parent folder cursor.
+                    val parentFolderCursor = bookmarksDatabase.rawQuery("SELECT $FOLDER_ID FROM $BOOKMARKS_TABLE WHERE $BOOKMARK_NAME = $sqlEscapedFolderName AND $IS_FOLDER = 1", null)
+
+                    // Get the new parent folder ID if it exists.
+                    if (parentFolderCursor.count > 0) {
+                        // Move to the first entry.
+                        parentFolderCursor.moveToFirst()
+
+                        // Get the new parent folder ID.
+                        newParentFolderId = parentFolderCursor.getLong(parentFolderCursor.getColumnIndexOrThrow(FOLDER_ID))
+                    }
+
+                    // Close the parent folder cursor.
+                    parentFolderCursor.close()
+                }
+
+                // Create a bookmark content values.
+                val bookmarkContentValues = ContentValues()
+
+                // Store the new parent folder ID in the content values.
+                bookmarkContentValues.put(PARENT_FOLDER_ID, newParentFolderId)
+
+                // Update the folder with the new folder ID.
+                bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+            }
+
+            // Close the bookmarks cursor.
+            bookmarksCursor.close()
+
+            // This upgrade removed the old `parentfolder` string column.
+            // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
+            // It will be a while before that is supported in Android.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
+            // Although a new table could be created and all the data copied to it, I think I will just leave the old parent folder column.  It will be wiped out the next time an import is run.
+        }
     }
 
+    // Get a cursor for all bookmarks and folders.
+    val allBookmarks: Cursor
+        get() {
+            // Get a readable database handle.
+            val bookmarksDatabase = this.readableDatabase
+
+            // Return a cursor with the entire contents of the bookmarks table.  The cursor cannot be closed because it is used in the parent activity.
+            return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE", null)
+        }
+
+    // Get a cursor with just the database IDs of all the bookmarks and folders.  This is useful for counting the number of bookmarks imported.
+    val allBookmarkAndFolderIds: Cursor
+        get() {
+            // Get a readable database handle.
+            val bookmarksDatabase = this.readableDatabase
+
+            // Return a cursor with all the database IDs.  The cursor cannot be closed because it is used in the parent activity.
+            return bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE", null)
+        }
+
+    // Get a cursor for all bookmarks and folders ordered by display order.
+    val allBookmarksByDisplayOrder: Cursor
+        get() {
+            // Get a readable database handle.
+            val bookmarksDatabase = this.readableDatabase
+
+            // 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.
+            return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE ORDER BY $DISPLAY_ORDER ASC", null)
+        }
+
     // Create a bookmark.
-    fun createBookmark(bookmarkName: String, bookmarkURL: String, parentFolder: String, displayOrder: Int, favoriteIcon: ByteArray) {
+    fun createBookmark(bookmarkName: String, bookmarkUrl: String, parentFolderId: Long, displayOrder: Int, favoriteIcon: ByteArray) {
         // Store the bookmark data in a content values.
         val bookmarkContentValues = ContentValues()
 
         // The ID is created automatically.
         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
-        bookmarkContentValues.put(BOOKMARK_URL, bookmarkURL)
-        bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
+        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
+        bookmarkContentValues.put(PARENT_FOLDER_ID, parentFolderId)
         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
         bookmarkContentValues.put(IS_FOLDER, false)
         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon)
@@ -101,15 +228,19 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
     }
 
     // Create a folder.
-    fun createFolder(folderName: String, parentFolder: String, favoriteIcon: ByteArray) {
-        // Store the bookmark folder data in a content values.
+    fun createFolder(folderName: String, parentFolderId: Long, displayOrder: Int, favoriteIcon: ByteArray): Long {
+        // Create a bookmark folder content values.
         val bookmarkFolderContentValues = ContentValues()
 
+        // Generate the folder ID.
+        val folderId = generateFolderId()
+
         // The ID is created automatically.  Folders are always created at the top of the list.
         bookmarkFolderContentValues.put(BOOKMARK_NAME, folderName)
-        bookmarkFolderContentValues.put(PARENT_FOLDER, parentFolder)
-        bookmarkFolderContentValues.put(DISPLAY_ORDER, 0)
+        bookmarkFolderContentValues.put(PARENT_FOLDER_ID, parentFolderId)
+        bookmarkFolderContentValues.put(DISPLAY_ORDER, displayOrder)
         bookmarkFolderContentValues.put(IS_FOLDER, true)
+        bookmarkFolderContentValues.put(FOLDER_ID, folderId)
         bookmarkFolderContentValues.put(FAVORITE_ICON, favoriteIcon)
 
         // Get a writable database handle.
@@ -120,6 +251,21 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
 
         // Close the database handle.
         bookmarksDatabase.close()
+
+        // Return the new folder ID.
+        return folderId
+    }
+
+    // Delete one bookmark.
+    fun deleteBookmark(databaseId: Int) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Deletes the row with the given database ID.
+        bookmarksDatabase.delete(BOOKMARKS_TABLE, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
     }
 
     // Get a cursor for the bookmark with the specified database ID.
@@ -131,301 +277,390 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
         return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
     }
 
-    // Get the folder name for the specified database ID.
-    fun getFolderName(databaseId: Int): String {
+    // Get a cursor for all bookmarks and folders by display order except those with the specified IDs.
+    fun getAllBookmarksByDisplayOrderExcept(exceptIdLongArray: LongArray): Cursor {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // Get the cursor for the folder with the specified database ID.
-        val folderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
-
-        // Move to the first record.
-        folderCursor.moveToFirst()
+        // Prepare a string builder to contain the comma-separated list of IDs not to get.
+        val idsNotToGetStringBuilder = StringBuilder()
 
-        // Get the folder name.
-        val folderName = folderCursor.getString(folderCursor.getColumnIndexOrThrow(BOOKMARK_NAME))
+        // Extract the array of IDs not to get to the string builder.
+        for (databaseIdLong in exceptIdLongArray) {
+            // Check to see if there is already a number in the builder.
+            if (idsNotToGetStringBuilder.isNotEmpty()) {
+                // This is not the first number, so place a `,` before the new number.
+                idsNotToGetStringBuilder.append(",")
+            }
 
-        // Close the cursor and the database handle.
-        folderCursor.close()
-        bookmarksDatabase.close()
+            // Add the new number to the builder.
+            idsNotToGetStringBuilder.append(databaseIdLong)
+        }
 
-        // Return the folder name.
-        return folderName
+        // 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.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC", null)
     }
 
-    // Get the database ID for the specified folder name.
-    fun getFolderDatabaseId(folderName: String): Int {
+    // Get a cursor for all bookmarks and folders except those with the specified IDs.
+    fun getAllBookmarksExcept(exceptIdLongArray: LongArray): Cursor {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // SQL escape the folder name.
-        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+        // Prepare a string builder to contain the comma-separated list of IDs not to get.
+        val idsNotToGetStringBuilder = StringBuilder()
 
-        // Get the cursor for the folder with the specified name.
-        val folderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $BOOKMARK_NAME = $sqlEscapedFolderName AND $IS_FOLDER = 1", null)
+        // Extract the array of IDs not to get to the string builder.
+        for (databaseIdLong in exceptIdLongArray) {
+            // Check to see if there is already a number in the builder.
+            if (idsNotToGetStringBuilder.isNotEmpty()) {
+                // This is not the first number, so place a `,` before the new number.
+                idsNotToGetStringBuilder.append(",")
+            }
 
-        // Move to the first record.
-        folderCursor.moveToFirst()
+            // Add the new number to the builder.
+            idsNotToGetStringBuilder.append(databaseIdLong)
+        }
 
-        // Get the database ID.
-        val databaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(ID))
+        // Return a cursor with all the bookmarks except those specified.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder)", null)
+    }
 
-        // Close the cursor and the database handle.
-        folderCursor.close()
-        bookmarksDatabase.close()
+    // Get a cursor with just the database IDs 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.
+    fun getBookmarkAndFolderIds(parentFolderId: Long): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
 
-        // Return the database ID.
-        return databaseId
+        // Return a cursor with all the database IDs.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId", null)
     }
 
-    // Get a cursor for the specified folder name.
-    fun getFolder(folderName: String): Cursor {
+    // Get a cursor for bookmarks and folders in the specified folder.
+    fun getBookmarks(parentFolderId: Long): Cursor {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // SQL escape the folder name.
-        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+        // Return a cursor with all the bookmarks in a specified folder.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId", null)
+    }
 
-        // Return the cursor for the specified folder.  The cursor can't be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $BOOKMARK_NAME = $sqlEscapedFolderName AND $IS_FOLDER = 1", null)
+    // Get a cursor for bookmarks and folders in the specified folder ordered by display order.
+    fun getBookmarksByDisplayOrder(parentFolderId: Long): Cursor {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
+
+        // 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.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId ORDER BY $DISPLAY_ORDER ASC", null)
     }
 
-    // Get a cursor of all the folders except those specified.
-    fun getFoldersExcept(exceptFolders: String): Cursor {
+    // Get a cursor for bookmarks and folders in the specified folder by display order except those with the specified IDs.
+    fun getBookmarksByDisplayOrderExcept(exceptIdLongArray: LongArray, parentFolderId: Long): Cursor {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // 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.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 AND $BOOKMARK_NAME NOT IN ($exceptFolders) ORDER BY $BOOKMARK_NAME ASC", null)
+        // Prepare a string builder to contain the comma-separated list of IDs not to get.
+        val idsNotToGetStringBuilder = StringBuilder()
+
+        // Extract the array of IDs not to get to the string builder.
+        for (databaseIdLong in exceptIdLongArray) {
+            // Check to see if there is already a number in the builder.
+            if (idsNotToGetStringBuilder.isNotEmpty()) {
+                // This is not the first number, so place a `,` before the new number.
+                idsNotToGetStringBuilder.append(",")
+            }
+
+            // Add the new number to the builder.
+            idsNotToGetStringBuilder.append(databaseIdLong)
+        }
+
+        // Return a cursor with all the bookmarks in the specified folder except for those database IDs specified ordered by display order.
+        // The cursor cannot be closed because it will be used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC", null)
     }
 
-    // Get a cursor with all the subfolders of the specified folder.
-    fun getSubfolders(currentFolder: String): Cursor {
+    // Get a cursor for bookmarks and folders in the specified folder except those with the specified IDs.
+    fun getBookmarksExcept(exceptIdLongArray: LongArray, parentFolderId: Long): Cursor {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // SQL escape the current folder.
-        val sqlEscapedCurrentFolder = DatabaseUtils.sqlEscapeString(currentFolder)
+        // Prepare a string builder to contain the comma-separated list of IDs not to get.
+        val idsNotToGetStringBuilder = StringBuilder()
 
-        // Return the cursor with the subfolders.  The cursor can't be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedCurrentFolder AND $IS_FOLDER = 1", null)
+        // Extract the array of IDs not to get to the string builder.
+        for (databaseIdLong in exceptIdLongArray) {
+            // Check to see if there is already a number in the builder.
+            if (idsNotToGetStringBuilder.isNotEmpty()) {
+                // This is not the first number, so place a `,` before the new number.
+                idsNotToGetStringBuilder.append(",")
+            }
+
+            // Add the new number to the builder.
+            idsNotToGetStringBuilder.append(databaseIdLong)
+        }
+
+        // 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.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $ID NOT IN ($idsNotToGetStringBuilder)", null)
     }
 
-    // Get the name of the parent folder.
-    fun getParentFolderName(currentFolder: String): String {
+    fun getFolderBookmarks(parentFolderId: Long): Cursor {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // SQL escape the current folder.
-        val sqlEscapedCurrentFolder = DatabaseUtils.sqlEscapeString(currentFolder)
+        // Return a cursor with all the bookmarks in the folder.  The cursor cannot be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $parentFolderId AND $IS_FOLDER = 0 ORDER BY $DISPLAY_ORDER ASC", null)
+    }
 
-        // Get a cursor for the current folder.
-        val bookmarkCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 AND $BOOKMARK_NAME = $sqlEscapedCurrentFolder", null)
+    // Get the database ID for the specified folder name.
+    fun getFolderDatabaseId(folderId: Long): Int {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
 
-        // Move to the first record.
-        bookmarkCursor.moveToFirst()
+        // Initialize the database ID.
+        var databaseId = 0
 
-        // Store the name of the parent folder.
-        val parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER))
+        // Get the cursor for the folder with the specified name.
+        val folderCursor = bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE WHERE $FOLDER_ID = $folderId", null)
 
-        // Close the cursor and the database.
-        bookmarkCursor.close()
+        // Get the database ID if it exists.
+        if (folderCursor.count > 0) {
+            // Move to the first record.
+            folderCursor.moveToFirst()
+
+            // Get the database ID.
+            databaseId = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(ID))
+        }
+
+        // Close the cursor and the database handle.
+        folderCursor.close()
         bookmarksDatabase.close()
 
-        // Return the parent folder string.
-        return parentFolder
+        // Return the database ID.
+        return databaseId
     }
 
-    // Get the name of the parent folder.
-    fun getParentFolderName(databaseId: Int): String {
+    // Get the folder ID for the specified folder database ID.
+    fun getFolderId(folderDatabaseId: Int): Long {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // Get a cursor for the specified database ID.
-        val bookmarkCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
+        // Get the cursor for the folder with the specified database ID.
+        val folderCursor = bookmarksDatabase.rawQuery("SELECT $FOLDER_ID FROM $BOOKMARKS_TABLE WHERE $ID = $folderDatabaseId", null)
 
         // Move to the first record.
-        bookmarkCursor.moveToFirst()
+        folderCursor.moveToFirst()
 
-        // Store the name of the parent folder.
-        val parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER))
+        // Get the folder ID.
+        val folderId = folderCursor.getLong(folderCursor.getColumnIndexOrThrow(FOLDER_ID))
 
-        // Close the cursor and the database.
-        bookmarkCursor.close()
+        // Close the cursor and the database handle.
+        folderCursor.close()
         bookmarksDatabase.close()
 
-        // Return the parent folder string.
-        return parentFolder
+        // Return the folder ID.
+        return folderId
     }
 
-    // Get a cursor of all the folders.
-    val allFolders: Cursor
-        get() {
-            // Get a readable database handle.
-            val bookmarksDatabase = this.readableDatabase
-
-            // Return the cursor with the all the folders.  The cursor cannot be closed because it is used in the parent activity.
-            return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 ORDER BY $BOOKMARK_NAME ASC", null)
-        }
+    // Get the folder name for the specified folder ID.
+    fun getFolderName(folderId: Long): String {
+        // Get a readable database handle.
+        val bookmarksDatabase = this.readableDatabase
 
-    // Get a cursor for all bookmarks and folders.
-    val allBookmarks: Cursor
-        get() {
-            // Get a readable database handle.
-            val bookmarksDatabase = this.readableDatabase
+        // Initialize the folder name.
+        var folderName = ""
 
-            // Return a cursor with the entire contents of the bookmarks table.  The cursor cannot be closed because it is used in the parent activity.
-            return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE", null)
-        }
+        // Get the cursor for the folder with the specified folder ID.
+        val folderCursor = bookmarksDatabase.rawQuery("SELECT $BOOKMARK_NAME FROM $BOOKMARKS_TABLE WHERE $FOLDER_ID = $folderId", null)
 
-    // Get a cursor for all bookmarks and folders ordered by display order.
-    val allBookmarksByDisplayOrder: Cursor
-        get() {
-            // Get a readable database handle.
-            val bookmarksDatabase = this.readableDatabase
+        // Get the folder name if it exists.
+        if (folderCursor.count > 0) {
+            // Move to the first record.
+            folderCursor.moveToFirst()
 
-            // 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.
-            return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE ORDER BY $DISPLAY_ORDER ASC", null)
+            // Get the folder name.
+            folderName = folderCursor.getString(folderCursor.getColumnIndexOrThrow(BOOKMARK_NAME))
         }
 
-    // Get a cursor for all bookmarks and folders except those with the specified IDs.
-    fun getAllBookmarksExcept(exceptIdLongArray: LongArray): Cursor {
-        // Get a readable database handle.
-        val bookmarksDatabase = this.readableDatabase
+        // Close the cursor and the database handle.
+        folderCursor.close()
+        bookmarksDatabase.close()
 
+        // Return the folder name.
+        return folderName
+    }
+
+    // Get a cursor of all the folders except those specified.
+    fun getFoldersExcept(exceptFolderIdLongList: List<Long>): Cursor {
         // Prepare a string builder to contain the comma-separated list of IDs not to get.
-        val idsNotToGetStringBuilder = StringBuilder()
+        val folderIdsNotToGetStringBuilder = StringBuilder()
 
         // Extract the array of IDs not to get to the string builder.
-        for (databaseIdLong in exceptIdLongArray) {
+        for (folderId in exceptFolderIdLongList) {
             // Check to see if there is already a number in the builder.
-            if (idsNotToGetStringBuilder.isNotEmpty()) {
+            if (folderIdsNotToGetStringBuilder.isNotEmpty()) {
                 // This is not the first number, so place a `,` before the new number.
-                idsNotToGetStringBuilder.append(",")
+                folderIdsNotToGetStringBuilder.append(",")
             }
 
             // Add the new number to the builder.
-            idsNotToGetStringBuilder.append(databaseIdLong)
+            folderIdsNotToGetStringBuilder.append(folderId)
         }
 
-        // Return a cursor with all the bookmarks except those specified.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder)", null)
+        // Get an array list with all of the requested subfolders.
+        val subfoldersCursorArrayList = getSubfoldersExcept(HOME_FOLDER_ID, folderIdsNotToGetStringBuilder.toString())
+
+        // Return a cursor.
+        return if (subfoldersCursorArrayList.isEmpty()) {  // There are no folders.  Return an empty cursor.
+            // A matrix cursor requires the definition of at least one column.
+            MatrixCursor(arrayOf(ID))
+        } else {  // There is at least one folder.
+            // Use a merge cursor to return the folders.
+            MergeCursor(subfoldersCursorArrayList.toTypedArray())
+        }
     }
 
-    // Get a cursor for all bookmarks and folders by display order except those with the specified IDs.
-    fun getAllBookmarksByDisplayOrderExcept(exceptIdLongArray: LongArray): Cursor {
-        // Get a readable database handle.
-        val bookmarksDatabase = this.readableDatabase
+    // Determine if any folders exist beside the specified database IDs.  The array of database IDs can include both bookmarks and folders.
+    fun hasFoldersExceptDatabaseId(exceptDatabaseIdLongArray: LongArray): Boolean {
+        // Create a folder ID long list.
+        val folderIdLongList = mutableListOf<Long>()
 
-        // Prepare a string builder to contain the comma-separated list of IDs not to get.
-        val idsNotToGetStringBuilder = StringBuilder()
+        // Populate the list.
+        for (databaseId in exceptDatabaseIdLongArray) {
+            // Convert the database ID to an Int.
+            val databaseIdInt = databaseId.toInt()
 
-        // Extract the array of IDs not to get to the string builder.
-        for (databaseIdLong in exceptIdLongArray) {
-            // Check to see if there is already a number in the builder.
-            if (idsNotToGetStringBuilder.isNotEmpty()) {
-                // This is not the first number, so place a `,` before the new number.
-                idsNotToGetStringBuilder.append(",")
+            // Only process database IDs that are folders.
+            if (isFolder(databaseIdInt)) {
+                // Add the folder ID to the list.
+                folderIdLongList.add(getFolderId(databaseIdInt))
             }
-
-            // Add the new number to the builder.
-            idsNotToGetStringBuilder.append(databaseIdLong)
         }
 
-        // 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.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC", null)
-    }
+        // Get a lit of all the folders except those specified and their subfolders.
+        val foldersCursor = getFoldersExcept(folderIdLongList)
 
-    // Get a cursor for bookmarks and folders in the specified folder.
-    fun getBookmarks(folderName: String): Cursor {
-        // Get a readable database handle.
-        val bookmarksDatabase = this.readableDatabase
+        // Determine if any other folders exists.
+        val hasFolder = (foldersCursor.count > 0)
 
-        // SQL escape the folder name.
-        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+        // Close the cursor.
+        foldersCursor.close()
 
-        // Return a cursor with all the bookmarks in a specified folder.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName", null)
+        // Return the folder status.
+        return hasFolder
     }
 
-    // Get a cursor for bookmarks and folders in the specified folder ordered by display order.
-    fun getBookmarksByDisplayOrder(folderName: String): Cursor {
+    // Get the name of the parent folder
+    fun getParentFolderId(currentFolderId: Long): Long {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // SQL escape the folder name.
-        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+        // Get a cursor for the current folder.
+        val bookmarkCursor = bookmarksDatabase.rawQuery("SELECT $PARENT_FOLDER_ID FROM $BOOKMARKS_TABLE WHERE $FOLDER_ID = $currentFolderId", null)
 
-        // 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.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName ORDER BY $DISPLAY_ORDER ASC", null)
+        // Move to the first record.
+        bookmarkCursor.moveToFirst()
+
+        // Store the parent folder ID.
+        val parentFolderId = bookmarkCursor.getLong(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER_ID))
+
+        // Close the cursor and the database.
+        bookmarkCursor.close()
+        bookmarksDatabase.close()
+
+        // Return the parent folder string ID.
+        return parentFolderId
     }
 
-    // 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.
-    fun getBookmarkIds(folderName: String): Cursor {
+    // Get the name of the parent folder.
+    fun getParentFolderId(databaseId: Int): Long {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // SQL escape the folder name.
-        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+        // Get a cursor for the specified database ID.
+        val bookmarkCursor = bookmarksDatabase.rawQuery("SELECT $PARENT_FOLDER_ID FROM $BOOKMARKS_TABLE WHERE $ID = $databaseId", null)
 
-        // Return a cursor with all the database IDs.  The cursor cannot be closed because it is used in the parent activity.
-        return bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName", null)
+        // Move to the first record.
+        bookmarkCursor.moveToFirst()
+
+        // Store the name of the parent folder.
+        val parentFolderId = bookmarkCursor.getLong(bookmarkCursor.getColumnIndexOrThrow(PARENT_FOLDER_ID))
+
+        // Close the cursor and the database.
+        bookmarkCursor.close()
+        bookmarksDatabase.close()
+
+        // Return the parent folder string.
+        return parentFolderId
     }
 
-    // Get a cursor for bookmarks and folders in the specified folder except those with the specified IDs.
-    fun getBookmarksExcept(exceptIdLongArray: LongArray, folderName: String): Cursor {
+    // Get a cursor with the names and folder IDs of all the subfolders of the specified folder.
+    fun getSubfolderNamesAndFolderIds(currentFolderId: Long): Cursor {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // Prepare a string builder to contain the comma-separated list of IDs not to get.
-        val idsNotToGetStringBuilder = StringBuilder()
+        // Return the cursor with the subfolders.  The cursor can't be closed because it is used in the parent activity.
+        return bookmarksDatabase.rawQuery("SELECT $BOOKMARK_NAME, $FOLDER_ID FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $currentFolderId AND $IS_FOLDER = 1", null)
+    }
 
-        // Extract the array of IDs not to get to the string builder.
-        for (databaseIdLong in exceptIdLongArray) {
-            // Check to see if there is already a number in the builder.
-            if (idsNotToGetStringBuilder.isNotEmpty()) {
-                // This is not the first number, so place a `,` before the new number.
-                idsNotToGetStringBuilder.append(",")
-            }
+    fun getSubfolderSpacer(folderId: Long): String {
+        // Create a spacer string
+        var spacerString = ""
 
-            // Add the new number to the builder.
-            idsNotToGetStringBuilder.append(databaseIdLong)
-        }
+        // Get the parent folder ID.
+        val parentFolderId = getParentFolderId(folderId)
 
-        // SQL escape the folder name.
-        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+        // Check to see if the parent folder is not in the home folder.
+        if (parentFolderId != HOME_FOLDER_ID) {
+            // Add two spaces to the spacer string.
+            spacerString += "  "
 
-        // 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.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName AND $ID NOT IN ($idsNotToGetStringBuilder)", null)
+            // Check the parent folder recursively.
+            spacerString += getSubfolderSpacer(parentFolderId)
+        }
+
+        // Return the spacer string.
+        return spacerString
     }
 
-    // Get a cursor for bookmarks and folders in the specified folder by display order except those with the specified IDs.
-    fun getBookmarksByDisplayOrderExcept(exceptIdLongArray: LongArray, folderName: String): Cursor {
+    private fun getSubfoldersExcept(folderId: Long, exceptFolderIdString: String): ArrayList<Cursor> {
         // Get a readable database handle.
         val bookmarksDatabase = this.readableDatabase
 
-        // Prepare a string builder to contain the comma-separated list of IDs not to get.
-        val idsNotToGetStringBuilder = StringBuilder()
+        // Create a cursor array list.
+        val cursorArrayList = ArrayList<Cursor>()
 
-        // Extract the array of IDs not to get to the string builder.
-        for (databaseIdLong in exceptIdLongArray) {
-            // Check to see if there is already a number in the builder.
-            if (idsNotToGetStringBuilder.isNotEmpty()) {
-                // This is not the first number, so place a `,` before the new number.
-                idsNotToGetStringBuilder.append(",")
-            }
+        // Create a matrix cursor column names.
+        val matrixCursorColumnNames = arrayOf(ID, BOOKMARK_NAME, FAVORITE_ICON, PARENT_FOLDER_ID, FOLDER_ID)
 
-            // Add the new number to the builder.
-            idsNotToGetStringBuilder.append(databaseIdLong)
+        // Get a cursor with the subfolders.
+        val subfolderCursor = bookmarksDatabase.rawQuery(
+            "SELECT * FROM $BOOKMARKS_TABLE WHERE $IS_FOLDER = 1 AND $PARENT_FOLDER_ID = $folderId AND $FOLDER_ID NOT IN ($exceptFolderIdString) ORDER BY $DISPLAY_ORDER ASC", null)
+
+        // Get the subfolder cursor column indexes.
+        val idColumnIndex = subfolderCursor.getColumnIndexOrThrow(ID)
+        val nameColumnIndex = subfolderCursor.getColumnIndexOrThrow(BOOKMARK_NAME)
+        val favoriteIconColumnIndex = subfolderCursor.getColumnIndexOrThrow(FAVORITE_ICON)
+        val parentFolderIdColumnIndex = subfolderCursor.getColumnIndexOrThrow(PARENT_FOLDER_ID)
+        val folderIdColumnIndex = subfolderCursor.getColumnIndexOrThrow(FOLDER_ID)
+
+        while (subfolderCursor.moveToNext()) {
+            // Create an array list.
+            val matrixCursor = MatrixCursor(matrixCursorColumnNames)
+
+            // Add the subfolder to the matrix cursor.
+            matrixCursor.addRow(arrayOf<Any>(subfolderCursor.getInt(idColumnIndex), subfolderCursor.getString(nameColumnIndex), subfolderCursor.getBlob(favoriteIconColumnIndex),
+                subfolderCursor.getLong(parentFolderIdColumnIndex), subfolderCursor.getLong(folderIdColumnIndex)))
+
+            // Add the matrix cursor to the array list.
+            cursorArrayList.add(matrixCursor)
+
+            // Get all the sub-subfolders recursively
+            cursorArrayList.addAll(getSubfoldersExcept(subfolderCursor.getLong(folderIdColumnIndex), exceptFolderIdString))
         }
 
-        // SQL escape the folder name.
-        val sqlEscapedFolderName = DatabaseUtils.sqlEscapeString(folderName)
+        // Close the subfolder cursor.
+        subfolderCursor.close()
 
-        // Return a cursor with all the bookmarks in the specified folder except for those database IDs specified ordered by display order.
-        // The cursor cannot be closed because it will be used in the parent activity.
-        return bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedFolderName AND $ID NOT IN ($idsNotToGetStringBuilder) ORDER BY $DISPLAY_ORDER ASC",
-            null)
+        // Return the matrix cursor.
+        return cursorArrayList
     }
 
     // Check if a database ID is a folder.
@@ -440,7 +675,7 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
         folderCursor.moveToFirst()
 
         // Ascertain if this database ID is a folder.
-        val isFolder = folderCursor.getInt(folderCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1
+        val isFolder = (folderCursor.getInt(folderCursor.getColumnIndexOrThrow(IS_FOLDER)) == 1)
 
         // Close the cursor and the database handle.
         folderCursor.close()
@@ -450,6 +685,43 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
         return isFolder
     }
 
+    // Move one bookmark or folder to a new folder.
+    fun moveToFolder(databaseId: Int, newFolderId: Long) {
+        // Get a writable database handle.
+        val bookmarksDatabase = this.writableDatabase
+
+        // Get a cursor for all the bookmarks in the new folder ordered by display order.
+        val newFolderCursor = bookmarksDatabase.rawQuery("SELECT $DISPLAY_ORDER FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER_ID = $newFolderId ORDER BY $DISPLAY_ORDER ASC", null)
+
+        // Set the new display order.
+        val displayOrder: Int = if (newFolderCursor.count > 0) {  // There are already bookmarks in the folder.
+            // Move to the last bookmark.
+            newFolderCursor.moveToLast()
+
+            // Set the display order to be one greater that the last bookmark.
+            newFolderCursor.getInt(newFolderCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) + 1
+        } else {  // There are no bookmarks in the new folder.
+            // Set the display order to be `0`.
+            0
+        }
+
+        // Close the cursor.
+        newFolderCursor.close()
+
+        // Create a content values.
+        val bookmarkContentValues = ContentValues()
+
+        // Store the new values.
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
+        bookmarkContentValues.put(PARENT_FOLDER_ID, newFolderId)
+
+        // Update the database.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+
+        // Close the database handle.
+        bookmarksDatabase.close()
+    }
+
     // Update the bookmark name and URL.
     fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String) {
         // Initialize a content values.
@@ -470,14 +742,14 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
     }
 
     // Update the bookmark name, URL, parent folder, and display order.
-    fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, parentFolder: String, displayOrder: Int) {
+    fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, parentFolderId: Long, displayOrder: Int) {
         // Initialize a content values.
         val bookmarkContentValues = ContentValues()
 
         // Store the updated values.
         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
-        bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
+        bookmarkContentValues.put(PARENT_FOLDER_ID, parentFolderId)
         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
 
         // Get a writable database handle.
@@ -511,14 +783,14 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
     }
 
     // Update the bookmark name, URL, parent folder, display order, and favorite icon.
-    fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, parentFolder: String, displayOrder: Int, favoriteIcon: ByteArray) {
+    fun updateBookmark(databaseId: Int, bookmarkName: String, bookmarkUrl: String, parentFolderId: Long, displayOrder: Int, favoriteIcon: ByteArray) {
         // Initialize a content values.
         val bookmarkContentValues = ContentValues()
 
         // Store the updated values.
         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName)
         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl)
-        bookmarkContentValues.put(PARENT_FOLDER, parentFolder)
+        bookmarkContentValues.put(PARENT_FOLDER_ID, parentFolderId)
         bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon)
 
@@ -532,46 +804,34 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
         bookmarksDatabase.close()
     }
 
-    // Update the folder name.
-    fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String) {
+    // Update the display order for one bookmark or folder.
+    fun updateDisplayOrder(databaseId: Int, displayOrder: Int) {
         // Get a writable database handle.
         val bookmarksDatabase = this.writableDatabase
 
-        // Create a folder content values.
-        val folderContentValues = ContentValues()
-
-        // Store the new folder name.
-        folderContentValues.put(BOOKMARK_NAME, newFolderName)
-
-        // Run the update on the folder.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
-
-        // Create a bookmark content values.
+        // Create a content values.
         val bookmarkContentValues = ContentValues()
 
-        // Store the new parent folder name.
-        bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
-
-        // SQL escape the old folder name.
-        val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
+        // Store the new display order.
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
 
-        // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
+        // Update the database.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
 
         // Close the database handle.
         bookmarksDatabase.close()
     }
 
-    // Update the folder icon.
-    fun updateFolder(databaseId: Int, folderIcon: ByteArray) {
+    // Update the folder name.
+    fun updateFolder(databaseId: Int, newFolderName: String) {
         // Get a writable database handle.
         val bookmarksDatabase = this.writableDatabase
 
-        // Create a content values.
+        // Create a folder content values.
         val folderContentValues = ContentValues()
 
-        // Store the updated icon.
-        folderContentValues.put(FAVORITE_ICON, folderIcon)
+        // Store the new folder name.
+        folderContentValues.put(BOOKMARK_NAME, newFolderName)
 
         // Run the update on the folder.
         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
@@ -581,7 +841,7 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
     }
 
     // Update the folder name, parent folder, and display order.
-    fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, parentFolder: String, displayOrder: Int) {
+    fun updateFolder(databaseId: Int, newFolderName: String, parentFolderId: Long, displayOrder: Int) {
         // Get a writable database handle.
         val bookmarksDatabase = this.writableDatabase
 
@@ -590,30 +850,18 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
 
         // Store the new folder values.
         folderContentValues.put(BOOKMARK_NAME, newFolderName)
-        folderContentValues.put(PARENT_FOLDER, parentFolder)
+        folderContentValues.put(PARENT_FOLDER_ID, parentFolderId)
         folderContentValues.put(DISPLAY_ORDER, displayOrder)
 
         // Run the update on the folder.
         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
 
-        // Create a bookmark content values.
-        val bookmarkContentValues = ContentValues()
-
-        // Store the new parent folder name.
-        bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
-
-        // SQL escape the old folder name.
-        val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
-
-        // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
-
         // Close the database handle.
         bookmarksDatabase.close()
     }
 
     // Update the folder name and icon.
-    fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, folderIcon: ByteArray) {
+    fun updateFolder(databaseId: Int, newFolderName: String, folderIcon: ByteArray) {
         // Get a writable database handle.
         val bookmarksDatabase = this.writableDatabase
 
@@ -627,24 +875,12 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
         // Run the update on the folder.
         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
 
-        // Create a bookmark content values.
-        val bookmarkContentValues = ContentValues()
-
-        // Store the new parent folder name.
-        bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
-
-        // SQL escape the old folder name.
-        val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
-
-        // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
-
         // Close the database handle.
         bookmarksDatabase.close()
     }
 
     // Update the folder name and icon.
-    fun updateFolder(databaseId: Int, oldFolderName: String, newFolderName: String, parentFolder: String, displayOrder: Int, folderIcon: ByteArray) {
+    fun updateFolder(databaseId: Int, newFolderName: String, parentFolderId: Long, displayOrder: Int, folderIcon: ByteArray) {
         // Get a writable database handle.
         val bookmarksDatabase = this.writableDatabase
 
@@ -653,96 +889,37 @@ class BookmarksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, BOOK
 
         // Store the updated values.
         folderContentValues.put(BOOKMARK_NAME, newFolderName)
-        folderContentValues.put(PARENT_FOLDER, parentFolder)
+        folderContentValues.put(PARENT_FOLDER_ID, parentFolderId)
         folderContentValues.put(DISPLAY_ORDER, displayOrder)
         folderContentValues.put(FAVORITE_ICON, folderIcon)
 
         // Run the update on the folder.
         bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, "$ID = $databaseId", null)
 
-        // Create a bookmark content values.
-        val bookmarkContentValues = ContentValues()
-
-        // Store the new parent folder name.
-        bookmarkContentValues.put(PARENT_FOLDER, newFolderName)
-
-        // SQL escape the old folder name.
-        val sqlEscapedOldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName)
-
-        // Run the update on all the bookmarks that currently list the old folder name as their parent folder.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$PARENT_FOLDER = $sqlEscapedOldFolderName", null)
-
-        // Close the database handle.
-        bookmarksDatabase.close()
-    }
-
-    // Update the display order for one bookmark or folder.
-    fun updateDisplayOrder(databaseId: Int, displayOrder: Int) {
-        // Get a writable database handle.
-        val bookmarksDatabase = this.writableDatabase
-
-        // Create a content values.
-        val bookmarkContentValues = ContentValues()
-
-        // Store the new display order.
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
-
-        // Update the database.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
-
         // Close the database handle.
         bookmarksDatabase.close()
     }
 
-    // Move one bookmark or folder to a new folder.
-    fun moveToFolder(databaseId: Int, newFolder: String) {
-        // Get a writable database handle.
-        val bookmarksDatabase = this.writableDatabase
+    private fun generateFolderId(): Long {
+        // Get the current time in epoch format (in milliseconds).
+        val possibleFolderId = Date().time
 
-        // SQL escape the new folder name.
-        val sqlEscapedNewFolder = DatabaseUtils.sqlEscapeString(newFolder)
+        // Get a readable database.
+        val bookmarksDatabase = this.readableDatabase
 
-        // Get a cursor for all the bookmarks in the new folder ordered by display order.
-        val newFolderCursor = bookmarksDatabase.rawQuery("SELECT * FROM $BOOKMARKS_TABLE WHERE $PARENT_FOLDER = $sqlEscapedNewFolder ORDER BY $DISPLAY_ORDER ASC", null)
+        // Get a cursor with any folders that already have this folder ID.
+        val existingFolderCursor = bookmarksDatabase.rawQuery("SELECT $ID FROM $BOOKMARKS_TABLE WHERE $FOLDER_ID = $possibleFolderId", null)
 
-        // Set the new display order.
-        val displayOrder: Int = if (newFolderCursor.count > 0) {  // There are already bookmarks in the folder.
-            // Move to the last bookmark.
-            newFolderCursor.moveToLast()
-
-            // Set the display order to be one greater that the last bookmark.
-            newFolderCursor.getInt(newFolderCursor.getColumnIndexOrThrow(DISPLAY_ORDER)) + 1
-        } else {  // There are no bookmarks in the new folder.
-            // Set the display order to be `0`.
-            0
-        }
+        // Check if the folder ID is unique.
+        val folderIdIsUnique = (existingFolderCursor.count == 0)
 
         // Close the cursor.
-        newFolderCursor.close()
-
-        // Create a content values.
-        val bookmarkContentValues = ContentValues()
-
-        // Store the new values.
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder)
-        bookmarkContentValues.put(PARENT_FOLDER, newFolder)
-
-        // Update the database.
-        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, "$ID = $databaseId", null)
+        existingFolderCursor.close()
 
-        // Close the database handle.
-        bookmarksDatabase.close()
-    }
-
-    // Delete one bookmark.
-    fun deleteBookmark(databaseId: Int) {
-        // Get a writable database handle.
-        val bookmarksDatabase = this.writableDatabase
-
-        // Deletes the row with the given database ID.
-        bookmarksDatabase.delete(BOOKMARKS_TABLE, "$ID = $databaseId", null)
-
-        // Close the database handle.
-        bookmarksDatabase.close()
+        // Either return the folder ID or test a new one.
+        return if (folderIdIsUnique)
+            possibleFolderId
+        else
+            generateFolderId()
     }
-}
\ No newline at end of file
+}