]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.kt
First wrong button text in View Headers in night theme. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / MoveToFolderDialog.kt
index f07f346ac687f67531c98c8b64731bd2bb1daed8..a3d2f09bf04de512e851ec14e083706e9c448b05 100644 (file)
@@ -1,20 +1,20 @@
 /*
- * Copyright © 2016-2021 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
  *
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
  *
- * Privacy Browser is free software: you can redistribute it and/or modify
+ * Privacy Browser Android is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
- * Privacy Browser is distributed in the hope that it will be useful,
+ * Privacy Browser Android is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
+ * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 package com.stoutner.privacybrowser.dialogs
@@ -23,7 +23,6 @@ import android.app.Dialog
 import android.content.Context
 import android.content.DialogInterface
 import android.database.Cursor
-import android.database.DatabaseUtils
 import android.database.MatrixCursor
 import android.database.MergeCursor
 import android.graphics.Bitmap
@@ -46,43 +45,34 @@ import androidx.fragment.app.DialogFragment
 import androidx.preference.PreferenceManager
 
 import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.activities.HOME_FOLDER_DATABASE_ID
+import com.stoutner.privacybrowser.activities.HOME_FOLDER_ID
+import com.stoutner.privacybrowser.helpers.BOOKMARK_NAME
+import com.stoutner.privacybrowser.helpers.FAVORITE_ICON
+import com.stoutner.privacybrowser.helpers.FOLDER_ID
+import com.stoutner.privacybrowser.helpers.ID
+import com.stoutner.privacybrowser.helpers.PARENT_FOLDER_ID
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper
 
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
 import java.io.ByteArrayOutputStream
-import java.lang.StringBuilder
 
 // Define the class constants.
-private const val CURRENT_FOLDER = "current_folder"
-private const val SELECTED_BOOKMARKS_LONG_ARRAY = "selected_bookmarks_long_array"
+private const val CURRENT_FOLDER_ID = "A"
+private const val SELECTED_BOOKMARKS_LONG_ARRAY = "B"
 
 class MoveToFolderDialog : DialogFragment() {
-    // Declare the class variables.
-    private lateinit var moveToFolderListener: MoveToFolderListener
-    private lateinit var bookmarksDatabaseHelper: BookmarksDatabaseHelper
-    private lateinit var exceptFolders: StringBuilder
-
-    // The public interface is used to send information back to the parent activity.
-    interface MoveToFolderListener {
-        fun onMoveToFolder(dialogFragment: DialogFragment)
-    }
-
-    override fun onAttach(context: Context) {
-        // Run the default commands.
-        super.onAttach(context)
-
-        // Get a handle for the move to folder listener from the launching context.
-        moveToFolderListener = context as MoveToFolderListener
-    }
-
     companion object {
-        // `@JvmStatic` will no longer be required once all the code has transitioned to Kotlin.
-        @JvmStatic
-        fun moveBookmarks(currentFolder: String, selectedBookmarksLongArray: LongArray): MoveToFolderDialog {
+        fun moveBookmarks(currentFolderId: Long, selectedBookmarksLongArray: LongArray): MoveToFolderDialog {
             // Create an arguments bundle.
             val argumentsBundle = Bundle()
 
             // Store the arguments in the bundle.
-            argumentsBundle.putString(CURRENT_FOLDER, currentFolder)
+            argumentsBundle.putLong(CURRENT_FOLDER_ID, currentFolderId)
             argumentsBundle.putLongArray(SELECTED_BOOKMARKS_LONG_ARRAY, selectedBookmarksLongArray)
 
             // Create a new instance of the dialog.
@@ -96,19 +86,36 @@ class MoveToFolderDialog : DialogFragment() {
         }
     }
 
+    // Declare the class variables.
+    private lateinit var moveToFolderListener: MoveToFolderListener
+    private lateinit var bookmarksDatabaseHelper: BookmarksDatabaseHelper
+
+    // The public interface is used to send information back to the parent activity.
+    interface MoveToFolderListener {
+        fun onMoveToFolder(dialogFragment: DialogFragment)
+    }
+
+    override fun onAttach(context: Context) {
+        // Run the default commands.
+        super.onAttach(context)
+
+        // Get a handle for the move to folder listener from the launching context.
+        moveToFolderListener = context as MoveToFolderListener
+    }
+
     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
         // Get the data from the arguments.
-        val currentFolder = requireArguments().getString(CURRENT_FOLDER)!!
+        val currentFolderId = requireArguments().getLong(CURRENT_FOLDER_ID, HOME_FOLDER_ID)
         val selectedBookmarksLongArray = requireArguments().getLongArray(SELECTED_BOOKMARKS_LONG_ARRAY)!!
 
-        // Initialize the database helper.  The `0` specifies a database version, but that is ignored and set instead using a constant in the bookmarks database helper.
-        bookmarksDatabaseHelper = BookmarksDatabaseHelper(context, null, null, 0)
+        // Initialize the database helper.
+        bookmarksDatabaseHelper = BookmarksDatabaseHelper(requireContext())
 
         // Use an alert dialog builder to create the alert dialog.
         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
 
-        // Set the icon according to the theme.
-        dialogBuilder.setIconAttribute(R.attr.moveToFolderBlueIcon)
+        // Set the icon.
+        dialogBuilder.setIcon(R.drawable.move_to_folder_blue)
 
         // Set the title.
         dialogBuilder.setTitle(R.string.move_to_folder)
@@ -129,7 +136,7 @@ class MoveToFolderDialog : DialogFragment() {
         val alertDialog = dialogBuilder.create()
 
         // Get a handle for the shared preferences.
-        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
 
         // Get the screenshot preference.
         val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
@@ -149,32 +156,40 @@ class MoveToFolderDialog : DialogFragment() {
         // Initially disable the positive button.
         moveButton.isEnabled = false
 
-        // Initialize the except folders string builder.
-        exceptFolders = StringBuilder()
+        // Create a list of folders not to display.
+        val folderIdsNotToDisplay = mutableListOf<Long>()
 
-        // Declare the cursor variables.
-        val foldersCursor: Cursor
-        val foldersCursorAdapter: CursorAdapter
+        // Add any selected folders and their subfolders to the list of folders not to display.
+        for (databaseIdLong in selectedBookmarksLongArray) {
+            // Get the database ID int for each selected bookmark.
+            val databaseIdInt = databaseIdLong.toInt()
 
-        // Check to see if the bookmark is currently in the home folder.
-        if (currentFolder.isEmpty()) {  // The bookmark is currently in the home folder.  Don't display `Home Folder` at the top of the list view.
-            // If a folder is selected, add it and all children to the list of folders not to display.
-            for (databaseIdLong in selectedBookmarksLongArray) {
-                // Get the database ID int for each selected bookmark.
-                val databaseIdInt = databaseIdLong.toInt()
-
-                // Check to see if the bookmark is a folder.
-                if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
-                    // Add the folder to the list of folders not to display.
-                    addFolderToExceptFolders(databaseIdInt)
-                }
+            // Check to see if the bookmark is a folder.
+            if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
+                // Add the folder to the list of folders not to display.
+                folderIdsNotToDisplay.add(bookmarksDatabaseHelper.getFolderId(databaseIdInt))
             }
+        }
 
+        // Check to see if the bookmark is currently in the home folder.
+        if (currentFolderId == HOME_FOLDER_ID) {  // The bookmark is currently in the home folder.  Don't display `Home Folder` at the top of the list view.
             // Get a cursor containing the folders to display.
-            foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(exceptFolders.toString())
+            val foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(folderIdsNotToDisplay)
 
             // Populate the folders cursor adapter.
-            foldersCursorAdapter = populateFoldersCursorAdapter(requireContext(), foldersCursor)
+            val foldersCursorAdapter = populateFoldersCursorAdapter(requireContext(), foldersCursor)
+
+            // Get a handle for the folders list view.
+            val foldersListView = alertDialog.findViewById<ListView>(R.id.move_to_folder_listview)!!
+
+            // Set the folder list view adapter.
+            foldersListView.adapter = foldersCursorAdapter
+
+            // Enable the move button when a folder is selected.
+            foldersListView.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
+                // Enable the move button.
+                moveButton.isEnabled = true
+            }
         } else {  // The current folder is not directly in the home folder.  Display `Home Folder` at the top of the list view.
             // Get the home folder icon drawable.
             val homeFolderIconDrawable = ContextCompat.getDrawable(requireActivity().applicationContext, R.drawable.folder_gray_bitmap)
@@ -188,101 +203,58 @@ class MoveToFolderDialog : DialogFragment() {
             // Create a home folder icon byte array output stream.
             val homeFolderIconByteArrayOutputStream = ByteArrayOutputStream()
 
-            // Convert the home folder bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
-            homeFolderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, homeFolderIconByteArrayOutputStream)
-
-            // Convert the home folder icon byte array output stream to a byte array.
-            val homeFolderIconByteArray = homeFolderIconByteArrayOutputStream.toByteArray()
+            // Compress the bitmap using a coroutine with Dispatchers.Default.
+            CoroutineScope(Dispatchers.Main).launch {
+                withContext(Dispatchers.Default) {
+                    // Convert the home folder bitmap to a byte array.  `0` is for lossless compression (the only option for a PNG).
+                    homeFolderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, homeFolderIconByteArrayOutputStream)
 
-            // Setup the home folder matrix cursor column names.
-            val homeFolderMatrixCursorColumnNames = arrayOf(BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME, BookmarksDatabaseHelper.FAVORITE_ICON)
+                    // Convert the home folder icon byte array output stream to a byte array.
+                    val homeFolderIconByteArray = homeFolderIconByteArrayOutputStream.toByteArray()
 
-            // Setup a matrix cursor for the `Home Folder`.
-            val homeFolderMatrixCursor = MatrixCursor(homeFolderMatrixCursorColumnNames)
+                    // Setup the home folder matrix cursor column names.
+                    val homeFolderMatrixCursorColumnNames = arrayOf(ID, BOOKMARK_NAME, FAVORITE_ICON, PARENT_FOLDER_ID)
 
-            // Add the home folder to the home folder matrix cursor.
-            homeFolderMatrixCursor.addRow(arrayOf<Any>(0, getString(R.string.home_folder), homeFolderIconByteArray))
+                    // Setup a matrix cursor for the `Home Folder`.
+                    val homeFolderMatrixCursor = MatrixCursor(homeFolderMatrixCursorColumnNames)
 
-            // Add the parent folder to the list of folders not to display.
-            exceptFolders.append(DatabaseUtils.sqlEscapeString(currentFolder))
+                    // Add the home folder to the home folder matrix cursor.
+                    homeFolderMatrixCursor.addRow(arrayOf<Any>(HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder), homeFolderIconByteArray, HOME_FOLDER_ID))
 
-            // If a folder is selected, add it and all children to the list of folders not to display.
-            for (databaseIdLong in selectedBookmarksLongArray) {
-                // Get the database ID int for each selected bookmark.
-                val databaseIdInt = databaseIdLong.toInt()
+                    // Add the current folder to the list of folders not to display.
+                    folderIdsNotToDisplay.add(currentFolderId)
 
-                // Check to see if the bookmark is a folder.
-                if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
-                    // Add the folder to the list of folders not to display.
-                    addFolderToExceptFolders(databaseIdInt)
-                }
-            }
-
-            // Get a cursor containing the folders to display.
-            foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(exceptFolders.toString())
+                    // Get a cursor containing the folders to display.
+                    val foldersCursor = bookmarksDatabaseHelper.getFoldersExcept(folderIdsNotToDisplay)
 
-            // Combine the home folder matrix cursor and the folders cursor.
-            val foldersMergeCursor = MergeCursor(arrayOf(homeFolderMatrixCursor, foldersCursor))
+                    // Combine the home folder matrix cursor and the folders cursor.
+                    val foldersMergeCursor = MergeCursor(arrayOf(homeFolderMatrixCursor, foldersCursor))
 
-            // Populate the folders cursor adapter.
-            foldersCursorAdapter = populateFoldersCursorAdapter(requireContext(), foldersMergeCursor)
-        }
+                    // Populate the folders cursor on the main thread.
+                    withContext(Dispatchers.Main) {
+                        // Populate the folders cursor adapter.
+                        val foldersCursorAdapter = populateFoldersCursorAdapter(requireContext(), foldersMergeCursor)
 
-        // Get a handle for the folders list view.
-        val foldersListView = alertDialog.findViewById<ListView>(R.id.move_to_folder_listview)!!
+                        // Get a handle for the folders list view.
+                        val foldersListView = alertDialog.findViewById<ListView>(R.id.move_to_folder_listview)!!
 
-        // Set the folder list view adapter.
-        foldersListView.adapter = foldersCursorAdapter
+                        // Set the folder list view adapter.
+                        foldersListView.adapter = foldersCursorAdapter
 
-        // Enable the move button when a folder is selected.
-        foldersListView.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
-            // Enable the move button.
-            moveButton.isEnabled = true
+                        // Enable the move button when a folder is selected.
+                        foldersListView.onItemClickListener = OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
+                            // Enable the move button.
+                            moveButton.isEnabled = true
+                        }
+                    }
+                }
+            }
         }
 
         // Return the alert dialog.
         return alertDialog
     }
 
-    private fun addFolderToExceptFolders(databaseIdInt: Int) {
-        // Get the name of the selected folder.
-        val folderName = bookmarksDatabaseHelper.getFolderName(databaseIdInt)
-
-        // Populate the list of folders not to get.
-        if (exceptFolders.isEmpty()) {
-            // Add the selected folder to the list of folders not to display.
-            exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName))
-        } else {
-            // Add the selected folder to the end of the list of folders not to display.
-            exceptFolders.append(",")
-            exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName))
-        }
-
-        // Add the selected folder's subfolders to the list of folders not to display.
-        addSubfoldersToExceptFolders(folderName)
-    }
-
-    private fun addSubfoldersToExceptFolders(folderName: String) {
-        // Get a cursor with all the immediate subfolders.
-        val subfoldersCursor = bookmarksDatabaseHelper.getSubfolders(folderName)
-
-        // Add each subfolder to the list of folders not to display.
-        for (i in 0 until subfoldersCursor.count) {
-            // Move the subfolder cursor to the current item.
-            subfoldersCursor.moveToPosition(i)
-
-            // Get the name of the subfolder.
-            val subfolderName = subfoldersCursor.getString(subfoldersCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))
-
-            // Add the subfolder to except folders.
-            exceptFolders.append(",")
-            exceptFolders.append(DatabaseUtils.sqlEscapeString(subfolderName))
-
-            // Run the same tasks for any subfolders of the subfolder.
-            addSubfoldersToExceptFolders(subfolderName)
-        }
-    }
-
     private fun populateFoldersCursorAdapter(context: Context, cursor: Cursor): CursorAdapter {
         // Return the folders cursor adapter.
         return object : CursorAdapter(context, cursor, false) {
@@ -293,12 +265,22 @@ class MoveToFolderDialog : DialogFragment() {
 
             override fun bindView(view: View, context: Context, cursor: Cursor) {
                 // Get the data from the cursor.
-                val folderIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON))
-                val folderName = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME))
+                val folderIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(FAVORITE_ICON))
+                val folderName = cursor.getString(cursor.getColumnIndexOrThrow(BOOKMARK_NAME))
 
                 // Get handles for the views.
-                val folderIconImageView = view.findViewById<ImageView>(R.id.move_to_folder_icon)
-                val folderNameTextView = view.findViewById<TextView>(R.id.move_to_folder_name_textview)
+                val subfolderSpacerTextView = view.findViewById<TextView>(R.id.subfolder_spacer_textview)
+                val folderIconImageView = view.findViewById<ImageView>(R.id.folder_icon_imageview)
+                val folderNameTextView = view.findViewById<TextView>(R.id.folder_name_textview)
+
+                // Populate the subfolder spacer.
+                if (cursor.getLong(cursor.getColumnIndexOrThrow(PARENT_FOLDER_ID)) != HOME_FOLDER_ID) {  // The folder is not in the home folder.
+                    // Get the subfolder spacer.
+                    subfolderSpacerTextView.text = bookmarksDatabaseHelper.getSubfolderSpacer(cursor.getLong(cursor.getColumnIndexOrThrow(FOLDER_ID)))
+                } else {  // The folder is in the home folder.
+                    // Reset the subfolder spacer.
+                    subfolderSpacerTextView.text = ""
+                }
 
                 // Convert the byte array to a bitmap beginning at the first byte and ending at the last.
                 val folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.size)
@@ -311,4 +293,4 @@ class MoveToFolderDialog : DialogFragment() {
             }
         }
     }
-}
\ No newline at end of file
+}