X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FBookmarksActivity.java;h=3cf6e6929d13610310389b7de3a54cae8d71d9b2;hp=5e9d9fafe657201d25b3a9f663b82a541d389e02;hb=d2c46729a14917017e48d88abc870909ba805700;hpb=398b393f577ac8fb0f08c4ce770d6e064592afd7 diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java index 5e9d9faf..3cf6e692 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2017 Soren Stoutner . + * Copyright © 2016-2020 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -19,9 +19,12 @@ package com.stoutner.privacybrowser.activities; -import android.app.Activity; +import android.annotation.SuppressLint; +import android.app.Dialog; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -29,21 +32,15 @@ import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.Snackbar; -import android.support.v4.app.NavUtils; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.app.AppCompatDialogFragment; -import android.support.v7.widget.Toolbar; +import android.preference.PreferenceManager; import android.util.SparseBooleanArray; import android.view.ActionMode; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.AbsListView; -import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.EditText; import android.widget.ImageView; @@ -51,6 +48,15 @@ import android.widget.ListView; import android.widget.RadioButton; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.fragment.app.DialogFragment; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; + import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog; import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog; import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog; @@ -60,245 +66,275 @@ import com.stoutner.privacybrowser.R; import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper; import java.io.ByteArrayOutputStream; +import java.util.ArrayList; -public class BookmarksActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, - MoveToFolderDialog.MoveToFolderListener { - - // `bookmarksDatabaseHelper` is public static so it can be accessed from `EditBookmarkDialog` and `MoveToFolderDialog`. It is also used in `onCreate()`, - // `onCreateBookmarkCreate()`, `updateBookmarksListView()`, and `updateBookmarksListViewExcept()`. - public static BookmarksDatabaseHelper bookmarksDatabaseHelper; +public class BookmarksActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, EditBookmarkDialog.EditBookmarkListener, + EditBookmarkFolderDialog.EditBookmarkFolderListener, MoveToFolderDialog.MoveToFolderListener { - // `currentFolder` is public static so it can be accessed from `MoveToFolderDialog`. - // It is used in `onCreate`, `onOptionsItemSelected()`, `onCreateBookmarkCreate`, `onCreateBookmarkFolderCreate`, and `onEditBookmarkSave`. + // `currentFolder` is public static so it can be accessed from `MoveToFolderDialog` and `BookmarksDatabaseViewActivity`. + // It is used in `onCreate`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, + // and `loadFolder()`. public static String currentFolder; - // `checkedItemIds` is public static so it can be accessed from `EditBookmarkDialog`, `EditBookmarkFolderDialog`, and `MoveToFolderDialog`. - // It is also used in `onActionItemClicked`. + // `checkedItemIds` is public static so it can be accessed from `MoveToFolderDialog`. It is also used in `onCreate()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, + // and `updateMoveIcons()`. public static long[] checkedItemIds; + // `restartFromBookmarksDatabaseViewActivity` is public static so it can be accessed from `BookmarksDatabaseViewActivity`. It is also used in `onRestart()`. + public static boolean restartFromBookmarksDatabaseViewActivity; - // `bookmarksListView` is used in `onCreate()`, `updateBookmarksListView()`, and `updateBookmarksListViewExcept()`. - private ListView bookmarksListView; - // `contextualActionMode` is used in `onCreate()` and `onEditBookmarkSave()`. - private ActionMode contextualActionMode; + // Define the saved instance state constants. + private final String CHECKED_BOOKMARKS_ARRAY_LIST = "checked_bookmarks_array_list"; - // `selectedBookmarkPosition` is used in `onCreate()` and `onEditBookmarkSave()`. - private int selectedBookmarkPosition; + // Define the class menu items. + private MenuItem moveBookmarkUpMenuItem; + private MenuItem moveBookmarkDownMenuItem; - // `appBar` is used in `onCreate()` and `updateBookmarksListView()`. - private ActionBar appBar; + // `bookmarksDatabaseHelper` is used in `onCreate()`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, + // `onMoveToFolder()`, `deleteBookmarkFolderContents()`, `loadFolder()`, and `onDestroy()`. + private BookmarksDatabaseHelper bookmarksDatabaseHelper; + + // `bookmarksListView` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, + // `updateMoveIcons()`, `scrollBookmarks()`, and `loadFolder()`. + private ListView bookmarksListView; - // `bookmarksCursor` is used in `onCreate()`, `updateBookmarksListView()`, and `updateBookmarksListViewExcept()`. + // `bookmarksCursor` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`, + // `loadFolder()`, and `onDestroy()`. private Cursor bookmarksCursor; - // `oldFolderName` is used in `onCreate()` and `onEditBookmarkFolderSave()`. + // `bookmarksCursorAdapter` is used in `onCreate(), `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, and `onLoadFolder()`. + private CursorAdapter bookmarksCursorAdapter; + + // `contextualActionMode` is used in `onCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()` and `onMoveToFolder()`. + private ActionMode contextualActionMode; + + // `appBar` is used in `onCreate()` and `loadFolder()`. + private ActionBar appBar; + + // `oldFolderName` is used in `onCreate()` and `onSaveBookmarkFolder()`. private String oldFolderNameString; + // `bookmarksDeletedSnackbar` is used in `onCreate()`, `onOptionsItemSelected()`, and `onBackPressed()`. + private Snackbar bookmarksDeletedSnackbar; + + // `closeActivityAfterDismissingSnackbar` is used in `onCreate()`, `onOptionsItemSelected()`, and `onBackPressed()`. + private boolean closeActivityAfterDismissingSnackbar; + + // The favorite icon byte array is populated in `onCreate()` and used in `onOptionsItemSelected()`. + private byte[] favoriteIconByteArray; + @Override protected void onCreate(Bundle savedInstanceState) { - // Set the activity theme. - if (MainWebViewActivity.darkTheme) { - setTheme(R.style.PrivacyBrowserDark_SecondaryActivity); - } else { - setTheme(R.style.PrivacyBrowserLight_SecondaryActivity); + // Get a handle for the shared preferences. + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + + // Get the screenshot preference. + boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false); + + // Disable screenshots if not allowed. + if (!allowScreenshots) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } + // Set the theme. + setTheme(R.style.PrivacyBrowser); + // Run the default commands. super.onCreate(savedInstanceState); + // Get the intent that launched the activity. + Intent launchingIntent = getIntent(); + + // Store the current URL and title. + String currentUrl = launchingIntent.getStringExtra("current_url"); + String currentTitle = launchingIntent.getStringExtra("current_title"); + + // Set the current folder variable. + if (launchingIntent.getStringExtra("current_folder") != null) { // Set the current folder from the intent. + currentFolder = launchingIntent.getStringExtra("current_folder"); + } else { // Set the current folder to be `""`, which is the home folder. + currentFolder = ""; + } + + // Get the favorite icon byte array. + favoriteIconByteArray = launchingIntent.getByteArrayExtra("favorite_icon_byte_array"); + + // Remove the incorrect lint warning that the favorite icon byte array might be null. + assert favoriteIconByteArray != null; + + // Convert the favorite icon byte array to a bitmap. + Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length); + // Set the content view. setContentView(R.layout.bookmarks_coordinatorlayout); - // Use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21. - final Toolbar bookmarksAppBar = (Toolbar) findViewById(R.id.bookmarks_toolbar); - setSupportActionBar(bookmarksAppBar); + // The AndroidX toolbar must be used until the minimum API is >= 21. + final Toolbar toolbar = findViewById(R.id.bookmarks_toolbar); + setSupportActionBar(toolbar); - // Display the home arrow on `SupportActionBar`. + // Get handles for the views. appBar = getSupportActionBar(); - assert appBar != null;// This assert removes the incorrect warning in Android Studio on the following line that `appBar` might be null. - appBar.setDisplayHomeAsUpEnabled(true); + bookmarksListView = findViewById(R.id.bookmarks_listview); + + // Remove the incorrect lint warning that `appBar` might be null. + assert appBar != null; + // Display the home arrow on the app bar. + appBar.setDisplayHomeAsUpEnabled(true); - // Initialize the database handler and the `ListView`. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`. + // Initialize the database helper. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`. // The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`. bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0); - bookmarksListView = (ListView) findViewById(R.id.bookmarks_listview); - // Set currentFolder to the home folder, which is `""` in the database. - currentFolder = ""; + // Load the home folder. + loadFolder(); - // Display the bookmarks in the ListView. - updateBookmarksListView(currentFolder); + // Set a listener so that tapping a list item loads the URL or folder. + bookmarksListView.setOnItemClickListener((parent, view, position, id) -> { + // Convert the id from long to int to match the format of the bookmarks database. + int databaseId = (int) id; - // Set a listener so that tapping a list item loads the URL. We need to store the activity in a variable so that we can return to the parent activity after loading the URL. - final Activity bookmarksActivity = this; - bookmarksListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - // Convert the id from long to int to match the format of the bookmarks database. - int databaseID = (int) id; - - // Get the bookmark `Cursor` for this ID and move it to the first row. - Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmarkCursor(databaseID); - bookmarkCursor.moveToFirst(); - - // If the bookmark is a folder load its contents into the ListView. - if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) { - // Update `currentFolder`. - currentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); - - // Reload the ListView with `currentFolder`. - updateBookmarksListView(currentFolder); - } else { // Load the URL into `mainWebView`. - // Get the bookmark URL and assign it to formattedUrlString. `mainWebView` will automatically reload when `BookmarksActivity` closes. - MainWebViewActivity.formattedUrlString = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)); - - NavUtils.navigateUpFromSameTask(bookmarksActivity); - } + // Get the bookmark cursor for this ID. + Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId); - // Close the `Cursor`. - bookmarkCursor.close(); + // Move the cursor to the first entry. + bookmarkCursor.moveToFirst(); + + // Act upon the bookmark according to the type. + if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) { // The selected bookmark is a folder. + // Update the current folder. + currentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); + + // Load the new folder. + loadFolder(); + } else { // The selected bookmark is not a folder. + // Instantiate the edit bookmark dialog. + DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId, favoriteIconBitmap); + + // Make it so. + editBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark)); } + + // Close the cursor. + bookmarkCursor.close(); }); - // `MultiChoiceModeListener` handles long clicks. + // Handle long presses on the list view. bookmarksListView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() { - // `moveBookmarkUpMenuItem` is used in `onCreateActionMode()` and `onItemCheckedStateChanged`. - MenuItem moveBookmarkUpMenuItem; - - // `moveBookmarkDownMenuItem` is used in `onCreateActionMode()` and `onItemCheckedStateChanged`. - MenuItem moveBookmarkDownMenuItem; - - // `editBookmarkMenuItem` is used in `onCreateActionMode()` and `onItemCheckedStateChanged`. + // Instantiate the common variables. MenuItem editBookmarkMenuItem; - - // `selectAllBookmarks` is used in `onCreateActionMode()` and `onItemCheckedStateChanges`. + MenuItem deleteBookmarksMenuItem; MenuItem selectAllBookmarksMenuItem; + boolean deletingBookmarks; @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - // Inflate the menu for the contextual app bar and set the title. + // Inflate the menu for the contextual app bar. getMenuInflater().inflate(R.menu.bookmarks_context_menu, menu); // Set the title. - if (currentFolder.isEmpty()) { - // Use `R.string.bookmarks` if we are in the home folder. + if (currentFolder.isEmpty()) { // Use `R.string.bookmarks` if in the home folder. mode.setTitle(R.string.bookmarks); } else { // Use the current folder name as the title. mode.setTitle(currentFolder); } - // Get a handle for MenuItems we need to selectively disable. + // Get handles for menu items that need to be selectively disabled. moveBookmarkUpMenuItem = menu.findItem(R.id.move_bookmark_up); moveBookmarkDownMenuItem = menu.findItem(R.id.move_bookmark_down); editBookmarkMenuItem = menu.findItem(R.id.edit_bookmark); + deleteBookmarksMenuItem = menu.findItem(R.id.delete_bookmark); selectAllBookmarksMenuItem = menu.findItem(R.id.context_menu_select_all_bookmarks); - // Store `contextualActionMode` so we can close it programatically. + // Disable the delete bookmarks menu item if a delete is pending. + deleteBookmarksMenuItem.setEnabled(!deletingBookmarks); + + // Store a handle for the contextual action mode so it can be closed programatically. contextualActionMode = mode; + // Make it so. return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; + // Get a handle for the move to folder menu item. + MenuItem moveToFolderMenuItem = menu.findItem(R.id.move_to_folder); + + // Get a Cursor with all of the folders. + Cursor folderCursor = bookmarksDatabaseHelper.getAllFolders(); + + // Enable the move to folder menu item if at least one folder exists. + moveToFolderMenuItem.setVisible(folderCursor.getCount() > 0); + + // Make it so. + return true; } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { - // Get an array of the selected bookmarks. - long[] selectedBookmarksLongArray = bookmarksListView.getCheckedItemIds(); - - // Calculate the number of selected bookmarks. - int numberOfSelectedBookmarks = selectedBookmarksLongArray.length; - - // Adjust the `mode` and the menu for the number of selected bookmarks. - if (numberOfSelectedBookmarks == 0) { - mode.finish(); - } else if (numberOfSelectedBookmarks == 1) { - // List the number of selected bookmarks in the subtitle. - mode.setSubtitle(getString(R.string.one_selected)); - - // Show the `Move Up`, `Move Down`, and `Edit` options. - moveBookmarkUpMenuItem.setVisible(true); - moveBookmarkDownMenuItem.setVisible(true); - editBookmarkMenuItem.setVisible(true); - - // Get the database IDs for the bookmarks. - int selectedBookmarkDatabaseId = (int) selectedBookmarksLongArray[0]; - int firstBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(0); - // bookmarksListView is 0 indexed. - int lastBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(bookmarksListView.getCount() - 1); - - // Disable `moveBookmarkUpMenuItem` if the selected bookmark is at the top of the ListView. - if (selectedBookmarkDatabaseId == firstBookmarkDatabaseId) { - moveBookmarkUpMenuItem.setEnabled(false); - moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_disabled); - } else { // Otherwise enable `moveBookmarkUpMenuItem`. - moveBookmarkUpMenuItem.setEnabled(true); - - // Set the icon according to the theme. - if (MainWebViewActivity.darkTheme) { - moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_enabled_dark); - } else { - moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_enabled_light); - } + // Get the number of selected bookmarks. + int numberOfSelectedBookmarks = bookmarksListView.getCheckedItemCount(); + + // Only process commands if at least one bookmark is selected. Otherwise, a context menu with 0 selected bookmarks is briefly displayed. + if (numberOfSelectedBookmarks > 0) { + // Adjust the ActionMode and the menu according to the number of selected bookmarks. + if (numberOfSelectedBookmarks == 1) { // One bookmark is selected. + // List the number of selected bookmarks in the subtitle. + mode.setSubtitle(getString(R.string.selected) + " 1"); + + // Show the `Move Up`, `Move Down`, and `Edit` options. + moveBookmarkUpMenuItem.setVisible(true); + moveBookmarkDownMenuItem.setVisible(true); + editBookmarkMenuItem.setVisible(true); + + // Update the enabled status of the move icons. + updateMoveIcons(); + } else { // More than one bookmark is selected. + // List the number of selected bookmarks in the subtitle. + mode.setSubtitle(getString(R.string.selected) + " " + numberOfSelectedBookmarks); + + // Hide non-applicable `MenuItems`. + moveBookmarkUpMenuItem.setVisible(false); + moveBookmarkDownMenuItem.setVisible(false); + editBookmarkMenuItem.setVisible(false); } - // Disable `moveBookmarkDownMenuItem` if the selected bookmark is at the bottom of the ListView. - if (selectedBookmarkDatabaseId == lastBookmarkDatabaseId) { - moveBookmarkDownMenuItem.setEnabled(false); - moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_disabled); - } else { // Otherwise enable `moveBookmarkDownMenuItem`. - moveBookmarkDownMenuItem.setEnabled(true); - - // Set the icon according to the theme. - if (MainWebViewActivity.darkTheme) { - moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_enabled_dark); - } else { - moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_enabled_light); - } + // Do not show the select all menu item if all the bookmarks are already checked. + if (bookmarksListView.getCheckedItemCount() == bookmarksListView.getCount()) { + selectAllBookmarksMenuItem.setVisible(false); + } else { + selectAllBookmarksMenuItem.setVisible(true); } - } else { // More than one bookmark is selected. - // List the number of selected bookmarks in the subtitle. - mode.setSubtitle(numberOfSelectedBookmarks + " " + getString(R.string.selected)); - - // Hide non-applicable `MenuItems`. - moveBookmarkUpMenuItem.setVisible(false); - moveBookmarkDownMenuItem.setVisible(false); - editBookmarkMenuItem.setVisible(false); - } - - // Do not show `Select All` if all the bookmarks are already checked. - if (bookmarksListView.getCheckedItemIds().length == bookmarksListView.getCount()) { - selectAllBookmarksMenuItem.setVisible(false); - } else { - selectAllBookmarksMenuItem.setVisible(true); } } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - // Get the menu item ID. - int menuItemId = item.getItemId(); - - // Instantiate the common variables. - int numberOfBookmarks; + // Declare the common variables. int selectedBookmarkNewPosition; - SparseBooleanArray bookmarkPositionSparseBooleanArray; + final SparseBooleanArray selectedBookmarksPositionsSparseBooleanArray; - switch (menuItemId) { - case R.id.move_bookmark_up: - // Get the array of checked bookmarks. - bookmarkPositionSparseBooleanArray = bookmarksListView.getCheckedItemPositions(); + // Initialize the selected bookmark position. + int selectedBookmarkPosition = 0; - // Store the position of the selected bookmark. - selectedBookmarkPosition = bookmarkPositionSparseBooleanArray.keyAt(0); + switch (item.getItemId()) { + case R.id.move_bookmark_up: + // Get the array of checked bookmark positions. + selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.getCheckedItemPositions(); + + // Get the position of the bookmark that is selected. If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`. + for (int i = 0; i < selectedBookmarksPositionsSparseBooleanArray.size(); i++) { + // Check to see if the value for the bookmark is true, meaning it is currently selected. + if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) { + // Only one bookmark should have a value of `true` when move bookmark up is enabled. + selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i); + } + } - // Initialize `selectedBookmarkNewPosition`. - selectedBookmarkNewPosition = 0; + // Calculate the new position of the selected bookmark. + selectedBookmarkNewPosition = selectedBookmarkPosition - 1; // Iterate through the bookmarks. for (int i = 0; i < bookmarksListView.getCount(); i++) { @@ -308,42 +344,49 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma // Update the display order for the current bookmark. if (i == selectedBookmarkPosition) { // The current bookmark is the selected bookmark. // Move the current bookmark up one. - bookmarksDatabaseHelper.updateBookmarkDisplayOrder(currentBookmarkDatabaseId, i - 1); - selectedBookmarkNewPosition = i - 1; + bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i - 1); } else if ((i + 1) == selectedBookmarkPosition){ // The current bookmark is immediately above the selected bookmark. // Move the current bookmark down one. - bookmarksDatabaseHelper.updateBookmarkDisplayOrder(currentBookmarkDatabaseId, i + 1); + bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i + 1); } else { // The current bookmark is not changing positions. // Move `bookmarksCursor` to the current bookmark position. bookmarksCursor.moveToPosition(i); // Update the display order only if it is not correct in the database. if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i) { - bookmarksDatabaseHelper.updateBookmarkDisplayOrder(currentBookmarkDatabaseId, i); + bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i); } } } - // Refresh the ListView. - updateBookmarksListView(currentFolder); + // Update the bookmarks cursor with the current contents of the bookmarks database. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder); - // Select the previously selected bookmark in the new location. - bookmarksListView.setItemChecked(selectedBookmarkNewPosition, true); + // Update the `ListView`. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); - // Scroll `bookmarksListView` to five items above `selectedBookmarkNewPosition`. - bookmarksListView.setSelection(selectedBookmarkNewPosition - 5); + // Scroll with the bookmark. + scrollBookmarks(selectedBookmarkNewPosition); + // Update the enabled status of the move icons. + updateMoveIcons(); break; case R.id.move_bookmark_down: - // Get the array of checked bookmarks. - bookmarkPositionSparseBooleanArray = bookmarksListView.getCheckedItemPositions(); - - // Store the position of the selected bookmark. - selectedBookmarkPosition = bookmarkPositionSparseBooleanArray.keyAt(0); + // Get the array of checked bookmark positions. + selectedBookmarksPositionsSparseBooleanArray = bookmarksListView.getCheckedItemPositions(); + + // Get the position of the bookmark that is selected. If other bookmarks have previously been selected they will be included in the sparse boolean array with a value of `false`. + for (int i = 0; i < selectedBookmarksPositionsSparseBooleanArray.size(); i++) { + // Check to see if the value for the bookmark is true, meaning it is currently selected. + if (selectedBookmarksPositionsSparseBooleanArray.valueAt(i)) { + // Only one bookmark should have a value of `true` when move bookmark down is enabled. + selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(i); + } + } - // Initialize `selectedBookmarkNewPosition`. - selectedBookmarkNewPosition = 0; + // Calculate the new position of the selected bookmark. + selectedBookmarkNewPosition = selectedBookmarkPosition + 1; // Iterate through the bookmarks. for (int i = 0; i { + // Do nothing because everything will be handled by `onDismissed()` below. }) .addCallback(new Snackbar.Callback() { + @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`. @Override public void onDismissed(Snackbar snackbar, int event) { - switch (event) { - // The user pushed the `Undo` button. - case Snackbar.Callback.DISMISS_EVENT_ACTION: - // Refresh the ListView to show the rows again. - updateBookmarksListView(currentFolder); - - // Scroll to where the first deleted bookmark was located. - bookmarksListView.setSelection(selectedBookmarkPosition - 5); - break; - - // The `Snackbar` was dismissed without the `Undo` button being pushed. - default: - // Delete each selected row. - for (long databaseIdLong : selectedBookmarksLongArray) { - // Convert `databaseIdLong` to an int. - int databaseIdInt = (int) databaseIdLong; - - if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) { - deleteBookmarkFolderContents(databaseIdInt); - } - - // Delete `databaseIdInt`. - bookmarksDatabaseHelper.deleteBookmark(databaseIdInt); + if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) { // The user pushed the undo button. + // Update the bookmarks cursor with the current contents of the bookmarks database, including the "deleted" bookmarks. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder); + + // Update the list view. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); + + // Re-select the previously selected bookmarks. + for (int i = 0; i < selectedBookmarksPositionsSparseBooleanArray.size(); i++) { + bookmarksListView.setItemChecked(selectedBookmarksPositionsSparseBooleanArray.keyAt(i), true); + } + } else { // The snackbar was dismissed without the undo button being pushed. + // Delete each selected bookmark. + for (long databaseIdLong : selectedBookmarksIdsLongArray) { + // Convert `databaseIdLong` to an int. + int databaseIdInt = (int) databaseIdLong; + + // Delete the contents of the folder if the selected bookmark is a folder. + if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) { + deleteBookmarkFolderContents(databaseIdInt); } - break; + + // Delete the selected bookmark. + bookmarksDatabaseHelper.deleteBookmark(databaseIdInt); + } + + // Update the display order. + for (int i = 0; i < bookmarksListView.getCount(); i++) { + // Get the database ID for the current bookmark. + int currentBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(i); + + // Move `bookmarksCursor` to the current bookmark position. + bookmarksCursor.moveToPosition(i); + + // Update the display order only if it is not correct in the database. + if (bookmarksCursor.getInt(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER)) != i) { + bookmarksDatabaseHelper.updateDisplayOrder(currentBookmarkDatabaseId, i); + } + } + } + + // Reset the deleting bookmarks flag. + deletingBookmarks = false; + + // Enable the delete bookmarks menu item. + deleteBookmarksMenuItem.setEnabled(true); + + // Close the activity if back has been pressed. + if (closeActivityAfterDismissingSnackbar) { + onBackPressed(); } } - }) - .show(); + }); - // Close the contextual app bar. - mode.finish(); + //Show the Snackbar. + bookmarksDeletedSnackbar.show(); break; case R.id.context_menu_select_all_bookmarks: - numberOfBookmarks = bookmarksListView.getCount(); + // Get the total number of bookmarks. + int numberOfBookmarks = bookmarksListView.getCount(); + // Select them all. for (int i = 0; i < numberOfBookmarks; i++) { bookmarksListView.setItemChecked(i, true); } break; } + // Consume the click. return true; } @Override public void onDestroyActionMode(ActionMode mode) { - + // Do nothing. } }); - // Set a FloatingActionButton for creating new bookmarks. - FloatingActionButton createBookmarkFAB = (FloatingActionButton) findViewById(R.id.create_bookmark_fab); - createBookmarkFAB.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - // Show the `CreateBookmarkDialog` `AlertDialog` and name the instance `@string/create_bookmark`. - AppCompatDialogFragment createBookmarkDialog = new CreateBookmarkDialog(); - createBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_bookmark)); - } + // Get handles for the `FloatingActionButtons`. + FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab); + FloatingActionButton createBookmarkFab = findViewById(R.id.create_bookmark_fab); + + // Set the create new bookmark folder FAB to display the `AlertDialog`. + createBookmarkFolderFab.setOnClickListener(v -> { + // Create a create bookmark folder dialog. + DialogFragment createBookmarkFolderDialog = CreateBookmarkFolderDialog.createBookmarkFolder(favoriteIconBitmap); + + // Show the create bookmark folder dialog. + createBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.create_folder)); }); + + // Set the create new bookmark FAB to display the `AlertDialog`. + createBookmarkFab.setOnClickListener(view -> { + // Remove the incorrect lint warning below. + assert currentUrl != null; + assert currentTitle != null; + + // Instantiate the create bookmark dialog. + DialogFragment createBookmarkDialog = CreateBookmarkDialog.createBookmark(currentUrl, currentTitle, favoriteIconBitmap); + + // Display the create bookmark dialog. + createBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_bookmark)); + }); + + // Restore the state if the app has been restarted. + if (savedInstanceState != null) { + // Update the bookmarks list view after it has loaded. + bookmarksListView.post(() -> { + // Get the checked bookmarks array list. + ArrayList checkedBookmarksArrayList = savedInstanceState.getIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST); + + // Check each previously checked bookmark in the list view. When the minimum API >= 24 a `forEach()` command can be used instead. + if (checkedBookmarksArrayList != null) { + for (int i = 0; i < checkedBookmarksArrayList.size(); i++) { + bookmarksListView.setItemChecked(checkedBookmarksArrayList.get(i), true); + } + } + }); + } + } + + @Override + public void onRestart() { + // Run the default commands. + super.onRestart(); + + // Update the list view if returning from the bookmarks database view activity. + if (restartFromBookmarksDatabaseViewActivity) { + // Load the current folder in the list view. + loadFolder(); + + // Reset `restartFromBookmarksDatabaseViewActivity`. + restartFromBookmarksDatabaseViewActivity = false; + } + } + + @Override + public void onSaveInstanceState(@NonNull Bundle savedInstanceState) { + // Run the default commands. + super.onSaveInstanceState(savedInstanceState); + + // Get the array of the checked items. + SparseBooleanArray checkedBookmarksSparseBooleanArray = bookmarksListView.getCheckedItemPositions(); + + // Create a checked items array list. + ArrayList checkedBookmarksArrayList = new ArrayList<>(); + + // Add each checked bookmark position to the array list. + for (int i = 0; i < checkedBookmarksSparseBooleanArray.size(); i++) { + // Check to see if the bookmark is currently checked. Bookmarks that have previously been checked but currently aren't will be populated in the sparse boolean array, but will return false. + if (checkedBookmarksSparseBooleanArray.valueAt(i)) { + // Add the bookmark position to the checked bookmarks array list. + checkedBookmarksArrayList.add(checkedBookmarksSparseBooleanArray.keyAt(i)); + } + } + + // Store the checked items array list in the saved instance state. + savedInstanceState.putIntegerArrayList(CHECKED_BOOKMARKS_ARRAY_LIST, checkedBookmarksArrayList); } @Override @@ -527,39 +672,38 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma @Override public boolean onOptionsItemSelected(MenuItem menuItem) { - // Get the ID of the `MenuItem` that was selected. - int menuItemId = menuItem.getItemId(); - - switch (menuItemId) { + switch (menuItem.getItemId()) { case android.R.id.home: // The home arrow is identified as `android.R.id.home`, not just `R.id.home`. - if (currentFolder.isEmpty()) { // Exit BookmarksActivity if currently in the home folder. - NavUtils.navigateUpFromSameTask(this); - } else { // Navigate up one folder. + if (currentFolder.isEmpty()) { // Currently in the home folder. + // Run the back commands. + onBackPressed(); + } else { // Currently in a subfolder. // Place the former parent folder in `currentFolder`. - currentFolder = bookmarksDatabaseHelper.getParentFolder(currentFolder); + currentFolder = bookmarksDatabaseHelper.getParentFolderName(currentFolder); - // Update `bookmarksListView`. - updateBookmarksListView(currentFolder); + // Load the new folder. + loadFolder(); } break; - case R.id.create_folder: - // Show the `CreateBookmarkFolderDialog` `AlertDialog` and name the instance `@string/create_folder`. - AppCompatDialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog(); - createBookmarkFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_folder)); - break; - case R.id.options_menu_select_all_bookmarks: + // Get the total number of bookmarks. int numberOfBookmarks = bookmarksListView.getCount(); + // Select them all. for (int i = 0; i < numberOfBookmarks; i++) { bookmarksListView.setItemChecked(i, true); } break; case R.id.bookmarks_database_view: - // Launch `BookmarksDatabaseViewActivity`. + // Create an intent to launch the bookmarks database view activity. Intent bookmarksDatabaseViewIntent = new Intent(this, BookmarksDatabaseViewActivity.class); + + // Include the favorite icon byte array to the intent. + bookmarksDatabaseViewIntent.putExtra("favorite_icon_byte_array", favoriteIconByteArray); + + // Make it so. startActivity(bookmarksDatabaseViewIntent); break; } @@ -568,110 +712,156 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma @Override public void onBackPressed() { - if (currentFolder.isEmpty()) { // Exit BookmarksActivity if currently in the home folder. - super.onBackPressed(); - } else { // Navigate up one folder. - // Place the former parent folder in `currentFolder`. - currentFolder = bookmarksDatabaseHelper.getParentFolder(currentFolder); + // Check to see if a snackbar is currently displayed. If so, it must be closed before exiting so that a pending delete is completed before reloading the list view in the bookmarks drawer. + if ((bookmarksDeletedSnackbar != null) && bookmarksDeletedSnackbar.isShown()) { // Close the bookmarks deleted snackbar before going home. + // Set the close flag. + closeActivityAfterDismissingSnackbar = true; + + // Dismiss the snackbar. + bookmarksDeletedSnackbar.dismiss(); + } else { // Go home immediately. + // Update the bookmarks folder for the bookmarks drawer in the main WebView activity. + MainWebViewActivity.currentBookmarksFolder = currentFolder; - // Reload the `ListView`. - updateBookmarksListView(currentFolder); + // Close the bookmarks drawer and reload the bookmarks ListView when returning to the main WebView activity. + MainWebViewActivity.restartFromBookmarksActivity = true; + + // Exit the bookmarks activity. + super.onBackPressed(); } } @Override - public void onCreateBookmark(AppCompatDialogFragment dialogFragment) { - // Get the `EditTexts` from the `dialogFragment`. - EditText createBookmarkNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext); - EditText createBookmarkUrlEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext); + public void onCreateBookmark(DialogFragment dialogFragment, Bitmap favoriteIconBitmap) { + // Get the alert dialog from the fragment. + Dialog dialog = dialogFragment.getDialog(); + + // Remove the incorrect lint warning below that the dialog might be null. + assert dialog != null; + + // Get the views from the dialog fragment. + EditText createBookmarkNameEditText = dialog.findViewById(R.id.create_bookmark_name_edittext); + EditText createBookmarkUrlEditText = dialog.findViewById(R.id.create_bookmark_url_edittext); - // Extract the strings from the `EditTexts`. + // Extract the strings from the edit texts. String bookmarkNameString = createBookmarkNameEditText.getText().toString(); String bookmarkUrlString = createBookmarkUrlEditText.getText().toString(); - // Convert the favoriteIcon Bitmap to a byte array. + // Create a favorite icon byte array output stream. ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); - // `0` is for lossless compression (the only option for a PNG). - MainWebViewActivity.favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); + + // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); + + // Convert the favorite icon byte array stream to a byte array. byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray(); // Display the new bookmark below the current items in the (0 indexed) list. int newBookmarkDisplayOrder = bookmarksListView.getCount(); // Create the bookmark. - bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, newBookmarkDisplayOrder, currentFolder, favoriteIconByteArray); + bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, currentFolder, newBookmarkDisplayOrder, favoriteIconByteArray); + + // Update the bookmarks cursor with the current contents of this folder. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder); + + // Update the `ListView`. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); - // Refresh the `ListView`. `setSelection` scrolls to the bottom of the list. - updateBookmarksListView(currentFolder); + // Scroll to the new bookmark. bookmarksListView.setSelection(newBookmarkDisplayOrder); } @Override - public void onCreateBookmarkFolder(AppCompatDialogFragment dialogFragment) { - // Get `create_folder_name_edit_text` and extract the string. - EditText createFolderNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext); + public void onCreateBookmarkFolder(DialogFragment dialogFragment, @NonNull Bitmap favoriteIconBitmap) { + // Get the dialog from the dialog fragment. + Dialog dialog = dialogFragment.getDialog(); + + // Remove the incorrect lint warning below that the dialog might be null. + assert dialog != null; + + // Get handles for the views in the dialog fragment. + EditText createFolderNameEditText = dialog.findViewById(R.id.create_folder_name_edittext); + RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.create_folder_default_icon_radiobutton); + ImageView folderIconImageView = dialog.findViewById(R.id.create_folder_default_icon); + + // Get new folder name string. String folderNameString = createFolderNameEditText.getText().toString(); - // Check to see if the folder already exists. - Cursor bookmarkFolderCursor = bookmarksDatabaseHelper.getFolderCursor(folderNameString); - int existingFoldersWithNewName = bookmarkFolderCursor.getCount(); - bookmarkFolderCursor.close(); - if (folderNameString.isEmpty() || (existingFoldersWithNewName > 0)) { - String cannotCreateFolder = getResources().getString(R.string.cannot_create_folder) + " \"" + folderNameString + "\""; - Snackbar.make(findViewById(R.id.bookmarks_coordinatorlayout), cannotCreateFolder, Snackbar.LENGTH_INDEFINITE).show(); - } else { // Create the folder. - // Get the new folder icon `Bitmap`. - RadioButton defaultFolderIconRadioButton = (RadioButton) dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton); - Bitmap folderIconBitmap; - if (defaultFolderIconRadioButton.isChecked()) { - // Get the default folder icon `ImageView` from the `Dialog` and convert it to a `Bitmap`. - ImageView folderIconImageView = (ImageView) dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon); - Drawable folderIconDrawable = folderIconImageView.getDrawable(); - BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; - folderIconBitmap = folderIconBitmapDrawable.getBitmap(); - } else { // Assign `favoriteIcon` from the `WebView`. - folderIconBitmap = MainWebViewActivity.favoriteIconBitmap; - } + // Create a folder icon bitmap. + Bitmap folderIconBitmap; - // Convert `folderIconBitmap` to a byte array. `0` is for lossless compression (the only option for a PNG). - ByteArrayOutputStream folderIconByteArrayOutputStream = new ByteArrayOutputStream(); - folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream); - byte[] folderIconByteArray = folderIconByteArrayOutputStream.toByteArray(); + // Set the folder icon bitmap according to the dialog. + if (defaultFolderIconRadioButton.isChecked()) { // Use the default folder icon. + // Get the default folder icon drawable. + Drawable folderIconDrawable = folderIconImageView.getDrawable(); - // Move all the bookmarks down one in the display order. - for (int i = 0; i < bookmarksListView.getCount(); i++) { - int databaseId = (int) bookmarksListView.getItemIdAtPosition(i); - bookmarksDatabaseHelper.updateBookmarkDisplayOrder(databaseId, i + 1); - } + // Convert the folder icon drawable to a bitmap drawable. + BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; + + // Convert the folder icon bitmap drawable to a bitmap. + folderIconBitmap = folderIconBitmapDrawable.getBitmap(); + } else { // Use the WebView favorite icon. + // Copy the favorite icon bitmap to the folder icon bitmap. + folderIconBitmap = favoriteIconBitmap; + } + + // Create a folder icon byte array output stream. + ByteArrayOutputStream folderIconByteArrayOutputStream = new ByteArrayOutputStream(); - // Create the folder, placing it at the top of the ListView - bookmarksDatabaseHelper.createFolder(folderNameString, 0, currentFolder, folderIconByteArray); + // Convert the folder icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream); - // Refresh the ListView. - updateBookmarksListView(currentFolder); + // Convert the folder icon byte array stream to a byte array. + byte[] folderIconByteArray = folderIconByteArrayOutputStream.toByteArray(); + + // Move all the bookmarks down one in the display order. + for (int i = 0; i < bookmarksListView.getCount(); i++) { + int databaseId = (int) bookmarksListView.getItemIdAtPosition(i); + bookmarksDatabaseHelper.updateDisplayOrder(databaseId, i + 1); } + + // Create the folder, which will be placed at the top of the `ListView`. + bookmarksDatabaseHelper.createFolder(folderNameString, currentFolder, folderIconByteArray); + + // Update the bookmarks cursor with the current contents of this folder. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder); + + // Update the `ListView`. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); + + // Scroll to the new folder. + bookmarksListView.setSelection(0); } @Override - public void onSaveEditBookmark(AppCompatDialogFragment dialogFragment) { - // Get a long array with the the databaseId of the selected bookmark and convert it to an `int`. - long[] selectedBookmarksLongArray = bookmarksListView.getCheckedItemIds(); - int selectedBookmarkDatabaseId = (int) selectedBookmarksLongArray[0]; + public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, @NonNull Bitmap favoriteIconBitmap) { + // Get the dialog from the dialog fragment. + Dialog dialog = dialogFragment.getDialog(); + + // Remove the incorrect lint warning below that the dialog might be null. + assert dialog != null; - // Get the `EditText`s from the `editBookmarkDialogFragment` and extract the strings. - EditText editBookmarkNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext); + // Get handles for the views from `dialogFragment`. + EditText editBookmarkNameEditText = dialog.findViewById(R.id.edit_bookmark_name_edittext); + EditText editBookmarkUrlEditText = dialog.findViewById(R.id.edit_bookmark_url_edittext); + RadioButton currentBookmarkIconRadioButton = dialog.findViewById(R.id.edit_bookmark_current_icon_radiobutton); + + // Store the bookmark strings. String bookmarkNameString = editBookmarkNameEditText.getText().toString(); - EditText editBookmarkUrlEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext); String bookmarkUrlString = editBookmarkUrlEditText.getText().toString(); - // Get `edit_bookmark_current_icon_radiobutton`. - RadioButton currentBookmarkIconRadioButton = (RadioButton) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_current_icon_radiobutton); - + // Update the bookmark. if (currentBookmarkIconRadioButton.isChecked()) { // Update the bookmark without changing the favorite icon. bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString); - } else { // Update the bookmark using the `WebView` favorite icon. + } else { // Update the bookmark using the WebView favorite icon. + // Create a favorite icon byte array output stream. ByteArrayOutputStream newFavoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); - MainWebViewActivity.favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream); + + // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream); + + // Convert the favorite icon byte array stream to a byte array. byte[] newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray(); // Update the bookmark and the favorite icon. @@ -681,228 +871,311 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma // Close the contextual action mode. contextualActionMode.finish(); - // Refresh the `ListView`. `setSelection` scrolls to the position of the bookmark that was edited. - updateBookmarksListView(currentFolder); - bookmarksListView.setSelection(selectedBookmarkPosition); + // Update the bookmarks cursor with the contents of the current folder. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder); + + // Update the `ListView`. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); } @Override - public void onSaveEditBookmarkFolder(AppCompatDialogFragment dialogFragment) { + public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) { + // Get the dialog from the dialog fragment. + Dialog dialog = dialogFragment.getDialog(); + + // Remove the incorrect lint warning below that the dialog might be null. + assert dialog != null; + + // Get handles for the views from `dialogFragment`. + RadioButton currentFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_current_icon_radiobutton); + RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_default_icon_radiobutton); + ImageView defaultFolderIconImageView = dialog.findViewById(R.id.edit_folder_default_icon_imageview); + EditText editFolderNameEditText = dialog.findViewById(R.id.edit_folder_name_edittext); + // Get the new folder name. - EditText editFolderNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext); String newFolderNameString = editFolderNameEditText.getText().toString(); - // Check to see if the new folder name is unique. - Cursor bookmarkFolderCursor = bookmarksDatabaseHelper.getFolderCursor(newFolderNameString); - int existingFoldersWithNewName = bookmarkFolderCursor.getCount(); - bookmarkFolderCursor.close(); - if ( ((existingFoldersWithNewName == 0) || newFolderNameString.equals(oldFolderNameString)) && !newFolderNameString.isEmpty()) { - // Get a long array with the the database ID of the selected folder and convert it to an `int`. - long[] selectedFolderLongArray = bookmarksListView.getCheckedItemIds(); - int selectedFolderDatabaseId = (int) selectedFolderLongArray[0]; - - // Get the `RadioButtons` from the `Dialog`. - RadioButton currentFolderIconRadioButton = (RadioButton) dialogFragment.getDialog().findViewById(R.id.edit_folder_current_icon_radiobutton); - RadioButton defaultFolderIconRadioButton = (RadioButton) dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_radiobutton); - - // Check if the favorite icon has changed. - if (currentFolderIconRadioButton.isChecked()) { - // Update the folder name if it has changed without modifying the favorite icon. - if (!newFolderNameString.equals(oldFolderNameString)) { - bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString); - - // Refresh the `ListView`. `setSelection` scrolls to the position of the folder that was edited. - updateBookmarksListView(currentFolder); - bookmarksListView.setSelection(selectedBookmarkPosition); - } - } else { // Update the folder icon. - // Get the new folder icon `Bitmap`. - Bitmap folderIconBitmap; - if (defaultFolderIconRadioButton.isChecked()) { - // Get the default folder icon `ImageView` from the `Drawable` and convert it to a `Bitmap`. - ImageView folderIconImageView = (ImageView) dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon); - Drawable folderIconDrawable = folderIconImageView.getDrawable(); - BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; - folderIconBitmap = folderIconBitmapDrawable.getBitmap(); - } else { // Get the web page icon `ImageView` from the `Dialog`. - folderIconBitmap = MainWebViewActivity.favoriteIconBitmap; - } + // Check if the favorite icon has changed. + if (currentFolderIconRadioButton.isChecked()) { // Only the name has changed. + // Update the name in the database. + bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString); + } else if (!currentFolderIconRadioButton.isChecked() && newFolderNameString.equals(oldFolderNameString)) { // Only the icon has changed. + // Create the new folder icon Bitmap. + Bitmap folderIconBitmap; + + // Populate the new folder icon bitmap. + if (defaultFolderIconRadioButton.isChecked()) { + // Get the default folder icon drawable. + Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable(); + + // Convert the folder icon drawable to a bitmap drawable. + BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; + + // Convert the folder icon bitmap drawable to a bitmap. + folderIconBitmap = folderIconBitmapDrawable.getBitmap(); + } else { // Use the WebView favorite icon. + // Copy the favorite icon bitmap to the folder icon bitmap. + folderIconBitmap = favoriteIconBitmap; + } + + // Create a folder icon byte array output stream. + ByteArrayOutputStream newFolderIconByteArrayOutputStream = new ByteArrayOutputStream(); - // Convert the folder `Bitmap` to a byte array. `0` is for lossless compression (the only option for a PNG). - ByteArrayOutputStream folderIconByteArrayOutputStream = new ByteArrayOutputStream(); - folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream); - byte[] folderIconByteArray = folderIconByteArrayOutputStream.toByteArray(); + // Convert the folder icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream); - bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString, folderIconByteArray); + // Convert the folder icon byte array stream to a byte array. + byte[] newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray(); - // Refresh the `ListView`. `setSelection` scrolls to the position of the folder that was edited. - updateBookmarksListView(currentFolder); - bookmarksListView.setSelection(selectedBookmarkPosition); + // Update the folder icon in the database. + bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, newFolderIconByteArray); + } else { // The folder icon and the name have changed. + // Instantiate the new folder icon `Bitmap`. + Bitmap folderIconBitmap; + + // Populate the new folder icon bitmap. + if (defaultFolderIconRadioButton.isChecked()) { + // Get the default folder icon drawable. + Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable(); + + // Convert the folder icon drawable to a bitmap drawable. + BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; + + // Convert the folder icon bitmap drawable to a bitmap. + folderIconBitmap = folderIconBitmapDrawable.getBitmap(); + } else { // Use the WebView favorite icon. + // Copy the favorite icon bitmap to the folder icon bitmap. + folderIconBitmap = favoriteIconBitmap; } - } else { // Don't edit the folder because the new name is not unique. - String cannot_rename_folder = getResources().getString(R.string.cannot_save_folder) + " \"" + newFolderNameString + "\""; - Snackbar.make(findViewById(R.id.bookmarks_coordinatorlayout), cannot_rename_folder, Snackbar.LENGTH_INDEFINITE).show(); + + // Create a folder icon byte array output stream. + ByteArrayOutputStream newFolderIconByteArrayOutputStream = new ByteArrayOutputStream(); + + // Convert the folder icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream); + + // Convert the folder icon byte array stream to a byte array. + byte[] newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray(); + + // Update the folder name and icon in the database. + bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString, newFolderIconByteArray); } + // Update the bookmarks cursor with the current contents of this folder. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder); + + // Update the `ListView`. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); + // Close the contextual action mode. contextualActionMode.finish(); } @Override - public void onMoveToFolder(AppCompatDialogFragment dialogFragment) { - // Get the new folder database id. - ListView folderListView = (ListView) dialogFragment.getDialog().findViewById(R.id.move_to_folder_listview); - long[] newFolderLongArray = folderListView.getCheckedItemIds(); + public void onMoveToFolder(DialogFragment dialogFragment) { + // Get the dialog from the dialog fragment. + Dialog dialog = dialogFragment.getDialog(); - if (newFolderLongArray.length == 0) { // No new folder was selected. - Snackbar.make(findViewById(R.id.bookmarks_coordinatorlayout), getString(R.string.cannot_move_bookmarks), Snackbar.LENGTH_INDEFINITE).show(); - } else { // Move the selected bookmarks. - // Get the new folder database ID. - int newFolderDatabaseId = (int) newFolderLongArray[0]; + // Remove the incorrect lint warning below that the dialog might be null. + assert dialog != null; - // Instantiate `newFolderName`. - String newFolderName; + // Get a handle for the list view from the dialog. + ListView folderListView = dialog.findViewById(R.id.move_to_folder_listview); - if (newFolderDatabaseId == 0) { - // The new folder is the home folder, represented as `""` in the database. - newFolderName = ""; - } else { - // Get the new folder name from the database. - newFolderName = bookmarksDatabaseHelper.getFolderName(newFolderDatabaseId); - } + // Store a long array of the selected folders. + long[] newFolderLongArray = folderListView.getCheckedItemIds(); - // Get a long array with the the database ID of the selected bookmarks. - long[] selectedBookmarksLongArray = bookmarksListView.getCheckedItemIds(); - for (long databaseIdLong : selectedBookmarksLongArray) { - // Get `databaseIdInt` for each selected bookmark. - int databaseIdInt = (int) databaseIdLong; + // Get the new folder database ID. Only one folder will be selected. + int newFolderDatabaseId = (int) newFolderLongArray[0]; - // Move the selected bookmark to the new folder. - bookmarksDatabaseHelper.moveToFolder(databaseIdInt, newFolderName); - } + // Instantiate `newFolderName`. + String newFolderName; - // Refresh the `ListView`. - updateBookmarksListView(currentFolder); + // Set the new folder name. + if (newFolderDatabaseId == 0) { + // The new folder is the home folder, represented as `""` in the database. + newFolderName = ""; + } else { + // Get the new folder name from the database. + newFolderName = bookmarksDatabaseHelper.getFolderName(newFolderDatabaseId); + } - // Close the contextual app bar. - contextualActionMode.finish(); + // Get a long array with the the database ID of the selected bookmarks. + long[] selectedBookmarksLongArray = bookmarksListView.getCheckedItemIds(); + + // Move each of the selected bookmarks to the new folder. + for (long databaseIdLong : selectedBookmarksLongArray) { + // Get `databaseIdInt` for each selected bookmark. + int databaseIdInt = (int) databaseIdLong; + + // Move the selected bookmark to the new folder. + bookmarksDatabaseHelper.moveToFolder(databaseIdInt, newFolderName); } + + // Update the bookmarks cursor with the current contents of this folder. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder); + + // Update the `ListView`. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); + + // Close the contextual app bar. + contextualActionMode.finish(); } - private void updateBookmarksListView(String folderName) { - // Get a `Cursor` with the current contents of the bookmarks database. - bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(folderName); + private void deleteBookmarkFolderContents(int databaseId) { + // Get the name of the folder. + String folderName = bookmarksDatabaseHelper.getFolderName(databaseId); - // Setup `bookmarksCursorAdapter` with `this` context. `false` disables `autoRequery`. - CursorAdapter bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) { - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - // Inflate the individual item layout. `false` does not attach it to the root. - return getLayoutInflater().inflate(R.layout.bookmarks_item_linearlayout, parent, false); - } + // Get the contents of the folder. + Cursor folderCursor = bookmarksDatabaseHelper.getBookmarkIDs(folderName); - @Override - public void bindView(View view, Context context, Cursor cursor) { - // Get the favorite icon byte array from the `Cursor`. - byte[] favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON)); + // Delete each of the bookmarks in the folder. + for (int i = 0; i < folderCursor.getCount(); i++) { + // Move `folderCursor` to the current row. + folderCursor.moveToPosition(i); - // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last. - Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length); + // Get the database ID of the item. + int itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper._ID)); - // Display the bitmap in `bookmarkFavoriteIcon`. - ImageView bookmarkFavoriteIcon = (ImageView) view.findViewById(R.id.bookmark_favorite_icon); - bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap); + // If this is a folder, recursively delete the contents first. + if (bookmarksDatabaseHelper.isFolder(itemDatabaseId)) { + deleteBookmarkFolderContents(itemDatabaseId); + } + // Delete the bookmark. + bookmarksDatabaseHelper.deleteBookmark(itemDatabaseId); + } + } - // Get the bookmark name from the cursor and display it in `bookmarkNameTextView`. - String bookmarkNameString = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); - TextView bookmarkNameTextView = (TextView) view.findViewById(R.id.bookmark_name); - bookmarkNameTextView.setText(bookmarkNameString); + private void updateMoveIcons() { + // Get a long array of the selected bookmarks. + long[] selectedBookmarksLongArray = bookmarksListView.getCheckedItemIds(); - // Make the font bold for folders. - if (cursor.getInt(cursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) { - bookmarkNameTextView.setTypeface(Typeface.DEFAULT_BOLD); - } else { // Reset the font to default for normal bookmarks. - bookmarkNameTextView.setTypeface(Typeface.DEFAULT); - } + // Get the database IDs for the first, last, and selected bookmarks. + int selectedBookmarkDatabaseId = (int) selectedBookmarksLongArray[0]; + int firstBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(0); + // bookmarksListView is 0 indexed. + int lastBookmarkDatabaseId = (int) bookmarksListView.getItemIdAtPosition(bookmarksListView.getCount() - 1); + + // Get the current theme status. + int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + + // Update the move bookmark up `MenuItem`. + if (selectedBookmarkDatabaseId == firstBookmarkDatabaseId) { // The selected bookmark is in the first position. + // Disable the move bookmark up `MenuItem`. + moveBookmarkUpMenuItem.setEnabled(false); + + // Set the move bookmark up icon to be ghosted. + moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_disabled); + } else { // The selected bookmark is not in the first position. + // Enable the move bookmark up menu item. + moveBookmarkUpMenuItem.setEnabled(true); + + // Set the icon according to the theme. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { + moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_enabled_night); + } else { + moveBookmarkUpMenuItem.setIcon(R.drawable.move_up_enabled_day); } - }; + } - // Update the ListView. - bookmarksListView.setAdapter(bookmarksCursorAdapter); + // Update the move bookmark down `MenuItem`. + if (selectedBookmarkDatabaseId == lastBookmarkDatabaseId) { // The selected bookmark is in the last position. + // Disable the move bookmark down `MenuItem`. + moveBookmarkDownMenuItem.setEnabled(false); - // Set the AppBar title. - if (currentFolder.isEmpty()) { - appBar.setTitle(R.string.bookmarks); - } else { - appBar.setTitle(currentFolder); + // Set the move bookmark down icon to be ghosted. + moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_disabled); + } else { // The selected bookmark is not in the last position. + // Enable the move bookmark down `MenuItem`. + moveBookmarkDownMenuItem.setEnabled(true); + + // Set the icon according to the theme. + if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { + moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_enabled_night); + } else { + moveBookmarkDownMenuItem.setIcon(R.drawable.move_down_enabled_day); + } } } - private void updateBookmarksListViewExcept(long[] exceptIdLongArray, String folderName) { - // Get a `Cursor` with the current contents of the bookmarks database except for the specified database IDs. - bookmarksCursor = bookmarksDatabaseHelper.getBookmarksCursorExcept(exceptIdLongArray, folderName); + private void scrollBookmarks(int selectedBookmarkPosition) { + // Get the first and last visible bookmark positions. + int firstVisibleBookmarkPosition = bookmarksListView.getFirstVisiblePosition(); + int lastVisibleBookmarkPosition = bookmarksListView.getLastVisiblePosition(); + + // Calculate the number of bookmarks per screen. + int numberOfBookmarksPerScreen = lastVisibleBookmarkPosition - firstVisibleBookmarkPosition; + + // Scroll with the moved bookmark if necessary. + if (selectedBookmarkPosition <= firstVisibleBookmarkPosition) { // The selected bookmark position is at or above the top of the screen. + // Scroll to the selected bookmark position. + bookmarksListView.setSelection(selectedBookmarkPosition); + } else if (selectedBookmarkPosition >= (lastVisibleBookmarkPosition - 1)) { // The selected bookmark is at or below the bottom of the screen. + // The `-1` handles partial bookmarks displayed at the bottom of the list view. This command scrolls to display the selected bookmark at the bottom of the screen. + // `+1` assures that the entire bookmark will be displayed in situations where only a partial bookmark fits at the bottom of the list view. + bookmarksListView.setSelection(selectedBookmarkPosition - numberOfBookmarksPerScreen + 1); + } + } + + private void loadFolder() { + // Update bookmarks cursor with the contents of the bookmarks database for the current folder. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentFolder); - // Setup `bookmarksCursorAdapter` with `this` context. `false` disables autoRequery. - CursorAdapter bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) { + // Setup a `CursorAdapter`. `this` specifies the `Context`. `false` disables `autoRequery`. + bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { // Inflate the individual item layout. `false` does not attach it to the root. - return getLayoutInflater().inflate(R.layout.bookmarks_item_linearlayout, parent, false); + return getLayoutInflater().inflate(R.layout.bookmarks_activity_item_linearlayout, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { - // Get the favorite icon byte array from the cursor. + // Get handles for the views. + ImageView bookmarkFavoriteIcon = view.findViewById(R.id.bookmark_favorite_icon); + TextView bookmarkNameTextView = view.findViewById(R.id.bookmark_name); + + // Get the favorite icon byte array from the `Cursor`. byte[] favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON)); - // Convert the byte array to a Bitmap beginning at the first byte and ending at the last. + // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last. Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length); // Display the bitmap in `bookmarkFavoriteIcon`. - ImageView bookmarkFavoriteIcon = (ImageView) view.findViewById(R.id.bookmark_favorite_icon); bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap); - // Get the bookmark name from the cursor and display it in `bookmarkNameTextView`. String bookmarkNameString = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); - TextView bookmarkNameTextView = (TextView) view.findViewById(R.id.bookmark_name); bookmarkNameTextView.setText(bookmarkNameString); // Make the font bold for folders. if (cursor.getInt(cursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) { - // The first argument is `null` because we don't want to change the font. - bookmarkNameTextView.setTypeface(null, Typeface.BOLD); - } else { // Reset the font to default. + bookmarkNameTextView.setTypeface(Typeface.DEFAULT_BOLD); + } else { // Reset the font to default for normal bookmarks. bookmarkNameTextView.setTypeface(Typeface.DEFAULT); } } }; - // Update the `ListView`. + // Populate the list view with the adapter. bookmarksListView.setAdapter(bookmarksCursorAdapter); - } - - private void deleteBookmarkFolderContents(int databaseId) { - // Get the name of the folder. - String folderName = bookmarksDatabaseHelper.getFolderName(databaseId); - // Get the contents of the folder. - Cursor folderCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(folderName); - - for (int i = 0; i < folderCursor.getCount(); i++) { - // Move `folderCursor` to the current row. - folderCursor.moveToPosition(i); - - // Get the database ID of the item. - int itemDatabaseId = folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper._ID)); + // Set the `AppBar` title. + if (currentFolder.isEmpty()) { + appBar.setTitle(R.string.bookmarks); + } else { + appBar.setTitle(currentFolder); + } + } - // If this is a folder, delete the contents first. - if (bookmarksDatabaseHelper.isFolder(itemDatabaseId)) { - deleteBookmarkFolderContents(itemDatabaseId); - } + @Override + public void onDestroy() { + // Close the bookmarks cursor and database. + bookmarksCursor.close(); + bookmarksDatabaseHelper.close(); - bookmarksDatabaseHelper.deleteBookmark(itemDatabaseId); - } + // Run the default commands. + super.onDestroy(); } } \ No newline at end of file