Add editing functionality to the bookmarks database view. https://redmine.stoutner...
authorSoren Stoutner <soren@stoutner.com>
Fri, 3 Nov 2017 22:52:31 +0000 (15:52 -0700)
committerSoren Stoutner <soren@stoutner.com>
Fri, 3 Nov 2017 22:52:31 +0000 (15:52 -0700)
31 files changed:
.idea/dictionaries/soren.xml
.idea/misc.xml
app/build.gradle
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.java [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.java [new file with mode: 0644]
app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.java
app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java
app/src/main/java/com/stoutner/privacybrowser/fragments/AboutTabFragment.java
app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java
app/src/main/res/color/edit_bookmark_spinner_color_selector_dark.xml [new file with mode: 0644]
app/src/main/res/color/edit_bookmark_spinner_color_selector_light.xml [new file with mode: 0644]
app/src/main/res/color/primary_text_color_selector_light.xml
app/src/main/res/layout/about_tab_version.xml
app/src/main/res/layout/bookmarks_databaseview_coordinatorlayout.xml
app/src/main/res/layout/bookmarks_databaseview_item_linearlayout.xml
app/src/main/res/layout/bookmarks_databaseview_spinner.xml
app/src/main/res/layout/edit_bookmark_databaseview_dialog.xml [new file with mode: 0644]
app/src/main/res/layout/edit_bookmark_databaseview_spinner_dropdown_item.xml [new file with mode: 0644]
app/src/main/res/layout/edit_bookmark_databaseview_spinner_item.xml [new file with mode: 0644]
app/src/main/res/layout/edit_bookmark_dialog.xml
app/src/main/res/layout/edit_bookmark_folder_databaseview_dialog.xml [new file with mode: 0644]
app/src/main/res/layout/edit_bookmark_folder_dialog.xml
app/src/main/res/values/attrs.xml
app/src/main/res/values/strings.xml
app/src/main/res/values/styles.xml
build.gradle
gradle/wrapper/gradle-wrapper.properties

index 39f061db622bedefe34f14a0c85e44d06f39c2d5..c6d78c7ebecb037bedfffbe14df270acbd8ebde0 100644 (file)
@@ -11,6 +11,7 @@
       <w>anonymized</w>
       <w>appbarlayout</w>
       <w>aren</w>
+      <w>autoselected</w>
       <w>bebdb</w>
       <w>becerra</w>
       <w>beeb</w>
@@ -27,6 +28,7 @@
       <w>commitdiff</w>
       <w>coordinatorlayout</w>
       <w>customuseragent</w>
+      <w>databaseview</w>
       <w>deeplinks</w>
       <w>didn</w>
       <w>displayimages</w>
index 95f0f031062694350692a50d7adca27d559d5d41..4a0c9c497490c6fad7a7dc7fa0e3cce68bf8868a 100644 (file)
       </value>
     </option>
   </component>
-  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
-    <OptionsSetting value="true" id="Add" />
-    <OptionsSetting value="true" id="Remove" />
-    <OptionsSetting value="true" id="Checkout" />
-    <OptionsSetting value="true" id="Update" />
-    <OptionsSetting value="true" id="Status" />
-    <OptionsSetting value="true" id="Edit" />
-    <ConfirmationsSetting value="0" id="Add" />
-    <ConfirmationsSetting value="0" id="Remove" />
-  </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/build/classes" />
   </component>
   <component name="ProjectType">
index aa47ffcbfb40c6ad74af153cf329e4230ac8a176..75ed74f3c6d9b9d709c2d039b4fd636ce892ed60 100644 (file)
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 25
-    buildToolsVersion "25.0.3"
+    compileSdkVersion 26
+    buildToolsVersion '26.0.2'
     defaultConfig {
         minSdkVersion 19
-        targetSdkVersion 25
+        targetSdkVersion 26
         versionCode 26
         versionName "2.6"
     }
@@ -42,23 +42,32 @@ android {
         }
     }
 
+    // Gradle requires a `flavorDimension`, but it isn't used for anything in Privacy Browser.
+    flavorDimensions "basic"
+
     productFlavors {
         standard {
             applicationId "com.stoutner.privacybrowser.standard"
+            dimension "basic"
         }
 
         free {
             applicationId "com.stoutner.privacybrowser.free"
+            dimension "basic"
         }
     }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
 }
 
 dependencies {
-    compile fileTree(include: ['*.jar'], dir: 'libs')
-    compile 'com.android.support:design:25.4.0'
-
+    implementation fileTree(include: ['*.jar'], dir: 'libs')
+    implementation 'com.android.support:design:26.1.0'
     // Only compile `com.google.firebase:firebase-ads:9.8.0` for the free flavor.
-    freeCompile 'com.google.firebase:firebase-ads:11.0.4'
+    freeImplementation 'com.google.firebase:firebase-ads:11.4.2'
 }
 
 // Google's documentation says the following line is required for `firebase-ads` but things work correctly without it.  I have no interest in applying the Google Mobile Services plugin in the standard flavor if I don't have to.
index e640b792291e208736931a21637a5a4ea51639cc..763f63851cec91512347eb9ed8d504324df0bfb1 100644 (file)
@@ -34,6 +34,7 @@ import android.support.design.widget.Snackbar;
 import android.support.v4.app.NavUtils;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
+// `AppCompatDialogFragment` is required instead of `DialogFragment` or an error is produced on API <=22.
 import android.support.v7.app.AppCompatDialogFragment;
 import android.support.v7.widget.Toolbar;
 import android.util.SparseBooleanArray;
@@ -43,7 +44,6 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
-import android.widget.AdapterView;
 import android.widget.CursorAdapter;
 import android.widget.EditText;
 import android.widget.ImageView;
@@ -64,26 +64,26 @@ import java.io.ByteArrayOutputStream;
 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 `MoveToFolderDialog`.  It is also used in `onCreate()`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`,
-    // `onSaveEditBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`, and `loadFolder().
-    public static BookmarksDatabaseHelper bookmarksDatabaseHelper;
-
     // `currentFolder` is public static so it can be accessed from `MoveToFolderDialog`.
-    // It is used in `onCreate`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, `onMoveToFolder()`, and `loadFolder()`.
+    // 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 `MoveToFolderDialog`.  It is also used in `onCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, `onMoveToFolder()`, and `updateMoveIcons()`.
+    // `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;
 
 
-    // `bookmarksListView` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, `onMoveToFolder()`, `updateMoveIcons()`, `scrollBookmarks()`,
+    // `bookmarksDatabaseHelper` is used in `onCreate()`, `onOptionsItemSelected()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`,
+    // and `loadFolder().
+    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()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`, and `loadFolder()`.
+    // `bookmarksCursor` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, `deleteBookmarkFolderContents()`, and `loadFolder()`.
     private Cursor bookmarksCursor;
 
-    // `bookmarksCursorAdapter` is used in `onCreate(), `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, `onMoveToFolder()`, and `onLoadFolder()`.
+    // `bookmarksCursorAdapter` is used in `onCreate(), `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`, `onMoveToFolder()`, and `onLoadFolder()`.
     private CursorAdapter bookmarksCursorAdapter;
 
     // `contextualActionMode` is used in `onCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()` and `onMoveToFolder()`.
@@ -92,7 +92,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
     // `appBar` is used in `onCreate()` and `loadFolder()`.
     private ActionBar appBar;
 
-    // `oldFolderName` is used in `onCreate()` and `onSaveEditBookmarkFolder()`.
+    // `oldFolderName` is used in `onCreate()` and `onSaveBookmarkFolder()`.
     private String oldFolderNameString;
 
     // `moveBookmarkUpMenuItem` is used in `onCreate()` and `updateMoveIcons`.
@@ -127,13 +127,13 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
         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);
+        final Toolbar bookmarksAppBar = findViewById(R.id.bookmarks_toolbar);
         setSupportActionBar(bookmarksAppBar);
 
         // Get a handle for the activity, the app bar, and the `ListView`.
         final Activity bookmarksActivity = this;
         appBar = getSupportActionBar();
-        bookmarksListView = (ListView) findViewById(R.id.bookmarks_listview);
+        bookmarksListView = findViewById(R.id.bookmarks_listview);
 
         // This assert removes the incorrect warning in Android Studio on the following line that `appBar` might be null.
         assert appBar != null;
@@ -149,40 +149,37 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
         loadFolder();
 
         // Set a listener so that tapping a list item loads the URL or folder.
-        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;
+        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;
 
-                // Get the bookmark `Cursor` for this ID and move it to the first row.
-                Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmarkCursor(databaseID);
-                bookmarkCursor.moveToFirst();
+            // Get the bookmark `Cursor` for this ID and move it to the first row.
+            Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmarkCursor(databaseID);
+            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 `currentFolder`.
-                    currentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+            // Act upon the bookmark according to the type.
+            if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {  // The selected bookmark is a folder.
+                // Update `currentFolder`.
+                currentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
 
-                    // Load the new folder.
-                    loadFolder();
-                } else {  // The selected bookmark is not a folder.
-                    // 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));
+                // Load the new folder.
+                loadFolder();
+            } else {  // The selected bookmark is not a folder.
+                // 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));
 
-                    // Update the bookmarks folder for the bookmarks drawer in `MainWebViewActivity`.
-                    MainWebViewActivity.currentBookmarksFolder = currentFolder;
+                // Update the bookmarks folder for the bookmarks drawer in `MainWebViewActivity`.
+                MainWebViewActivity.currentBookmarksFolder = currentFolder;
 
-                    // Close the bookmarks drawer and reload the bookmarks `ListView` when returning to `MainWebViewActivity`.
-                    MainWebViewActivity.restartFromBookmarksActivity = true;
-
-                    // Return to `MainWebViewActivity`.
-                    NavUtils.navigateUpFromSameTask(bookmarksActivity);
-                }
+                // Close the bookmarks drawer and reload the bookmarks `ListView` when returning to `MainWebViewActivity`.
+                MainWebViewActivity.restartFromBookmarksActivity = true;
 
-                // Close the `Cursor`.
-                bookmarkCursor.close();
+                // Return to `MainWebViewActivity`.
+                NavUtils.navigateUpFromSameTask(bookmarksActivity);
             }
+
+            // Close the `Cursor`.
+            bookmarkCursor.close();
         });
 
         // `MultiChoiceModeListener` handles long clicks.
@@ -400,22 +397,23 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
                         // Get the position of the selected bookmark.  Only one bookmark is selected when `edit_bookmark_down` is enabled.
                         selectedBookmarkPosition = selectedBookmarksPositionsSparseBooleanArray.keyAt(0);
 
-                        // Move to the selected position and find out if it is a folder.
+                        // Move the `Cursor` to the selected position and find out if it is a folder.
                         bookmarksCursor.moveToPosition(selectedBookmarkPosition);
                         boolean isFolder = (bookmarksCursor.getInt(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1);
 
                         // Get the selected bookmark database ID.
                         int databaseId = bookmarksCursor.getInt(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper._ID));
 
+                        // Show the edit bookmark or edit bookmark folder dialog.
                         if (isFolder) {
-                            // Save the current folder name, which is used in `onSaveEditBookmarkFolder()`.
+                            // Save the current folder name, which is used in `onSaveBookmarkFolder()`.
                             oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
 
-                            // Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`.
+                            // Show the edit bookmark folder dialog.
                             AppCompatDialogFragment editFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId);
                             editFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_folder));
                         } else {
-                            // Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`.
+                            // Show the edit bookmark dialog.
                             AppCompatDialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId);
                             editBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark));
                         }
@@ -449,11 +447,8 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
 
                         // Show a SnackBar.
                         Snackbar.make(findViewById(R.id.bookmarks_coordinatorlayout), snackbarMessage, Snackbar.LENGTH_LONG)
-                                .setAction(R.string.undo, new View.OnClickListener() {
-                                    @Override
-                                    public void onClick(View view) {
-                                        // Do nothing because everything will be handled by `onDismissed()` below.
-                                    }
+                                .setAction(R.string.undo, view -> {
+                                    // Do nothing because everything will be handled by `onDismissed()` below.
                                 })
                                 .addCallback(new Snackbar.Callback() {
                                     @Override
@@ -541,27 +536,21 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
         });
 
         // Get handles for the `FloatingActionButtons`.
-        FloatingActionButton createBookmarkFolderFab = (FloatingActionButton) findViewById(R.id.create_bookmark_folder_fab);
-        FloatingActionButton createBookmarkFab = (FloatingActionButton) findViewById(R.id.create_bookmark_fab);
+        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(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Show the `CreateBookmarkFolderDialog` `AlertDialog` and name the instance `@string/create_folder`.
-                AppCompatDialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog();
-                createBookmarkFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_folder));
-            }
+        createBookmarkFolderFab.setOnClickListener(v -> {
+            // Show the `CreateBookmarkFolderDialog` `AlertDialog` and name the instance `@string/create_folder`.
+            AppCompatDialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog();
+            createBookmarkFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_folder));
         });
 
         // Set the create new bookmark FAB to display the `AlertDialog`.
-        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));
-            }
+        createBookmarkFab.setOnClickListener(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));
         });
     }
 
@@ -641,8 +630,8 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
     @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);
+        EditText createBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext);
+        EditText createBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext);
 
         // Extract the strings from the `EditTexts`.
         String bookmarkNameString = createBookmarkNameEditText.getText().toString();
@@ -657,7 +646,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
         int newBookmarkDisplayOrder = bookmarksListView.getCount();
 
         // Create the bookmark.
-        bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, newBookmarkDisplayOrder, currentFolder, favoriteIconByteArray);
+        bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, currentFolder, newBookmarkDisplayOrder, favoriteIconByteArray);
 
         // Update `bookmarksCursor` with the current contents of this folder.
         bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(currentFolder);
@@ -672,9 +661,9 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
     @Override
     public void onCreateBookmarkFolder(AppCompatDialogFragment dialogFragment) {
         // Get handles for the views in `dialogFragment`.
-        EditText createFolderNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext);
-        RadioButton defaultFolderIconRadioButton = (RadioButton) dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton);
-        ImageView folderIconImageView = (ImageView) dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon);
+        EditText createFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext);
+        RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton);
+        ImageView folderIconImageView = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon);
 
         // Get new folder name string.
         String folderNameString = createFolderNameEditText.getText().toString();
@@ -701,8 +690,8 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
             bookmarksDatabaseHelper.updateDisplayOrder(databaseId, i + 1);
         }
 
-        // Create the folder, placing it at the top of the ListView
-        bookmarksDatabaseHelper.createFolder(folderNameString, 0, currentFolder, folderIconByteArray);
+        // Create the folder, which will be placed at the top of the `ListView`.
+        bookmarksDatabaseHelper.createFolder(folderNameString, currentFolder, folderIconByteArray);
 
         // Update `bookmarksCursor` with the current contents of this folder.
         bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(currentFolder);
@@ -715,11 +704,11 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
     }
 
     @Override
-    public void onSaveEditBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId) {
+    public void onSaveBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId) {
         // Get handles for the views from `dialogFragment`.
-        EditText editBookmarkNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext);
-        EditText editBookmarkUrlEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext);
-        RadioButton currentBookmarkIconRadioButton = (RadioButton) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_current_icon_radiobutton);
+        EditText editBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext);
+        EditText editBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext);
+        RadioButton currentBookmarkIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_current_icon_radiobutton);
 
         // Store the bookmark strings.
         String bookmarkNameString = editBookmarkNameEditText.getText().toString();
@@ -741,7 +730,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
         // Close the contextual action mode.
         contextualActionMode.finish();
 
-        // Update `bookmarksCursor` with the current contents of this folder.
+        // Update `bookmarksCursor` with the contents of the current folder.
         bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(currentFolder);
 
         // Update the `ListView`.
@@ -749,12 +738,12 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
     }
 
     @Override
-    public void onSaveEditBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId) {
+    public void onSaveBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId) {
         // Get handles for the views from `dialogFragment`.
-        EditText editFolderNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext);
-        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);
-        ImageView folderIconImageView = (ImageView) dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon);
+        RadioButton currentFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_current_icon_radiobutton);
+        RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_radiobutton);
+        ImageView defaultFolderIconImageView = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_imageview);
+        EditText editFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext);
 
         // Get the new folder name.
         String newFolderNameString = editFolderNameEditText.getText().toString();
@@ -768,7 +757,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
             Bitmap folderIconBitmap;
             if (defaultFolderIconRadioButton.isChecked()) {
                 // Get the default folder icon and convert it to a `Bitmap`.
-                Drawable folderIconDrawable = folderIconImageView.getDrawable();
+                Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable();
                 BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable;
                 folderIconBitmap = folderIconBitmapDrawable.getBitmap();
             } else {  // Use the `WebView` favorite icon.
@@ -783,11 +772,13 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
             // Update the folder icon in the database.
             bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, folderIconByteArray);
         } else {  // The folder icon and the name have changed.
-            // Get the new folder icon `Bitmap`.
+            // Instantiate the new folder icon `Bitmap`.
             Bitmap folderIconBitmap;
+
+            // Populate the new folder icon bitmap.
             if (defaultFolderIconRadioButton.isChecked()) {
                 // Get the default folder icon and convert it to a `Bitmap`.
-                Drawable folderIconDrawable = folderIconImageView.getDrawable();
+                Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable();
                 BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable;
                 folderIconBitmap = folderIconBitmapDrawable.getBitmap();
             } else {  // Use the `WebView` favorite icon.
@@ -816,7 +807,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
     @Override
     public void onMoveToFolder(AppCompatDialogFragment dialogFragment) {
         // Get a handle for the `ListView` from `dialogFragment`.
-        ListView folderListView = (ListView) dialogFragment.getDialog().findViewById(R.id.move_to_folder_listview);
+        ListView folderListView = dialogFragment.getDialog().findViewById(R.id.move_to_folder_listview);
 
         // Store a long array of the selected folders.
         long[] newFolderLongArray = folderListView.getCheckedItemIds();
@@ -965,8 +956,8 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma
             @Override
             public void bindView(View view, Context context, Cursor cursor) {
                 // Get handles for the views.
-                ImageView bookmarkFavoriteIcon = (ImageView) view.findViewById(R.id.bookmark_favorite_icon);
-                TextView bookmarkNameTextView = (TextView) view.findViewById(R.id.bookmark_name);
+                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));
index 1725cac46756d2c2c670061e7300a8ffe79633f0..a61391db27c45f3bb21058346f1166d3c4583593 100644 (file)
@@ -26,34 +26,57 @@ import android.database.MergeCursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.widget.CursorAdapter;
 import android.support.v4.widget.ResourceCursorAdapter;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
+// `AppCompatDialogFragment` is required instead of `DialogFragment` or an error is produced on API <=22.
+import android.support.v7.app.AppCompatDialogFragment;
 import android.support.v7.widget.Toolbar;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
+import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.ListView;
+import android.widget.RadioButton;
 import android.widget.Spinner;
 import android.widget.TextView;
 
 import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.dialogs.EditBookmarkDatabaseViewDialog;
+import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDatabaseViewDialog;
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
 
-public class BookmarksDatabaseViewActivity extends AppCompatActivity {
+import java.io.ByteArrayOutputStream;
+
+public class BookmarksDatabaseViewActivity extends AppCompatActivity implements EditBookmarkDatabaseViewDialog.EditBookmarkDatabaseViewListener, EditBookmarkFolderDatabaseViewDialog.EditBookmarkFolderDatabaseViewListener {
+    // Instantiate the constants.
+    private static final int ALL_FOLDERS_DATABASE_ID = -2;
+    private static final int HOME_FOLDER_DATABASE_ID = -1;
+
     // `bookmarksDatabaseHelper` is used in `onCreate()` and `updateBookmarksListView()`.
     private BookmarksDatabaseHelper bookmarksDatabaseHelper;
 
-    // `bookmarksListView` is used in `onCreate()` and `updateBookmarksListView()`.
-    private ListView bookmarksListView;
+    // `bookmarksCursor` is used in `onCreate()`, `updateBookmarksListView()`, `onSaveBookmark()`, and `onSaveBookmarkFolder()`.
+    private Cursor bookmarksCursor;
 
-    // `bookmarksCursorAdapter` is used in `onCreate()` and `updateBookmarksListView()`.
+    // `bookmarksCursorAdapter` is used in `onCreate()`, `onSaveBookmark()`, `onSaveBookmarkFolder()`.
     private CursorAdapter bookmarksCursorAdapter;
 
+    // `oldFolderNameString` is used in `onCreate()` and `onSaveBookmarkFolder()`.
+    private String oldFolderNameString;
+
+    // `currentFolderDatabaseId` is used in `onCreate()`, `onSaveBookmark()`, and `onSaveBookmarkFolder()`.
+    private int currentFolderDatabaseId;
+
+    // `currentFolder` is used in `onCreate()`, `onSaveBookmark()`, and `onSaveBookmarkFolder()`.
+    private String currentFolderName;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         // Set the activity theme.
@@ -63,11 +86,14 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
             setTheme(R.style.PrivacyBrowserLight_SecondaryActivity);
         }
 
+        // Run the default commands.
         super.onCreate(savedInstanceState);
+
+        // Set the content view.
         setContentView(R.layout.bookmarks_databaseview_coordinatorlayout);
 
-        // We need to use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21.
-        final Toolbar bookmarksDatabaseViewAppBar = (Toolbar) findViewById(R.id.bookmarks_database_view_toolbar);
+        // The `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21.
+        final Toolbar bookmarksDatabaseViewAppBar = findViewById(R.id.bookmarks_databaseview_toolbar);
         setSupportActionBar(bookmarksDatabaseViewAppBar);
 
         // Get a handle for the `AppBar`.
@@ -86,8 +112,8 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
         // Setup a `MatrixCursor` for "All Folders" and "Home Folder".
         String[] matrixCursorColumnNames = {BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME};
         MatrixCursor matrixCursor = new MatrixCursor(matrixCursorColumnNames);
-        matrixCursor.addRow(new Object[]{-2, getString(R.string.all_folders)});
-        matrixCursor.addRow(new Object[]{-1, getString(R.string.home_folder)});
+        matrixCursor.addRow(new Object[]{ALL_FOLDERS_DATABASE_ID, getString(R.string.all_folders)});
+        matrixCursor.addRow(new Object[]{HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)});
 
         // Get a `Cursor` with the list of all the folders.
         Cursor foldersCursor = bookmarksDatabaseHelper.getAllFoldersCursor();
@@ -95,12 +121,12 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
         // Combine `matrixCursor` and `foldersCursor`.
         MergeCursor foldersMergeCursor = new MergeCursor(new Cursor[]{matrixCursor, foldersCursor});
 
-        // Create a `ResourceCursorAdapter` for the spinner with `this` context.  `0` specifies no flags.;
+        // Create a `ResourceCursorAdapter` for the `Spinner` with `this` context.  `0` specifies no flags.;
         ResourceCursorAdapter foldersCursorAdapter = new ResourceCursorAdapter(this, R.layout.bookmarks_databaseview_spinner_item, foldersMergeCursor, 0) {
             @Override
             public void bindView(View view, Context context, Cursor cursor) {
-                // Get a handle for the spinner item `TextView`.
-                TextView spinnerItemTextView = (TextView) view.findViewById(R.id.spinner_item_textview);
+                // Get a handle for the `Spinner` item `TextView`.
+                TextView spinnerItemTextView = view.findViewById(R.id.spinner_item_textview);
 
                 // Set the `TextView` to display the folder name.
                 spinnerItemTextView.setText(cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)));
@@ -111,7 +137,7 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
         foldersCursorAdapter.setDropDownViewResource(R.layout.bookmarks_databaseview_spinner_dropdown_item);
 
         // Get a handle for the folder `Spinner`.
-        Spinner folderSpinner = (Spinner) findViewById(R.id.bookmarks_database_view_spinner);
+        Spinner folderSpinner = findViewById(R.id.bookmarks_databaseview_spinner);
 
         // Set the adapter for the folder `Spinner`.
         folderSpinner.setAdapter(foldersCursorAdapter);
@@ -123,33 +149,34 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
                 // Convert the database ID to an `int`.
                 int databaseId = (int) id;
 
-                // Instantiate the bookmarks `Cursor`.
-                Cursor bookmarksCursor;
+                // Store the current folder database ID.
+                currentFolderDatabaseId = databaseId;
 
                 // Populate the bookmarks `ListView` based on the `Spinner` selection.
                 switch (databaseId) {
-                    // Display all the folders.
-                    case -2:
-                        // Get a cursor with all the folders.
+                    // Get a cursor with all the folders.
+                    case ALL_FOLDERS_DATABASE_ID:
                         bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor();
                         break;
 
-                    // Display the home folder.
-                    case -1:
-                        // Get a cursor for the home folder.
+                    // Get a cursor for the home folder.
+                    case HOME_FOLDER_DATABASE_ID:
                         bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor("");
                         break;
 
                     // Display the selected folder.
                     default:
                         // Get a handle for the selected view.
-                        TextView selectedFolderTextView = (TextView) view.findViewById(R.id.spinner_item_textview);
+                        TextView selectedFolderTextView = view.findViewById(R.id.spinner_item_textview);
 
                         // Extract the name of the selected folder.
                         String folderName = selectedFolderTextView.getText().toString();
 
                         // Get a cursor for the selected folder.
                         bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor(folderName);
+
+                        // Store the current folder name.
+                        currentFolderName = folderName;
                 }
 
                 // Update the `ListView`.
@@ -163,17 +190,12 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
         });
 
         // Get a handle for the bookmarks `ListView`.
-        bookmarksListView = (ListView) findViewById(R.id.bookmarks_database_view_listview);
+        ListView bookmarksListView = findViewById(R.id.bookmarks_databaseview_listview);
 
-        // Display the bookmarks in the `ListView`.
-        updateBookmarksListView();
-    }
-
-    private void updateBookmarksListView() {
         // Get a `Cursor` with the current contents of the bookmarks database.
-        final Cursor bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor();
+        bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor();
 
-        // Setup `bookmarksCursorAdapter` with `this` context.  The `false` disables autoRequery.
+        // Setup a `CursorAdapter` with `this` context.  `false` disables autoRequery.
         bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) {
             @Override
             public View newView(Context context, Cursor cursor, ViewGroup parent) {
@@ -187,7 +209,7 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
 
                 // Get the database ID from the `Cursor` and display it in `bookmarkDatabaseIdTextView`.
                 int bookmarkDatabaseId = cursor.getInt(cursor.getColumnIndex(BookmarksDatabaseHelper._ID));
-                TextView bookmarkDatabaseIdTextView = (TextView) view.findViewById(R.id.bookmarks_database_view_database_id);
+                TextView bookmarkDatabaseIdTextView = view.findViewById(R.id.bookmarks_databaseview_database_id);
                 bookmarkDatabaseIdTextView.setText(String.valueOf(bookmarkDatabaseId));
 
                 // Get the favorite icon byte array from the `Cursor`.
@@ -195,30 +217,44 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
                 // Convert the byte array to a `Bitmap` beginning at the 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.bookmarks_database_view_favorite_icon);
+                ImageView bookmarkFavoriteIcon = view.findViewById(R.id.bookmarks_databaseview_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.bookmarks_database_view_bookmark_name);
+                TextView bookmarkNameTextView = view.findViewById(R.id.bookmarks_databaseview_bookmark_name);
                 bookmarkNameTextView.setText(bookmarkNameString);
+
                 // Make the font bold for folders.
                 if (isFolder) {
-                    // The first argument is `null` because we don't want to change the font.
+                    // The first argument is `null` prevent changing of the font.
                     bookmarkNameTextView.setTypeface(null, Typeface.BOLD);
                 } else {  // Reset the font to default.
                     bookmarkNameTextView.setTypeface(Typeface.DEFAULT);
                 }
 
+                // Get the bookmark URL form the `Cursor` and display it in `bookmarkUrlTextView`.
+                String bookmarkUrlString = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL));
+                TextView bookmarkUrlTextView = view.findViewById(R.id.bookmarks_databaseview_bookmark_url);
+                bookmarkUrlTextView.setText(bookmarkUrlString);
+
+                // Hide the URL if the bookmark is a folder.
+                if (isFolder) {
+                    bookmarkUrlTextView.setVisibility(View.GONE);
+                } else {
+                    bookmarkUrlTextView.setVisibility(View.VISIBLE);
+                }
+
                 // Get the display order from the `Cursor` and display it in `bookmarkDisplayOrderTextView`.
                 int bookmarkDisplayOrder = cursor.getInt(cursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER));
-                TextView bookmarkDisplayOrderTextView = (TextView) view.findViewById(R.id.bookmarks_database_view_display_order);
+                TextView bookmarkDisplayOrderTextView = view.findViewById(R.id.bookmarks_databaseview_display_order);
                 bookmarkDisplayOrderTextView.setText(String.valueOf(bookmarkDisplayOrder));
 
                 // Get the parent folder from the `Cursor` and display it in `bookmarkParentFolder`.
                 String bookmarkParentFolder = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER));
-                ImageView parentFolderImageView = (ImageView) view.findViewById(R.id.bookmarks_database_view_parent_folder_icon);
-                TextView bookmarkParentFolderTextView = (TextView) view.findViewById(R.id.bookmarks_database_view_parent_folder);
+                ImageView parentFolderImageView = view.findViewById(R.id.bookmarks_databaseview_parent_folder_icon);
+                TextView bookmarkParentFolderTextView = view.findViewById(R.id.bookmarks_databaseview_parent_folder);
+
                 // Make the folder name gray if it is the home folder.
                 if (bookmarkParentFolder.isEmpty()) {
                     parentFolderImageView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), R.drawable.folder_gray));
@@ -235,20 +271,164 @@ public class BookmarksDatabaseViewActivity extends AppCompatActivity {
                         bookmarkParentFolderTextView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.black));
                     }
                 }
-
-                // Get the bookmark URL form the `Cursor` and display it in `bookmarkUrlTextView`.
-                String bookmarkUrlString = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL));
-                TextView bookmarkUrlTextView = (TextView) view.findViewById(R.id.bookmarks_database_view_bookmark_url);
-                bookmarkUrlTextView.setText(bookmarkUrlString);
-                if (isFolder) {
-                    bookmarkUrlTextView.setVisibility(View.GONE);
-                } else {
-                    bookmarkUrlTextView.setVisibility(View.VISIBLE);
-                }
             }
         };
 
         // Update the ListView.
         bookmarksListView.setAdapter(bookmarksCursorAdapter);
+
+        // Set the current folder database ID.
+        currentFolderDatabaseId = ALL_FOLDERS_DATABASE_ID;
+
+        // Set a listener to edit a bookmark when it is tapped.
+        bookmarksListView.setOnItemClickListener((AdapterView<?> parent, View view, int position, long id) -> {
+            // Convert the database ID to an `int`.
+            int databaseId = (int) id;
+
+            // Show the edit bookmark or edit bookmark folder dialog.
+            if (bookmarksDatabaseHelper.isFolder(databaseId)) {
+                // Save the current folder name, which is used in `onSaveBookmarkFolder()`.
+                oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+
+                // Show the edit bookmark folder dialog.
+                AppCompatDialogFragment editBookmarkFolderDatabaseViewDialog = EditBookmarkFolderDatabaseViewDialog.folderDatabaseId(databaseId);
+                editBookmarkFolderDatabaseViewDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_folder));
+            } else {
+                // Show the edit bookmark dialog.
+                AppCompatDialogFragment editBookmarkDatabaseViewDialog = EditBookmarkDatabaseViewDialog.bookmarkDatabaseId(databaseId);
+                editBookmarkDatabaseViewDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark));
+            }
+        });
+    }
+
+    @Override
+    public void onSaveBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId) {
+        // Get handles for the views from dialog fragment.
+        RadioButton currentBookmarkIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_current_icon_radiobutton);
+        EditText editBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext);
+        EditText editBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext);
+        Spinner folderSpinner = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_folder_spinner);
+        EditText displayOrderEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_display_order_edittext);
+
+        // Extract the bookmark information.
+        String bookmarkNameString = editBookmarkNameEditText.getText().toString();
+        String bookmarkUrlString = editBookmarkUrlEditText.getText().toString();
+        int folderDatabaseId = (int) folderSpinner.getSelectedItemId();
+        int displayOrderInt = Integer.valueOf(displayOrderEditText.getText().toString());
+
+        // Instantiate the parent folder name `String`.
+        String parentFolderNameString;
+
+        // Set the parent folder name.
+        if (folderDatabaseId == EditBookmarkDatabaseViewDialog.HOME_FOLDER_DATABASE_ID) {  // The home folder is selected.  Use `""`.
+            parentFolderNameString = "";
+        } else {  // Get the parent folder name from the database.
+            parentFolderNameString = bookmarksDatabaseHelper.getFolderName(folderDatabaseId);
+        }
+
+        // Update the bookmark.
+        if (currentBookmarkIconRadioButton.isChecked()) {  // Update the bookmark without changing the favorite icon.
+            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, parentFolderNameString, displayOrderInt);
+        } else {  // Update the bookmark using the `WebView` favorite icon.
+            // Convert the favorite icon to a byte array.  `0` is for lossless compression (the only option for a PNG).
+            ByteArrayOutputStream newFavoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
+            MainWebViewActivity.favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream);
+            byte[] newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray();
+
+            //  Update the bookmark and the favorite icon.
+            bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, parentFolderNameString, displayOrderInt, newFavoriteIconByteArray);
+        }
+
+        // Update `bookmarksCursor` with the contents of the current folder.
+        switch (currentFolderDatabaseId) {
+            case ALL_FOLDERS_DATABASE_ID:
+                // Get a cursor with all the bookmarks.
+                bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor();
+                break;
+
+            case HOME_FOLDER_DATABASE_ID:
+                // Get a cursor with all the bookmarks in the home folder.
+                bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor("");
+                break;
+
+            default:
+                // Get a cursor with all the bookmarks in the current folder.
+                bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor(currentFolderName);
+        }
+
+        // Update the `ListView`.
+        bookmarksCursorAdapter.changeCursor(bookmarksCursor);
+    }
+
+    @Override
+    public void onSaveBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId) {
+        // Get handles for the views from dialog fragment.
+        RadioButton currentBookmarkIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_current_icon_radiobutton);
+        RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_radiobutton);
+        ImageView defaultFolderIconImageView = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_imageview);
+        EditText editBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext);
+        Spinner parentFolderSpinner = dialogFragment.getDialog().findViewById(R.id.edit_folder_parent_folder_spinner);
+        EditText displayOrderEditText = dialogFragment.getDialog().findViewById(R.id.edit_folder_display_order_edittext);
+
+        // Extract the folder information.
+        String newFolderNameString = editBookmarkNameEditText.getText().toString();
+        int parentFolderDatabaseId = (int) parentFolderSpinner.getSelectedItemId();
+        int displayOrderInt = Integer.valueOf(displayOrderEditText.getText().toString());
+
+        // Instantiate the parent folder name `String`.
+        String parentFolderNameString;
+
+        // Set the parent folder name.
+        if (parentFolderDatabaseId == EditBookmarkFolderDatabaseViewDialog.HOME_FOLDER_DATABASE_ID) {  // The home folder is selected.  Use `""`.
+            parentFolderNameString = "";
+        } else {  // Get the parent folder name from the database.
+            parentFolderNameString = bookmarksDatabaseHelper.getFolderName(parentFolderDatabaseId);
+        }
+
+        // Update the folder.
+        if (currentBookmarkIconRadioButton.isChecked()) {  // Update the folder without changing the favorite icon.
+            bookmarksDatabaseHelper.updateFolder(selectedBookmarkDatabaseId, oldFolderNameString, newFolderNameString, parentFolderNameString, displayOrderInt);
+        } else {  // Update the folder and the icon.
+            // Instantiate the new folder icon `Bitmap`.
+            Bitmap folderIconBitmap;
+
+            // Populate the new folder icon bitmap.
+            if (defaultFolderIconRadioButton.isChecked()) {
+                // Get the default folder icon and convert it to a `Bitmap`.
+                Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable();
+                BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable;
+                folderIconBitmap = folderIconBitmapDrawable.getBitmap();
+            } else {  // Use the `WebView` favorite icon.
+                folderIconBitmap = MainWebViewActivity.favoriteIconBitmap;
+            }
+
+            // Convert the folder icon to a byte array.  `0` is for lossless compression (the only option for a PNG).
+            ByteArrayOutputStream newFolderIconByteArrayOutputStream = new ByteArrayOutputStream();
+            folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream);
+            byte[] newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray();
+
+            //  Update the folder and the icon.
+            bookmarksDatabaseHelper.updateFolder(selectedBookmarkDatabaseId, oldFolderNameString, newFolderNameString, parentFolderNameString, displayOrderInt, newFolderIconByteArray);
+        }
+
+        // Update `bookmarksCursor` with the contents of the current folder.
+        switch (currentFolderDatabaseId) {
+            case ALL_FOLDERS_DATABASE_ID:
+                // Get a cursor with all the bookmarks.
+                bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor();
+                break;
+
+            case HOME_FOLDER_DATABASE_ID:
+                // Get a cursor with all the bookmarks in the home folder.
+                bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor("");
+                break;
+
+            default:
+                // Get a cursor with all the bookmarks in the current folder.
+                bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursor(currentFolderName);
+        }
+
+        // Update the `ListView`.
+        bookmarksCursorAdapter.changeCursor(bookmarksCursor);
     }
 }
\ No newline at end of file
index bb7ae1dedb6eac55911b4d0a0f9db371e439dc3e..c6f04a0fab47119a7ca812ea8b6af1c2a257e63c 100644 (file)
@@ -78,10 +78,8 @@ import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.CookieManager;
-import android.webkit.DownloadListener;
 import android.webkit.HttpAuthHandler;
 import android.webkit.SslErrorHandler;
-import android.webkit.ValueCallback;
 import android.webkit.WebBackForwardList;
 import android.webkit.WebChromeClient;
 import android.webkit.WebResourceResponse;
@@ -89,7 +87,6 @@ import android.webkit.WebStorage;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import android.webkit.WebViewDatabase;
-import android.widget.AdapterView;
 import android.widget.CursorAdapter;
 import android.widget.EditText;
 import android.widget.FrameLayout;
@@ -143,12 +140,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         NavigationView.OnNavigationItemSelectedListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener {
 
     // `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`,
-    // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `HttpAuthenticationDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`,
-    // `ViewSslCertificateDialog`, `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
+    // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `EditBookmarkDatabaseViewDialog`, `HttpAuthenticationDialog`, `MoveToFolderDialog`,
+    // `SslCertificateErrorDialog`, `UrlHistoryDialog`, `ViewSslCertificateDialog`, `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
     public static boolean darkTheme;
 
-    // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`,
-    // and `ViewSslCertificateDialog`.  It is also used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcutCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`.
+    // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `EditBookmarkDialog`,
+    // `EditBookmarkFolderDialog`, `EditBookmarkDatabaseViewDialog`, and `ViewSslCertificateDialog`.  It is also used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcutCreate()`, `onSaveEditBookmark()`,
+    // `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`.
     public static Bitmap favoriteIconBitmap;
 
     // `formattedUrlString` is public static so it can be accessed from `BookmarksActivity`, `CreateBookmarkDialog`, and `AddDomainDialog`.
@@ -369,7 +367,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.  The whole premise of Privacy Browser is built around an understanding of these dangers.
-    @SuppressLint("SetJavaScriptEnabled")
+    @SuppressLint({"SetJavaScriptEnabled"})
     // Remove Android Studio's warning about deprecations.  We have to use the deprecated `getColor()` until API >= 23.
     @SuppressWarnings("deprecation")
     protected void onCreate(Bundle savedInstanceState) {
@@ -396,7 +394,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
 
         // `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21.
-        supportAppBar = (Toolbar) findViewById(R.id.app_bar);
+        supportAppBar = findViewById(R.id.app_bar);
         setSupportActionBar(supportAppBar);
         appBar = getSupportActionBar();
 
@@ -413,42 +411,36 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
 
         // Get a handle for `urlTextBox`.
-        urlTextBox = (EditText) appBar.getCustomView().findViewById(R.id.url_edittext);
+        urlTextBox = appBar.getCustomView().findViewById(R.id.url_edittext);
 
         // Remove the formatting from `urlTextBar` when the user is editing the text.
-        urlTextBox.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                if (hasFocus) {  // The user is editing `urlTextBox`.
-                    // Remove the highlighting.
-                    urlTextBox.getText().removeSpan(redColorSpan);
-                    urlTextBox.getText().removeSpan(initialGrayColorSpan);
-                    urlTextBox.getText().removeSpan(finalGrayColorSpan);
-                } else {  // The user has stopped editing `urlTextBox`.
-                    // Reapply the highlighting.
-                    highlightUrlText();
-                }
+        urlTextBox.setOnFocusChangeListener((v, hasFocus) -> {
+            if (hasFocus) {  // The user is editing `urlTextBox`.
+                // Remove the highlighting.
+                urlTextBox.getText().removeSpan(redColorSpan);
+                urlTextBox.getText().removeSpan(initialGrayColorSpan);
+                urlTextBox.getText().removeSpan(finalGrayColorSpan);
+            } else {  // The user has stopped editing `urlTextBox`.
+                // Reapply the highlighting.
+                highlightUrlText();
             }
         });
 
         // Set the `Go` button on the keyboard to load the URL in `urlTextBox`.
-        urlTextBox.setOnKeyListener(new View.OnKeyListener() {
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                // If the event is a key-down event on the `enter` button, load the URL.
-                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
-                    // Load the URL into the mainWebView and consume the event.
-                    try {
-                        loadUrlFromTextBox();
-                    } catch (UnsupportedEncodingException e) {
-                        e.printStackTrace();
-                    }
-                    // If the enter key was pressed, consume the event.
-                    return true;
-                } else {
-                    // If any other key was pressed, do not consume the event.
-                    return false;
+        urlTextBox.setOnKeyListener((v, keyCode, event) -> {
+            // If the event is a key-down event on the `enter` button, load the URL.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
+                // Load the URL into the mainWebView and consume the event.
+                try {
+                    loadUrlFromTextBox();
+                } catch (UnsupportedEncodingException e) {
+                    e.printStackTrace();
                 }
+                // If the enter key was pressed, consume the event.
+                return true;
+            } else {
+                // If any other key was pressed, do not consume the event.
+                return false;
             }
         });
 
@@ -482,20 +474,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
 
         // Get handles for views that need to be accessed.
-        drawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout);
-        rootCoordinatorLayout = (CoordinatorLayout) findViewById(R.id.root_coordinatorlayout);
-        bookmarksListView = (ListView) findViewById(R.id.bookmarks_drawer_listview);
-        bookmarksTitleTextView = (TextView) findViewById(R.id.bookmarks_title_textview);
-        FloatingActionButton launchBookmarksActivityFab = (FloatingActionButton) findViewById(R.id.launch_bookmarks_activity_fab);
-        FloatingActionButton createBookmarkFolderFab = (FloatingActionButton) findViewById(R.id.create_bookmark_folder_fab);
-        FloatingActionButton createBookmarkFab = (FloatingActionButton) findViewById(R.id.create_bookmark_fab);
-        mainWebViewRelativeLayout = (RelativeLayout) findViewById(R.id.main_webview_relativelayout);
-        mainWebView = (WebView) findViewById(R.id.main_webview);
-        findOnPageLinearLayout = (LinearLayout) findViewById(R.id.find_on_page_linearlayout);
-        findOnPageEditText = (EditText) findViewById(R.id.find_on_page_edittext);
-        fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.full_screen_video_framelayout);
-        urlAppBarRelativeLayout = (RelativeLayout) findViewById(R.id.url_app_bar_relativelayout);
-        favoriteIconImageView = (ImageView) findViewById(R.id.favorite_icon);
+        drawerLayout = findViewById(R.id.drawerlayout);
+        rootCoordinatorLayout = findViewById(R.id.root_coordinatorlayout);
+        bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+        bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview);
+        FloatingActionButton launchBookmarksActivityFab = findViewById(R.id.launch_bookmarks_activity_fab);
+        FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab);
+        FloatingActionButton createBookmarkFab = findViewById(R.id.create_bookmark_fab);
+        mainWebViewRelativeLayout = findViewById(R.id.main_webview_relativelayout);
+        mainWebView = findViewById(R.id.main_webview);
+        findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+        findOnPageEditText = findViewById(R.id.find_on_page_edittext);
+        fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
+        urlAppBarRelativeLayout = findViewById(R.id.url_app_bar_relativelayout);
+        favoriteIconImageView = findViewById(R.id.favorite_icon);
 
         // Set the bookmarks drawer resources according to the theme.  This can't be done in the layout due to compatibility issues with the `DrawerLayout` support widget.
         if (darkTheme) {
@@ -511,38 +503,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         }
 
         // Set the launch bookmarks activity FAB to launch the bookmarks activity.
-        launchBookmarksActivityFab.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Create an intent to launch the bookmarks activity.
-                Intent bookmarksIntent = new Intent(getApplicationContext(), BookmarksActivity.class);
+        launchBookmarksActivityFab.setOnClickListener(v -> {
+            // Create an intent to launch the bookmarks activity.
+            Intent bookmarksIntent = new Intent(getApplicationContext(), BookmarksActivity.class);
 
-                // Include the current folder with the `Intent`.
-                bookmarksIntent.putExtra("Current Folder", currentBookmarksFolder);
+            // Include the current folder with the `Intent`.
+            bookmarksIntent.putExtra("Current Folder", currentBookmarksFolder);
 
-                // Make it so.
-                startActivity(bookmarksIntent);
-            }
+            // Make it so.
+            startActivity(bookmarksIntent);
         });
 
         // Set the create new bookmark folder FAB to display the `AlertDialog`.
-        createBookmarkFolderFab.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Show the `CreateBookmarkFolderDialog` `AlertDialog` and name the instance `@string/create_folder`.
-                AppCompatDialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog();
-                createBookmarkFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_folder));
-            }
+        createBookmarkFolderFab.setOnClickListener(v -> {
+            // Show the `CreateBookmarkFolderDialog` `AlertDialog` and name the instance `@string/create_folder`.
+            AppCompatDialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog();
+            createBookmarkFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.create_folder));
         });
 
         // Set the create new bookmark FAB to display the `AlertDialog`.
-        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));
-            }
+        createBookmarkFab.setOnClickListener(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));
         });
 
         // Create a double-tap listener to toggle full-screen mode.
@@ -623,12 +606,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         });
 
         // Pass all touch events on `mainWebView` through `gestureDetector` to check for double-taps.
-        mainWebView.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                // Send the `event` to `gestureDetector`.
-                return gestureDetector.onTouchEvent(event);
-            }
+        mainWebView.setOnTouchListener((View v, MotionEvent event) -> {
+            // Call `performClick()` on the view, which is required for accessibility.
+            v.performClick();
+
+            // Send the `event` to `gestureDetector`.
+            return gestureDetector.onTouchEvent(event);
         });
 
         // Update `findOnPageCountTextView`.
@@ -645,8 +628,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // `activeMatchOrdinal` is zero-based.
                     int activeMatch = activeMatchOrdinal + 1;
 
+                    // Build the match string.
+                    String matchString = activeMatch + "/" + numberOfMatches;
+
                     // Set `findOnPageCountTextView`.
-                    findOnPageCountTextView.setText(activeMatch + "/" + numberOfMatches);
+                    findOnPageCountTextView.setText(matchString);
                 }
             }
         });
@@ -671,38 +657,30 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         });
 
         // Set the `check mark` button for the `findOnPageEditText` keyboard to close the soft keyboard.
-        findOnPageEditText.setOnKeyListener(new View.OnKeyListener() {
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {  // The `enter` key was pressed.
-                    // Hide the soft keyboard.  `0` indicates no additional flags.
-                    inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
+        findOnPageEditText.setOnKeyListener((v, keyCode, event) -> {
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {  // The `enter` key was pressed.
+                // Hide the soft keyboard.  `0` indicates no additional flags.
+                inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
 
-                    // Consume the event.
-                    return true;
-                } else {  // A different key was pressed.
-                    // Do not consume the event.
-                    return false;
-                }
+                // Consume the event.
+                return true;
+            } else {  // A different key was pressed.
+                // Do not consume the event.
+                return false;
             }
         });
 
         // Implement swipe to refresh
-        swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refreshlayout);
+        swipeRefreshLayout = findViewById(R.id.swipe_refreshlayout);
         swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
-        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
-            @Override
-            public void onRefresh() {
-                mainWebView.reload();
-            }
-        });
+        swipeRefreshLayout.setOnRefreshListener(() -> mainWebView.reload());
 
         // `DrawerTitle` identifies the `DrawerLayouts` in accessibility mode.
         drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer));
         drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks));
 
         // Listen for touches on the navigation menu.
-        final NavigationView navigationView = (NavigationView) findViewById(R.id.navigationview);
+        final NavigationView navigationView = findViewById(R.id.navigationview);
         navigationView.setNavigationItemSelectedListener(this);
 
         // Get handles for `navigationMenu` and the back and forward menu items.  The menu is zero-based, so items 1, 2, and 3 are the second, third, and fourth entries in the menu.
@@ -721,61 +699,55 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Load the home folder, which is `""` in the database.
         loadBookmarksFolder();
 
-        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();
-
-                // Act upon the bookmark according to the type.
-                if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {  // The selected bookmark is a folder.
-                    // Store the new folder name in `currentBookmarksFolder`.
-                    currentBookmarksFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
-
-                    // Load the new folder.
-                    loadBookmarksFolder();
-                } else {  // The selected bookmark is not a folder.
-                    // Load the bookmark URL.
-                    loadUrl(bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)));
-
-                    // Close the bookmarks drawer.
-                    drawerLayout.closeDrawer(GravityCompat.END);
-                }
+        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;
+
+            // Get the bookmark `Cursor` for this ID and move it to the first row.
+            Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmarkCursor(databaseID);
+            bookmarkCursor.moveToFirst();
 
-                // Close the `Cursor`.
-                bookmarkCursor.close();
+            // Act upon the bookmark according to the type.
+            if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {  // The selected bookmark is a folder.
+                // Store the new folder name in `currentBookmarksFolder`.
+                currentBookmarksFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+
+                // Load the new folder.
+                loadBookmarksFolder();
+            } else {  // The selected bookmark is not a folder.
+                // Load the bookmark URL.
+                loadUrl(bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)));
+
+                // Close the bookmarks drawer.
+                drawerLayout.closeDrawer(GravityCompat.END);
             }
-        });
 
-        bookmarksListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
-            @Override
-            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
-                // Convert the database ID from `long` to `int`.
-                int databaseId = (int) id;
+            // Close the `Cursor`.
+            bookmarkCursor.close();
+        });
 
-                // Find out if the selected bookmark is a folder.
-                boolean isFolder = bookmarksDatabaseHelper.isFolder(databaseId);
+        bookmarksListView.setOnItemLongClickListener((parent, view, position, id) -> {
+            // Convert the database ID from `long` to `int`.
+            int databaseId = (int) id;
 
-                if (isFolder) {
-                    // Save the current folder name, which is used in `onSaveEditBookmarkFolder()`.
-                    oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+            // Find out if the selected bookmark is a folder.
+            boolean isFolder = bookmarksDatabaseHelper.isFolder(databaseId);
 
-                    // Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`.
-                    AppCompatDialogFragment editFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId);
-                    editFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_folder));
-                } else {
-                    // Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`.
-                    AppCompatDialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId);
-                    editBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark));
-                }
+            if (isFolder) {
+                // Save the current folder name, which is used in `onSaveEditBookmarkFolder()`.
+                oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
 
-                // Consume the event.
-                return true;
+                // Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`.
+                AppCompatDialogFragment editFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId);
+                editFolderDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_folder));
+            } else {
+                // Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`.
+                AppCompatDialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId);
+                editBookmarkDialog.show(getSupportFragmentManager(), getResources().getString(R.string.edit_bookmark));
             }
+
+            // Consume the event.
+            return true;
         });
 
         // The `DrawerListener` allows us to update the Navigation Menu.
@@ -1099,7 +1071,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         });
 
         // Get a handle for the progress bar.
-        final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
+        final ProgressBar progressBar = findViewById(R.id.progress_bar);
 
         mainWebView.setWebChromeClient(new WebChromeClient() {
             // Update the progress bar when a page is loading.
@@ -1113,26 +1085,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     mainWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = '" +
                             "* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important; text-shadow: none !important; border: none !important;}" +
                             "a {color: #1565C0 !important;}" +
-                            "'; parent.appendChild(style)})()", new ValueCallback<String>() {
-                        @Override
-                        public void onReceiveValue(String value) {
-                            // Initialize a `Handler` to display `mainWebView`.
-                            Handler displayWebViewHandler = new Handler();
-
-                            // Setup a `Runnable` to display `mainWebView` after a delay to allow the CSS to be applied.
-                            Runnable displayWebViewRunnable = new Runnable() {
-                                public void run() {
+                            "'; parent.appendChild(style)})()", value -> {
+                                // Initialize a `Handler` to display `mainWebView`.
+                                Handler displayWebViewHandler = new Handler();
+
+                                // Setup a `Runnable` to display `mainWebView` after a delay to allow the CSS to be applied.
+                                Runnable displayWebViewRunnable = () -> {
                                     // Only display `mainWebView` if the progress bar is one.  This prevents the display of the `WebView` while it is still loading.
                                     if (progressBar.getVisibility() == View.GONE) {
                                         mainWebView.setVisibility(View.VISIBLE);
                                     }
-                                }
-                            };
+                                };
 
-                            // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 500 milliseconds.
-                            displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
-                        }
-                    });
+                                // Use `displayWebViewHandler` to delay the displaying of `mainWebView` for 500 milliseconds.
+                                displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
+                            });
                 }
 
                 progressBar.setProgress(progress);
@@ -1229,13 +1196,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         registerForContextMenu(mainWebView);
 
         // Allow the downloading of files.
-        mainWebView.setDownloadListener(new DownloadListener() {
-            @Override
-            public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
-                // Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
-                AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
-                downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
-            }
+        mainWebView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
+            // Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`.
+            AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
+            downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
         });
 
         // Allow pinch to zoom.
@@ -1702,11 +1666,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.clear_cookies:
                 Snackbar.make(findViewById(R.id.main_webview), R.string.cookies_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, new View.OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                // Do nothing because everything will be handled by `onDismissed()` below.
-                            }
+                        .setAction(R.string.undo, v -> {
+                            // Do nothing because everything will be handled by `onDismissed()` below.
                         })
                         .addCallback(new Snackbar.Callback() {
                             @Override
@@ -1734,11 +1695,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.clear_dom_storage:
                 Snackbar.make(findViewById(R.id.main_webview), R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, new View.OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                // Do nothing because everything will be handled by `onDismissed()` below.
-                            }
+                        .setAction(R.string.undo, v -> {
+                            // Do nothing because everything will be handled by `onDismissed()` below.
                         })
                         .addCallback(new Snackbar.Callback() {
                             @Override
@@ -1769,11 +1727,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.clear_form_data:
                 Snackbar.make(findViewById(R.id.main_webview), R.string.form_data_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, new View.OnClickListener() {
-                            @Override
-                            public void onClick(View v) {
-                                // Do nothing because everything will be handled by `onDismissed()` below.
-                            }
+                        .setAction(R.string.undo, v -> {
+                            // Do nothing because everything will be handled by `onDismissed()` below.
                         })
                         .addCallback(new Snackbar.Callback() {
                             @Override
@@ -1861,16 +1816,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 findOnPageLinearLayout.setVisibility(View.VISIBLE);
 
                 // Display the keyboard.  We have to wait 200 ms before running the command to work around a bug in Android.  http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
-                findOnPageEditText.postDelayed(new Runnable() {
-                    @Override
-                    public void run()
-                    {
-                        // Set the focus on `findOnPageEditText`.
-                        findOnPageEditText.requestFocus();
-
-                        // Display the keyboard.  `0` sets no input flags.
-                        inputMethodManager.showSoftInput(findOnPageEditText, 0);
-                    }
+                findOnPageEditText.postDelayed(() -> {
+                    // Set the focus on `findOnPageEditText`.
+                    findOnPageEditText.requestFocus();
+
+                    // Display the keyboard.  `0` sets no input flags.
+                    inputMethodManager.showSoftInput(findOnPageEditText, 0);
                 }, 200);
                 return true;
 
@@ -1881,6 +1832,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Convert `mainWebView` to `printDocumentAdapter`.
                 PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter();
 
+                // Remove the lint error below that `printManager` might be `null`.
+                assert printManager != null;
+
                 // Print the document.  The print attributes are `null`.
                 printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
                 return true;
@@ -2143,6 +2097,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Get a handle for the `ClipboardManager`.
         final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
 
+        // Remove the lint errors below that `clipboardManager` might be `null`.
+        assert clipboardManager != null;
+
         switch (hitTestResult.getType()) {
             // `SRC_ANCHOR_TYPE` is a link.
             case WebView.HitTestResult.SRC_ANCHOR_TYPE:
@@ -2153,25 +2110,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 menu.setHeaderTitle(linkUrl);
 
                 // Add a `Load URL` entry.
-                menu.add(R.string.load_url).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        loadUrl(linkUrl);
-                        return false;
-                    }
+                menu.add(R.string.load_url).setOnMenuItemClickListener(item -> {
+                    loadUrl(linkUrl);
+                    return false;
                 });
 
                 // Add a `Copy URL` entry.
-                menu.add(R.string.copy_url).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        // Save the link URL in a `ClipData`.
-                        ClipData srcAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), linkUrl);
-
-                        // Set the `ClipData` as the clipboard's primary clip.
-                        clipboardManager.setPrimaryClip(srcAnchorTypeClipData);
-                        return false;
-                    }
+                menu.add(R.string.copy_url).setOnMenuItemClickListener(item -> {
+                    // Save the link URL in a `ClipData`.
+                    ClipData srcAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), linkUrl);
+
+                    // Set the `ClipData` as the clipboard's primary clip.
+                    clipboardManager.setPrimaryClip(srcAnchorTypeClipData);
+                    return false;
                 });
 
                 // Add a `Cancel` entry, which by default closes the `ContextMenu`.
@@ -2186,35 +2137,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 menu.setHeaderTitle(linkUrl);
 
                 // Add a `Write Email` entry.
-                menu.add(R.string.write_email).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        // We use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched.
-                        Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
+                menu.add(R.string.write_email).setOnMenuItemClickListener(item -> {
+                    // We use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched.
+                    Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
 
-                        // Parse the url and set it as the data for the `Intent`.
-                        emailIntent.setData(Uri.parse("mailto:" + linkUrl));
+                    // Parse the url and set it as the data for the `Intent`.
+                    emailIntent.setData(Uri.parse("mailto:" + linkUrl));
 
-                        // `FLAG_ACTIVITY_NEW_TASK` opens the email program in a new task instead as part of Privacy Browser.
-                        emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    // `FLAG_ACTIVITY_NEW_TASK` opens the email program in a new task instead as part of Privacy Browser.
+                    emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                        // Make it so.
-                        startActivity(emailIntent);
-                        return false;
-                    }
+                    // Make it so.
+                    startActivity(emailIntent);
+                    return false;
                 });
 
                 // Add a `Copy Email Address` entry.
-                menu.add(R.string.copy_email_address).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        // Save the email address in a `ClipData`.
-                        ClipData srcEmailTypeClipData = ClipData.newPlainText(getString(R.string.email_address), linkUrl);
-
-                        // Set the `ClipData` as the clipboard's primary clip.
-                        clipboardManager.setPrimaryClip(srcEmailTypeClipData);
-                        return false;
-                    }
+                menu.add(R.string.copy_email_address).setOnMenuItemClickListener(item -> {
+                    // Save the email address in a `ClipData`.
+                    ClipData srcEmailTypeClipData = ClipData.newPlainText(getString(R.string.email_address), linkUrl);
+
+                    // Set the `ClipData` as the clipboard's primary clip.
+                    clipboardManager.setPrimaryClip(srcEmailTypeClipData);
+                    return false;
                 });
 
                 // Add a `Cancel` entry, which by default closes the `ContextMenu`.
@@ -2230,36 +2175,27 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 menu.setHeaderTitle(imageUrl);
 
                 // Add a `View Image` entry.
-                menu.add(R.string.view_image).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        loadUrl(imageUrl);
-                        return false;
-                    }
+                menu.add(R.string.view_image).setOnMenuItemClickListener(item -> {
+                    loadUrl(imageUrl);
+                    return false;
                 });
 
                 // Add a `Download Image` entry.
-                menu.add(R.string.download_image).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
-                        AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
-                        downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
-                        return false;
-                    }
+                menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+                    // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
+                    AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
+                    downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
+                    return false;
                 });
 
                 // Add a `Copy URL` entry.
-                menu.add(R.string.copy_url).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        // Save the image URL in a `ClipData`.
-                        ClipData srcImageTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
-
-                        // Set the `ClipData` as the clipboard's primary clip.
-                        clipboardManager.setPrimaryClip(srcImageTypeClipData);
-                        return false;
-                    }
+                menu.add(R.string.copy_url).setOnMenuItemClickListener(item -> {
+                    // Save the image URL in a `ClipData`.
+                    ClipData srcImageTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
+
+                    // Set the `ClipData` as the clipboard's primary clip.
+                    clipboardManager.setPrimaryClip(srcImageTypeClipData);
+                    return false;
                 });
 
                 // Add a `Cancel` entry, which by default closes the `ContextMenu`.
@@ -2276,36 +2212,27 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 menu.setHeaderTitle(imageUrl);
 
                 // Add a `View Image` entry.
-                menu.add(R.string.view_image).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        loadUrl(imageUrl);
-                        return false;
-                    }
+                menu.add(R.string.view_image).setOnMenuItemClickListener(item -> {
+                    loadUrl(imageUrl);
+                    return false;
                 });
 
                 // Add a `Download Image` entry.
-                menu.add(R.string.download_image).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
-                        AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
-                        downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
-                        return false;
-                    }
+                menu.add(R.string.download_image).setOnMenuItemClickListener(item -> {
+                    // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`.
+                    AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl);
+                    downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
+                    return false;
                 });
 
                 // Add a `Copy URL` entry.
-                menu.add(R.string.copy_url).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    @Override
-                    public boolean onMenuItemClick(MenuItem item) {
-                        // Save the image URL in a `ClipData`.
-                        ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
-
-                        // Set the `ClipData` as the clipboard's primary clip.
-                        clipboardManager.setPrimaryClip(srcImageAnchorTypeClipData);
-                        return false;
-                    }
+                menu.add(R.string.copy_url).setOnMenuItemClickListener(item -> {
+                    // Save the image URL in a `ClipData`.
+                    ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl);
+
+                    // Set the `ClipData` as the clipboard's primary clip.
+                    clipboardManager.setPrimaryClip(srcImageAnchorTypeClipData);
+                    return false;
                 });
 
                 // Add a `Cancel` entry, which by default closes the `ContextMenu`.
@@ -2317,8 +2244,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     @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);
+        EditText createBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext);
+        EditText createBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext);
 
         // Extract the strings from the `EditTexts`.
         String bookmarkNameString = createBookmarkNameEditText.getText().toString();
@@ -2333,7 +2260,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         int newBookmarkDisplayOrder = bookmarksListView.getCount();
 
         // Create the bookmark.
-        bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, newBookmarkDisplayOrder, currentBookmarksFolder, favoriteIconByteArray);
+        bookmarksDatabaseHelper.createBookmark(bookmarkNameString, bookmarkUrlString, currentBookmarksFolder, newBookmarkDisplayOrder, favoriteIconByteArray);
 
         // Update `bookmarksCursor` with the current contents of this folder.
         bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(currentBookmarksFolder);
@@ -2348,9 +2275,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     @Override
     public void onCreateBookmarkFolder(AppCompatDialogFragment dialogFragment) {
         // Get handles for the views in `dialogFragment`.
-        EditText createFolderNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext);
-        RadioButton defaultFolderIconRadioButton = (RadioButton) dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton);
-        ImageView folderIconImageView = (ImageView) dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon);
+        EditText createFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext);
+        RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton);
+        ImageView folderIconImageView = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon);
 
         // Get new folder name string.
         String folderNameString = createFolderNameEditText.getText().toString();
@@ -2377,8 +2304,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             bookmarksDatabaseHelper.updateDisplayOrder(databaseId, i + 1);
         }
 
-        // Create the folder, placing it at the top of the ListView
-        bookmarksDatabaseHelper.createFolder(folderNameString, 0, currentBookmarksFolder, folderIconByteArray);
+        // Create the folder, which will be placed at the top of the `ListView`.
+        bookmarksDatabaseHelper.createFolder(folderNameString, currentBookmarksFolder, folderIconByteArray);
 
         // Update `bookmarksCursor` with the current contents of this folder.
         bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(currentBookmarksFolder);
@@ -2393,7 +2320,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     @Override
     public void onCreateHomeScreenShortcut(AppCompatDialogFragment dialogFragment) {
         // Get shortcutNameEditText from the alert dialog.
-        EditText shortcutNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext);
+        EditText shortcutNameEditText = dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext);
 
         // Create the bookmark shortcut based on formattedUrlString.
         Intent bookmarkShortcut = new Intent();
@@ -2430,7 +2357,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
             // Get the file name from `dialogFragment`.
-            EditText downloadImageNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.download_image_name);
+            EditText downloadImageNameEditText = dialogFragment.getDialog().findViewById(R.id.download_image_name);
             String imageName = downloadImageNameEditText.getText().toString();
 
             // Once we have `WRITE_EXTERNAL_STORAGE` permissions we can use `setDestinationInExternalPublicDir`.
@@ -2449,6 +2376,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Show the download notification after the download is completed.
             downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
 
+            // Remove the lint warning below that `downloadManager` might be `null`.
+            assert downloadManager != null;
+
             // Initiate the download.
             downloadManager.enqueue(downloadRequest);
         } else {  // The image is not an HTTP or HTTPS URI.
@@ -2478,7 +2408,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
             // Get the file name from `dialogFragment`.
-            EditText downloadFileNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.download_file_name);
+            EditText downloadFileNameEditText = dialogFragment.getDialog().findViewById(R.id.download_file_name);
             String fileName = downloadFileNameEditText.getText().toString();
 
             // Once we have `WRITE_EXTERNAL_STORAGE` permissions we can use `setDestinationInExternalPublicDir`.
@@ -2497,6 +2427,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Show the download notification after the download is completed.
             downloadRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
 
+            // Remove the lint warning below that `downloadManager` might be `null`.
+            assert downloadManager != null;
+
             // Initiate the download.
             downloadManager.enqueue(downloadRequest);
         } else {  // The download is not an HTTP or HTTPS URI.
@@ -2505,11 +2438,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void onSaveEditBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId) {
+    public void onSaveBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId) {
         // Get handles for the views from `dialogFragment`.
-        EditText editBookmarkNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext);
-        EditText editBookmarkUrlEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext);
-        RadioButton currentBookmarkIconRadioButton = (RadioButton) dialogFragment.getDialog().findViewById(R.id.edit_bookmark_current_icon_radiobutton);
+        EditText editBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext);
+        EditText editBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext);
+        RadioButton currentBookmarkIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_current_icon_radiobutton);
 
         // Store the bookmark strings.
         String bookmarkNameString = editBookmarkNameEditText.getText().toString();
@@ -2536,12 +2469,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void onSaveEditBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId) {
+    public void onSaveBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId) {
         // Get handles for the views from `dialogFragment`.
-        EditText editFolderNameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext);
-        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);
-        ImageView folderIconImageView = (ImageView) dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon);
+        EditText editFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext);
+        RadioButton currentFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_current_icon_radiobutton);
+        RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_radiobutton);
+        ImageView folderIconImageView = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_imageview);
 
         // Get the new folder name.
         String newFolderNameString = editFolderNameEditText.getText().toString();
@@ -2606,8 +2539,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     @Override
     public void onHttpAuthenticationProceed(AppCompatDialogFragment dialogFragment) {
         // Get handles for the `EditTexts`.
-        EditText usernameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.http_authentication_username);
-        EditText passwordEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.http_authentication_password);
+        EditText usernameEditText = dialogFragment.getDialog().findViewById(R.id.http_authentication_username);
+        EditText passwordEditText = dialogFragment.getDialog().findViewById(R.id.http_authentication_password);
 
         // Proceed with the HTTP authentication.
         httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());
@@ -3332,8 +3265,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             @Override
             public void bindView(View view, Context context, Cursor cursor) {
                 // Get handles for the views.
-                ImageView bookmarkFavoriteIcon = (ImageView) view.findViewById(R.id.bookmark_favorite_icon);
-                TextView bookmarkNameTextView = (TextView) view.findViewById(R.id.bookmark_name);
+                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));
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.java
new file mode 100644 (file)
index 0000000..d75cf6c
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser 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,
+ * 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/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MergeCursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+// `AppCompatDialogFragment` is required instead of `DialogFragment` or an error is produced on API <=22.
+import android.support.v4.widget.ResourceCursorAdapter;
+import android.support.v7.app.AppCompatDialogFragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
+
+public class EditBookmarkDatabaseViewDialog extends AppCompatDialogFragment {
+    // Instantiate the constants.
+    public static final int HOME_FOLDER_DATABASE_ID = -1;
+
+    // Instantiate the class variables.
+    private EditBookmarkDatabaseViewListener editBookmarkDatabaseViewListener;
+    private int bookmarkDatabaseId;
+    private String currentBookmarkName;
+    private String currentUrl;
+    private int currentFolderDatabaseId;
+    private String currentDisplayOrder;
+    private RadioButton newIconRadioButton;
+    private EditText nameEditText;
+    private EditText urlEditText;
+    private Spinner folderSpinner;
+    private EditText displayOrderEditText;
+    private Button editButton;
+
+    // The public interface is used to send information back to the parent activity.
+    public interface EditBookmarkDatabaseViewListener {
+        void onSaveBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId);
+    }
+
+    public void onAttach(Context context) {
+        // Run the default commands.
+        super.onAttach(context);
+
+        // Get a handle for `EditBookmarkDatabaseViewListener` from `context`.
+        try {
+            editBookmarkDatabaseViewListener = (EditBookmarkDatabaseViewListener) context;
+        } catch(ClassCastException exception) {
+            throw new ClassCastException(context.toString() + " must implement EditBookmarkDatabaseViewListener.");
+        }
+    }
+
+    // Store the database ID in the arguments bundle.
+    public static EditBookmarkDatabaseViewDialog bookmarkDatabaseId(int databaseId) {
+        // Create a bundle.
+        Bundle bundle = new Bundle();
+
+        // Store the bookmark database ID in the bundle.
+        bundle.putInt("Database ID", databaseId);
+
+        // Add the bundle to the dialog.
+        EditBookmarkDatabaseViewDialog editBookmarkDatabaseViewDialog = new EditBookmarkDatabaseViewDialog();
+        editBookmarkDatabaseViewDialog.setArguments(bundle);
+
+        // Return the new dialog.
+        return editBookmarkDatabaseViewDialog;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        // Run the default commands.
+        super.onCreate(savedInstanceState);
+
+        // Store the bookmark database ID in the class variable.
+        bookmarkDatabaseId = getArguments().getInt("Database ID");
+    }
+
+    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    @SuppressLint("InflateParams")
+    @Override
+    @NonNull
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Initialize the database helper.  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 bookmarksDatabaseHelper = new BookmarksDatabaseHelper(getContext(), null, null, 0);
+
+        // Get a `Cursor` with the selected bookmark and move it to the first position.
+        Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmarkCursor(bookmarkDatabaseId);
+        bookmarkCursor.moveToFirst();
+
+        // Use `AlertDialog.Builder` to create the `AlertDialog`.
+        AlertDialog.Builder dialogBuilder;
+
+        // Set the style according to the theme.
+        if (MainWebViewActivity.darkTheme) {
+            dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
+        } else {
+            dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
+        }
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.edit_bookmark);
+
+        // Set the view.  The parent view is `null` because it will be assigned by `AlertDialog`.
+        dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.edit_bookmark_databaseview_dialog, null));
+
+        // Set an `onClick()` listener for the negative button.
+        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
+            // Do nothing.  The `AlertDialog` will close automatically.
+        });
+
+        // Set the `onClick()` listener fo the positive button.
+        dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
+            // Return the `DialogFragment` to the parent activity on save.
+            editBookmarkDatabaseViewListener.onSaveBookmark(EditBookmarkDatabaseViewDialog.this, bookmarkDatabaseId);
+        });
+
+        // Create an `AlertDialog` from the `AlertDialog.Builder`.
+        final AlertDialog alertDialog = dialogBuilder.create();
+
+        // Remove the warning below that `setSoftInputMode` might produce `java.lang.NullPointerException`.
+        assert alertDialog.getWindow() != null;
+
+        // Set the keyboard to be hidden when the `AlertDialog` is first shown.  If this is not set, the `AlertDialog` will not shrink when the keyboard is displayed.
+        alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
+
+        // The `AlertDialog` must be shown before items in the layout can be modified.
+        alertDialog.show();
+
+        // Get handles for the layout items.
+        TextView databaseIdTextView = alertDialog.findViewById(R.id.edit_bookmark_database_id_textview);
+        RadioGroup iconRadioGroup = alertDialog.findViewById(R.id.edit_bookmark_icon_radiogroup);
+        ImageView currentIconImageView = alertDialog.findViewById(R.id.edit_bookmark_current_icon);
+        ImageView newFavoriteIconImageView = alertDialog.findViewById(R.id.edit_bookmark_webpage_favorite_icon);
+        newIconRadioButton = alertDialog.findViewById(R.id.edit_bookmark_webpage_favorite_icon_radiobutton);
+        nameEditText = alertDialog.findViewById(R.id.edit_bookmark_name_edittext);
+        urlEditText = alertDialog.findViewById(R.id.edit_bookmark_url_edittext);
+        folderSpinner = alertDialog.findViewById(R.id.edit_bookmark_folder_spinner);
+        displayOrderEditText = alertDialog.findViewById(R.id.edit_bookmark_display_order_edittext);
+        editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+
+        // Store the current bookmark values.
+        currentBookmarkName = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+        currentUrl = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL));
+        currentDisplayOrder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER));
+
+        // Set the database ID.
+        databaseIdTextView.setText(String.valueOf(bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper._ID))));
+
+        // Get the current favorite icon byte array from the `Cursor`.
+        byte[] currentIconByteArray = bookmarkCursor.getBlob(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON));
+
+        // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
+        Bitmap currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.length);
+
+        // Display `currentIconBitmap` in `edit_bookmark_current_icon`.
+        currentIconImageView.setImageBitmap(currentIconBitmap);
+
+        // Get a `Bitmap` of the favorite icon from `MainWebViewActivity` and display it in `edit_bookmark_web_page_favorite_icon`.
+        newFavoriteIconImageView.setImageBitmap(MainWebViewActivity.favoriteIconBitmap);
+
+        // Populate the bookmark name and URL `EditTexts`.
+        nameEditText.setText(currentBookmarkName);
+        urlEditText.setText(currentUrl);
+
+        // Setup a `MatrixCursor` "Home Folder".
+        String[] matrixCursorColumnNames = {BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME};
+        MatrixCursor matrixCursor = new MatrixCursor(matrixCursorColumnNames);
+        matrixCursor.addRow(new Object[]{HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)});
+
+        // Get a `Cursor` with the list of all the folders.
+        Cursor foldersCursor = bookmarksDatabaseHelper.getAllFoldersCursor();
+
+        // Combine `matrixCursor` and `foldersCursor`.
+        MergeCursor foldersMergeCursor = new MergeCursor(new Cursor[]{matrixCursor, foldersCursor});
+
+        // Create a `ResourceCursorAdapter` for the `Spinner`.  `0` specifies no flags.;
+        ResourceCursorAdapter foldersCursorAdapter = new ResourceCursorAdapter(getContext(), R.layout.edit_bookmark_databaseview_spinner_item, foldersMergeCursor, 0) {
+            @Override
+            public void bindView(View view, Context context, Cursor cursor) {
+                // Get a handle for the `Spinner` item `TextView`.
+                TextView spinnerItemTextView = view.findViewById(R.id.spinner_item_textview);
+
+                // Set the `TextView` to display the folder name.
+                spinnerItemTextView.setText(cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)));
+            }
+        };
+
+        // Set the `ResourceCursorAdapter` drop drown view resource.
+        foldersCursorAdapter.setDropDownViewResource(R.layout.edit_bookmark_databaseview_spinner_dropdown_item);
+
+        // Set the adapter for the folder `Spinner`.
+        folderSpinner.setAdapter(foldersCursorAdapter);
+
+        // Get the parent folder name.
+        String parentFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER));
+
+        // Select the current folder in the `Spinner` if the bookmark isn't in the "Home Folder".
+        if (!parentFolder.equals("")) {
+            // Get the database ID of the parent folder.
+            int folderDatabaseId = bookmarksDatabaseHelper.getFolderDatabaseId(bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER)));
+
+            // Initialize `parentFolderPosition` and the iteration variable.
+            int parentFolderPosition = 0;
+            int i = 0;
+
+            // Find the parent folder position in folders `ResourceCursorAdapter`.
+            do {
+                if (foldersCursorAdapter.getItemId(i) == folderDatabaseId) {
+                    // Store the current position for the parent folder.
+                    parentFolderPosition = i;
+                } else {
+                    // Try the next entry.
+                    i++;
+                }
+                // Stop when the parent folder position is found or all the items in the `ResourceCursorAdapter` have been checked.
+            } while ((parentFolderPosition == 0) && (i < foldersCursorAdapter.getCount()));
+
+            // Select the parent folder in the `Spinner`.
+            folderSpinner.setSelection(parentFolderPosition);
+        }
+
+        // Store the current folder database ID.
+        currentFolderDatabaseId = (int) folderSpinner.getSelectedItemId();
+
+        // Populate the display order `EditText`.
+        displayOrderEditText.setText(String.valueOf(bookmarkCursor.getInt(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER))));
+
+        // Initially disable the edit button.
+        editButton.setEnabled(false);
+
+        // Update the edit button if the icon selection changes.
+        iconRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+            // Update the edit button.
+            updateEditButton();
+        });
+
+        // Update the edit button if the bookmark name changes.
+        nameEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                // Do nothing.
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Update the edit button.
+                updateEditButton();
+            }
+        });
+
+        // Update the edit button if the URL changes.
+        urlEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                // Do nothing.
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Update the edit button.
+                updateEditButton();
+            }
+        });
+
+        // Update the edit button if the folder changes.
+        folderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                // Update the edit button.
+                updateEditButton();
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+
+            }
+        });
+
+        // Update the edit button if the display order changes.
+        displayOrderEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                // Do nothing.
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Update the edit button.
+                updateEditButton();
+            }
+        });
+
+        // Allow the `enter` key on the keyboard to save the bookmark from the bookmark name `EditText`.
+        nameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // Save the bookmark if the event is a key-down on the "enter" button.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
+                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
+                editBookmarkDatabaseViewListener.onSaveBookmark(EditBookmarkDatabaseViewDialog.this, bookmarkDatabaseId);
+
+                // Manually dismiss `alertDialog`.
+                alertDialog.dismiss();
+
+                // Consume the event.
+                return true;
+            } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+                return false;
+            }
+        });
+
+        // Allow the "enter" key on the keyboard to save the bookmark from the URL `EditText`.
+        urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // Save the bookmark if the event is a key-down on the "enter" button.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
+                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
+                editBookmarkDatabaseViewListener.onSaveBookmark(EditBookmarkDatabaseViewDialog.this, bookmarkDatabaseId);
+
+                // Manually dismiss the `AlertDialog`.
+                alertDialog.dismiss();
+
+                // Consume the event.
+                return true;
+            } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+                return false;
+            }
+        });
+
+        // Allow the "enter" key on the keyboard to save the bookmark from the display order `EditText`.
+        displayOrderEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // Save the bookmark if the event is a key-down on the "enter" button.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
+                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
+                editBookmarkDatabaseViewListener.onSaveBookmark(EditBookmarkDatabaseViewDialog.this, bookmarkDatabaseId);
+
+                // Manually dismiss the `AlertDialog`.
+                alertDialog.dismiss();
+
+                // Consume the event.
+                return true;
+            } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+                return false;
+            }
+        });
+
+        // `onCreateDialog` requires the return of an `AlertDialog`.
+        return alertDialog;
+    }
+
+    private void updateEditButton() {
+        // Get the values from the dialog.
+        String newName = nameEditText.getText().toString();
+        String newUrl = urlEditText.getText().toString();
+        int newFolderDatabaseId = (int) folderSpinner.getSelectedItemId();
+        String newDisplayOrder = displayOrderEditText.getText().toString();
+
+        // Has the favorite icon changed?
+        boolean iconChanged = newIconRadioButton.isChecked();
+
+        // Has the name changed?
+        boolean nameChanged = !newName.equals(currentBookmarkName);
+
+        // Has the URL changed?
+        boolean urlChanged = !newUrl.equals(currentUrl);
+
+        // Has the folder changed?
+        boolean folderChanged = newFolderDatabaseId != currentFolderDatabaseId;
+
+        // Has the display order changed?
+        boolean displayOrderChanged = !newDisplayOrder.equals(currentDisplayOrder);
+
+        // Is the display order empty?
+        boolean displayOrderNotEmpty = !newDisplayOrder.isEmpty();
+
+        // Update the enabled status of the edit button.
+        editButton.setEnabled((iconChanged || nameChanged || urlChanged || folderChanged || displayOrderChanged) && displayOrderNotEmpty);
+    }
+}
\ No newline at end of file
index a25864ba048165f1fef5715637843732a7cfe5d7..e37dacdf55de130b1b9c9e8ec2586adcf0fe0bf2 100644 (file)
@@ -28,7 +28,6 @@ import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Bundle;
-import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
 // `AppCompatDialogFragment` is required instead of `DialogFragment` or an error is produced on API <=22.
 import android.support.v7.app.AppCompatDialogFragment;
@@ -48,11 +47,6 @@ import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
 
 public class EditBookmarkDialog extends AppCompatDialogFragment {
-    // The public interface is used to send information back to the parent activity.
-    public interface EditBookmarkListener {
-        void onSaveEditBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId);
-    }
-
     // Instantiate the class variables.
     private EditBookmarkListener editBookmarkListener;
     private int selectedBookmarkDatabaseId;
@@ -63,6 +57,11 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
     private String currentName;
     private String currentUrl;
 
+    // The public interface is used to send information back to the parent activity.
+    public interface EditBookmarkListener {
+        void onSaveBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId);
+    }
+
     public void onAttach(Context context) {
         // Run the default commands.
         super.onAttach(context);
@@ -129,20 +128,14 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
         dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.edit_bookmark_dialog, null));
 
         // Set an `onClick()` listener for the negative button.
-        dialogBuilder.setNegativeButton(R.string.cancel, new Dialog.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                // Do nothing.  The `AlertDialog` will close automatically.
-            }
+        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
+            // Do nothing.  The `AlertDialog` will close automatically.
         });
 
         // Set the `onClick()` listener fo the positive button.
-        dialogBuilder.setPositiveButton(R.string.save, new Dialog.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                // Return the `DialogFragment` to the parent activity on save.
-                editBookmarkListener.onSaveEditBookmark(EditBookmarkDialog.this, selectedBookmarkDatabaseId);
-            }
+        dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
+            // Return the `DialogFragment` to the parent activity on save.
+            editBookmarkListener.onSaveBookmark(EditBookmarkDialog.this, selectedBookmarkDatabaseId);
         });
 
         // Create an `AlertDialog` from the `AlertDialog.Builder`.
@@ -158,12 +151,12 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
         alertDialog.show();
 
         // Get handles for the layout items.
-        RadioGroup iconRadioGroup = (RadioGroup) alertDialog.findViewById(R.id.edit_bookmark_icon_radiogroup);
-        ImageView currentIconImageView = (ImageView) alertDialog.findViewById(R.id.edit_bookmark_current_icon);
-        ImageView newFavoriteIconImageView = (ImageView) alertDialog.findViewById(R.id.edit_bookmark_web_page_favorite_icon);
-        newIconRadioButton = (RadioButton) alertDialog.findViewById(R.id.edit_bookmark_web_page_favorite_icon_radiobutton);
-        nameEditText = (EditText) alertDialog.findViewById(R.id.edit_bookmark_name_edittext);
-        urlEditText = (EditText) alertDialog.findViewById(R.id.edit_bookmark_url_edittext);
+        RadioGroup iconRadioGroup = alertDialog.findViewById(R.id.edit_bookmark_icon_radiogroup);
+        ImageView currentIconImageView = alertDialog.findViewById(R.id.edit_bookmark_current_icon);
+        ImageView newFavoriteIconImageView = alertDialog.findViewById(R.id.edit_bookmark_webpage_favorite_icon);
+        newIconRadioButton = alertDialog.findViewById(R.id.edit_bookmark_webpage_favorite_icon_radiobutton);
+        nameEditText = alertDialog.findViewById(R.id.edit_bookmark_name_edittext);
+        urlEditText = alertDialog.findViewById(R.id.edit_bookmark_url_edittext);
         editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
 
         // Get the current favorite icon byte array from the `Cursor`.
@@ -190,12 +183,9 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
         editButton.setEnabled(false);
 
         // Update the edit button if the icon selection changes.
-        iconRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
-                // Update the edit button.
-                updateEditButton();
-            }
+        iconRadioGroup.setOnCheckedChangeListener((RadioGroup group, int checkedId) -> {
+            // Update the edit button.
+            updateEditButton();
         });
 
         // Update the edit button if the bookmark name changes.
@@ -236,38 +226,37 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
             }
         });
 
-        // Allow the `enter` key on the keyboard to save the bookmark from `edit_bookmark_name_edittext`.
-        nameEditText.setOnKeyListener(new View.OnKeyListener() {
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                // If the event is an `ACTION_DOWN` on the `enter` key, save the bookmark.
-                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
-                    // Trigger `onSaveEditBookmark()` and return the `DialogFragment` to the parent activity.
-                    editBookmarkListener.onSaveEditBookmark(EditBookmarkDialog.this, selectedBookmarkDatabaseId);
-                    // Manually dismiss `alertDialog`.
-                    alertDialog.dismiss();
-                    // Consume the event.
-                    return true;
-                } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
-                    return false;
-                }
+        // Allow the `enter` key on the keyboard to save the bookmark from the bookmark name `EditText`.
+        nameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // Save the bookmark if the event is a key-down on the "enter" button.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
+                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
+                editBookmarkListener.onSaveBookmark(EditBookmarkDialog.this, selectedBookmarkDatabaseId);
+
+                // Manually dismiss `alertDialog`.
+                alertDialog.dismiss();
+
+                // Consume the event.
+                return true;
+            } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+                return false;
             }
         });
 
-        // Allow the "enter" key on the keyboard to save the bookmark from `edit_bookmark_url_edittext`.
-        urlEditText.setOnKeyListener(new View.OnKeyListener() {
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                // If the event is a key-down on the `enter` button, select the PositiveButton `Save`.
-                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
-                    // Trigger `editBookmarkListener` and return the DialogFragment to the parent activity.
-                    editBookmarkListener.onSaveEditBookmark(EditBookmarkDialog.this, selectedBookmarkDatabaseId);
-                    // Manually dismiss the `AlertDialog`.
-                    alertDialog.dismiss();
-                    // Consume the event.
-                    return true;
-                } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
-                    return false;
-                }
+        // Allow the "enter" key on the keyboard to save the bookmark from the URL `EditText`.
+        urlEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // Save the bookmark if the event is a key-down on the "enter" button.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
+                // Trigger the `Listener` and return the DialogFragment to the parent activity.
+                editBookmarkListener.onSaveBookmark(EditBookmarkDialog.this, selectedBookmarkDatabaseId);
+
+                // Manually dismiss the `AlertDialog`.
+                alertDialog.dismiss();
+
+                // Consume the event.
+                return true;
+            } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+                return false;
             }
         });
 
@@ -289,6 +278,7 @@ public class EditBookmarkDialog extends AppCompatDialogFragment {
         // Has the URL changed?
         boolean urlChanged = !newUrl.equals(currentUrl);
 
+        // Update the enabled status of the edit button.
         editButton.setEnabled(iconChanged || nameChanged || urlChanged);
     }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.java
new file mode 100644 (file)
index 0000000..41bfbd0
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ *
+ * Privacy Browser 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,
+ * 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/>.
+ */
+
+package com.stoutner.privacybrowser.dialogs;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+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;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.widget.ResourceCursorAdapter;
+// `AppCompatDialogFragment` is required instead of `DialogFragment` or an error is produced on API <=22.
+import android.support.v7.app.AppCompatDialogFragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.activities.MainWebViewActivity;
+import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
+
+public class EditBookmarkFolderDatabaseViewDialog extends AppCompatDialogFragment {
+    // Instantiate the constants.
+    public static final int HOME_FOLDER_DATABASE_ID = -1;
+
+    // Instantiate the class variables.
+    private EditBookmarkFolderDatabaseViewListener editBookmarkFolderDatabaseViewListener;
+    private BookmarksDatabaseHelper bookmarksDatabaseHelper;
+    private int folderDatabaseId;
+    private StringBuilder exceptFolders;
+    private String currentFolderName;
+    private int currentParentFolderDatabaseId;
+    private String currentDisplayOrder;
+    private RadioButton currentIconRadioButton;
+    private EditText nameEditText;
+    private Spinner folderSpinner;
+    private EditText displayOrderEditText;
+    private Button editButton;
+
+    // The public interface is used to send information back to the parent activity.
+    public interface EditBookmarkFolderDatabaseViewListener {
+        void onSaveBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId);
+    }
+
+    public void onAttach(Context context) {
+        // Run the default commands.
+        super.onAttach(context);
+
+        // Get a handle for `EditBookmarkDatabaseViewListener` from `context`.
+        try {
+            editBookmarkFolderDatabaseViewListener = (EditBookmarkFolderDatabaseViewListener) context;
+        } catch(ClassCastException exception) {
+            throw new ClassCastException(context.toString() + " must implement EditBookmarkFolderDatabaseViewListener.");
+        }
+    }
+
+    // Store the database ID in the arguments bundle.
+    public static EditBookmarkFolderDatabaseViewDialog folderDatabaseId(int databaseId) {
+        // Create a bundle.
+        Bundle bundle = new Bundle();
+
+        // Store the bookmark database ID in the bundle.
+        bundle.putInt("Database ID", databaseId);
+
+        // Add the bundle to the dialog.
+        EditBookmarkFolderDatabaseViewDialog editBookmarkFolderDatabaseViewDialog = new EditBookmarkFolderDatabaseViewDialog();
+        editBookmarkFolderDatabaseViewDialog.setArguments(bundle);
+
+        // Return the new dialog.
+        return editBookmarkFolderDatabaseViewDialog;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        // Run the default commands.
+        super.onCreate(savedInstanceState);
+
+        // Store the bookmark database ID in the class variable.
+        folderDatabaseId = getArguments().getInt("Database ID");
+    }
+
+    // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
+    @SuppressLint("InflateParams")
+    @Override
+    @NonNull
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Initialize the database helper.  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(getContext(), null, null, 0);
+
+        // Get a `Cursor` with the selected bookmark and move it to the first position.
+        Cursor folderCursor = bookmarksDatabaseHelper.getBookmarkCursor(folderDatabaseId);
+        folderCursor.moveToFirst();
+
+        // Use `AlertDialog.Builder` to create the `AlertDialog`.
+        AlertDialog.Builder dialogBuilder;
+
+        // Set the style according to the theme.
+        if (MainWebViewActivity.darkTheme) {
+            dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
+        } else {
+            dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
+        }
+
+        // Set the title.
+        dialogBuilder.setTitle(R.string.edit_folder);
+
+        // Set the view.  The parent view is `null` because it will be assigned by `AlertDialog`.
+        dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.edit_bookmark_folder_databaseview_dialog, null));
+
+        // Set an `onClick()` listener for the negative button.
+        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
+            // Do nothing.  The `AlertDialog` will close automatically.
+        });
+
+        // Set the `onClick()` listener fo the positive button.
+        dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
+            // Return the `DialogFragment` to the parent activity on save.
+            editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(EditBookmarkFolderDatabaseViewDialog.this, folderDatabaseId);
+        });
+
+        // Create an `AlertDialog` from the `AlertDialog.Builder`.
+        final AlertDialog alertDialog = dialogBuilder.create();
+
+        // Remove the warning below that `setSoftInputMode` might produce `java.lang.NullPointerException`.
+        assert alertDialog.getWindow() != null;
+
+        // Set the keyboard to be hidden when the `AlertDialog` is first shown.  If this is not set, the `AlertDialog` will not shrink when the keyboard is displayed.
+        alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
+
+        // The `AlertDialog` must be shown before items in the layout can be modified.
+        alertDialog.show();
+
+        // Get handles for the layout items.
+        TextView databaseIdTextView = alertDialog.findViewById(R.id.edit_folder_database_id_textview);
+        RadioGroup iconRadioGroup = alertDialog.findViewById(R.id.edit_folder_icon_radiogroup);
+        ImageView currentIconImageView = alertDialog.findViewById(R.id.edit_folder_current_icon_imageview);
+        ImageView newFavoriteIconImageView = alertDialog.findViewById(R.id.edit_folder_webpage_favorite_icon_imageview);
+        currentIconRadioButton = alertDialog.findViewById(R.id.edit_folder_current_icon_radiobutton);
+        nameEditText = alertDialog.findViewById(R.id.edit_folder_name_edittext);
+        folderSpinner = alertDialog.findViewById(R.id.edit_folder_parent_folder_spinner);
+        displayOrderEditText = alertDialog.findViewById(R.id.edit_folder_display_order_edittext);
+        editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+
+        // Store the current folder values.
+        currentFolderName = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+        currentDisplayOrder = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER));
+        String parentFolder = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER));
+
+        // Set the database ID.
+        databaseIdTextView.setText(String.valueOf(folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper._ID))));
+
+        // Get the current favorite icon byte array from the `Cursor`.
+        byte[] currentIconByteArray = folderCursor.getBlob(folderCursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON));
+
+        // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
+        Bitmap currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.length);
+
+        // Display `currentIconBitmap` in `edit_bookmark_current_icon`.
+        currentIconImageView.setImageBitmap(currentIconBitmap);
+
+        // Get a `Bitmap` of the favorite icon from `MainWebViewActivity` and display it in `edit_bookmark_web_page_favorite_icon`.
+        newFavoriteIconImageView.setImageBitmap(MainWebViewActivity.favoriteIconBitmap);
+
+        // Populate the folder name `EditText`.
+        nameEditText.setText(currentFolderName);
+
+        // Setup a `MatrixCursor` "Home Folder".
+        String[] matrixCursorColumnNames = {BookmarksDatabaseHelper._ID, BookmarksDatabaseHelper.BOOKMARK_NAME};
+        MatrixCursor matrixCursor = new MatrixCursor(matrixCursorColumnNames);
+        matrixCursor.addRow(new Object[]{HOME_FOLDER_DATABASE_ID, getString(R.string.home_folder)});
+
+        // Initialize a `StringBuilder` to track the folders not to display in the `Spinner` and populate it with the current folder.
+        exceptFolders = new StringBuilder(DatabaseUtils.sqlEscapeString(currentFolderName));
+
+        // Add all subfolders of the current folder to the list of folders not to display.
+        addSubfoldersToExceptFolders(currentFolderName);
+
+        // Get a `Cursor` with the list of all the folders.
+        Cursor foldersCursor = bookmarksDatabaseHelper.getFoldersCursorExcept(exceptFolders.toString());
+
+        // Combine `matrixCursor` and `foldersCursor`.
+        MergeCursor foldersMergeCursor = new MergeCursor(new Cursor[]{matrixCursor, foldersCursor});
+
+        // Create a `ResourceCursorAdapter` for the `Spinner`.  `0` specifies no flags.;
+        ResourceCursorAdapter foldersCursorAdapter = new ResourceCursorAdapter(getContext(), R.layout.edit_bookmark_databaseview_spinner_item, foldersMergeCursor, 0) {
+            @Override
+            public void bindView(View view, Context context, Cursor cursor) {
+                // Get a handle for the `Spinner` item `TextView`.
+                TextView spinnerItemTextView = view.findViewById(R.id.spinner_item_textview);
+
+                // Set the `TextView` to display the folder name.
+                spinnerItemTextView.setText(cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)));
+            }
+        };
+
+        // Set the `ResourceCursorAdapter` drop drown view resource.
+        foldersCursorAdapter.setDropDownViewResource(R.layout.edit_bookmark_databaseview_spinner_dropdown_item);
+
+        // Set the adapter for the folder `Spinner`.
+        folderSpinner.setAdapter(foldersCursorAdapter);
+
+        // Select the current folder in the `Spinner` if the bookmark isn't in the "Home Folder".
+        if (!parentFolder.equals("")) {
+            // Get the database ID of the parent folder.
+            int folderDatabaseId = bookmarksDatabaseHelper.getFolderDatabaseId(folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.PARENT_FOLDER)));
+
+            // Initialize `parentFolderPosition` and the iteration variable.
+            int parentFolderPosition = 0;
+            int i = 0;
+
+            // Find the parent folder position in folders `ResourceCursorAdapter`.
+            do {
+                if (foldersCursorAdapter.getItemId(i) == folderDatabaseId) {
+                    // Store the current position for the parent folder.
+                    parentFolderPosition = i;
+                } else {
+                    // Try the next entry.
+                    i++;
+                }
+                // Stop when the parent folder position is found or all the items in the `ResourceCursorAdapter` have been checked.
+            } while ((parentFolderPosition == 0) && (i < foldersCursorAdapter.getCount()));
+
+            // Select the parent folder in the `Spinner`.
+            folderSpinner.setSelection(parentFolderPosition);
+        }
+
+        // Store the current folder database ID.
+        currentParentFolderDatabaseId = (int) folderSpinner.getSelectedItemId();
+
+        // Populate the display order `EditText`.
+        displayOrderEditText.setText(String.valueOf(folderCursor.getInt(folderCursor.getColumnIndex(BookmarksDatabaseHelper.DISPLAY_ORDER))));
+
+        // Initially disable the edit button.
+        editButton.setEnabled(false);
+
+        // Update the edit button if the icon selection changes.
+        iconRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
+            // Update the edit button.
+            updateEditButton();
+        });
+
+        // Update the edit button if the bookmark name changes.
+        nameEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                // Do nothing.
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Update the edit button.
+                updateEditButton();
+            }
+        });
+
+        // Update the edit button if the folder changes.
+        folderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                // Update the edit button.
+                updateEditButton();
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+
+            }
+        });
+
+        // Update the edit button if the display order changes.
+        displayOrderEditText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                // Do nothing.
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                // Do nothing.
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                // Update the edit button.
+                updateEditButton();
+            }
+        });
+
+        // Allow the `enter` key on the keyboard to save the bookmark from the bookmark name `EditText`.
+        nameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // Save the bookmark if the event is a key-down on the "enter" button.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
+                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
+                editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(EditBookmarkFolderDatabaseViewDialog.this, folderDatabaseId);
+
+                // Manually dismiss `alertDialog`.
+                alertDialog.dismiss();
+
+                // Consume the event.
+                return true;
+            } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+                return false;
+            }
+        });
+
+        // Allow the "enter" key on the keyboard to save the bookmark from the display order `EditText`.
+        displayOrderEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // Save the bookmark if the event is a key-down on the "enter" button.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
+                // Trigger the `Listener` and return the `DialogFragment` to the parent activity.
+                editBookmarkFolderDatabaseViewListener.onSaveBookmarkFolder(EditBookmarkFolderDatabaseViewDialog.this, folderDatabaseId);
+
+                // Manually dismiss the `AlertDialog`.
+                alertDialog.dismiss();
+
+                // Consume the event.
+                return true;
+            } else { // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+                return false;
+            }
+        });
+
+        // `onCreateDialog` requires the return of an `AlertDialog`.
+        return alertDialog;
+    }
+
+    private void updateEditButton() {
+        // Get the values from the dialog.
+        String newFolderName = nameEditText.getText().toString();
+        int newParentFolderDatabaseId = (int) folderSpinner.getSelectedItemId();
+        String newDisplayOrder = displayOrderEditText.getText().toString();
+
+        // Get a cursor for the new folder name if it exists.
+        Cursor folderExistsCursor = bookmarksDatabaseHelper.getFolderCursor(newFolderName);
+
+        // Is the new folder name empty?
+        boolean folderNameNotEmpty = !newFolderName.isEmpty();
+
+        // Does the folder name already exist?
+        boolean folderNameAlreadyExists = (!newFolderName.equals(currentFolderName) && (folderExistsCursor.getCount() > 0));
+
+        // Has the favorite icon changed?
+        boolean iconChanged = !currentIconRadioButton.isChecked();
+
+        // Has the name been renamed?
+        boolean folderRenamed = (!newFolderName.equals(currentFolderName) && !folderNameAlreadyExists);
+
+        // Has the folder changed?
+        boolean parentFolderChanged = newParentFolderDatabaseId != currentParentFolderDatabaseId;
+
+        // Has the display order changed?
+        boolean displayOrderChanged = !newDisplayOrder.equals(currentDisplayOrder);
+
+        // Is the display order empty?
+        boolean displayOrderNotEmpty = !newDisplayOrder.isEmpty();
+
+        // Update the enabled status of the edit button.
+        editButton.setEnabled((iconChanged || folderRenamed || parentFolderChanged || displayOrderChanged) && folderNameNotEmpty && displayOrderNotEmpty);
+    }
+
+    private void addSubfoldersToExceptFolders(String folderName) {
+        // Get a `Cursor` will all the immediate subfolders.
+        Cursor subfoldersCursor = bookmarksDatabaseHelper.getSubfoldersCursor(folderName);
+
+        for (int i = 0; i < subfoldersCursor.getCount(); i++) {
+            // Move `subfolderCursor` to the current item.
+            subfoldersCursor.moveToPosition(i);
+
+            // Get the name of the subfolder.
+            String subfolderName = subfoldersCursor.getString(subfoldersCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
+
+            // Add the subfolder to `exceptFolders`.
+            exceptFolders.append(",");
+            exceptFolders.append(DatabaseUtils.sqlEscapeString(subfolderName));
+
+            // Run the same tasks for any subfolders of the subfolder.
+            addSubfoldersToExceptFolders(subfolderName);
+        }
+    }
+}
\ No newline at end of file
index 4bd1a689c09d1a096aa110afe9e10493fb514d35..ab3e690d01346f929743be59cc50c348686da59a 100644 (file)
@@ -28,7 +28,6 @@ import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Bundle;
-import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
 // `AppCompatDialogFragment` is required instead of `DialogFragment` or an error is produced on API <=22.
 import android.support.v7.app.AppCompatDialogFragment;
@@ -50,7 +49,7 @@ import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
 public class EditBookmarkFolderDialog extends AppCompatDialogFragment {
     // The public interface is used to send information back to the parent activity.
     public interface EditBookmarkFolderListener {
-        void onSaveEditBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId);
+        void onSaveBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId);
     }
 
     // Instantiate the class variables.
@@ -123,20 +122,14 @@ public class EditBookmarkFolderDialog extends AppCompatDialogFragment {
         dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.edit_bookmark_folder_dialog, null));
 
         // Set an `onClick()` listener for the negative button.
-        dialogBuilder.setNegativeButton(R.string.cancel, new Dialog.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                // Do nothing.  The `AlertDialog` will close automatically.
-            }
+        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
+            // Do nothing.  The `AlertDialog` will close automatically.
         });
 
         // Set the `onClick()` listener fo the positive button.
-        dialogBuilder.setPositiveButton(R.string.save, new Dialog.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                // Return the `DialogFragment` to the parent activity on save.
-                editBookmarkFolderListener.onSaveEditBookmarkFolder(EditBookmarkFolderDialog.this, selectedFolderDatabaseId);
-            }
+        dialogBuilder.setPositiveButton(R.string.save, (DialogInterface dialog, int which) -> {
+            // Return the `DialogFragment` to the parent activity on save.
+            editBookmarkFolderListener.onSaveBookmarkFolder(EditBookmarkFolderDialog.this, selectedFolderDatabaseId);
         });
 
         // Create an `AlertDialog` from the `AlertDialog.Builder`.
@@ -153,8 +146,8 @@ public class EditBookmarkFolderDialog extends AppCompatDialogFragment {
 
         // Get handles for layout items in the `AlertDialog`.
         final Button editButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-        final RadioButton currentIconRadioButton = (RadioButton) alertDialog.findViewById(R.id.edit_folder_current_icon_radiobutton);
-        RadioGroup iconRadioGroup = (RadioGroup) alertDialog.findViewById(R.id.edit_folder_icon_radio_group);
+        final RadioButton currentIconRadioButton = alertDialog.findViewById(R.id.edit_folder_current_icon_radiobutton);
+        RadioGroup iconRadioGroup = alertDialog.findViewById(R.id.edit_folder_icon_radio_group);
 
         // Initially disable the edit button.
         editButton.setEnabled(false);
@@ -164,18 +157,18 @@ public class EditBookmarkFolderDialog extends AppCompatDialogFragment {
         // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
         Bitmap currentIconBitmap = BitmapFactory.decodeByteArray(currentIconByteArray, 0, currentIconByteArray.length);
         // Display `currentIconBitmap` in `edit_folder_current_icon`.
-        ImageView currentIconImageView = (ImageView) alertDialog.findViewById(R.id.edit_folder_current_icon);
+        ImageView currentIconImageView = alertDialog.findViewById(R.id.edit_folder_current_icon_imageview);
         currentIconImageView.setImageBitmap(currentIconBitmap);
 
         // Get a `Bitmap` of the favorite icon from `MainWebViewActivity` and display it in `edit_folder_web_page_favorite_icon`.
-        ImageView webPageFavoriteIconImageView = (ImageView) alertDialog.findViewById(R.id.edit_folder_web_page_favorite_icon);
+        ImageView webPageFavoriteIconImageView = alertDialog.findViewById(R.id.edit_folder_web_page_favorite_icon_imageview);
         webPageFavoriteIconImageView.setImageBitmap(MainWebViewActivity.favoriteIconBitmap);
 
         // Get the current folder name.
         final String currentFolderName = folderCursor.getString(folderCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
 
         // Display the current folder name in `edit_folder_name_edittext`.
-        final EditText folderNameEditText = (EditText) alertDialog.findViewById(R.id.edit_folder_name_edittext);
+        final EditText folderNameEditText = alertDialog.findViewById(R.id.edit_folder_name_edittext);
         folderNameEditText.setText(currentFolderName);
 
         // Update the status of the edit button when the folder name is changed.
@@ -199,7 +192,7 @@ public class EditBookmarkFolderDialog extends AppCompatDialogFragment {
                 Cursor folderExistsCursor = bookmarksDatabaseHelper.getFolderCursor(newFolderName);
 
                 // Is the new folder name empty?
-                boolean folderNameEmpty = newFolderName.isEmpty();
+                boolean folderNameNotEmpty = !newFolderName.isEmpty();
 
                 // Does the folder name already exist?
                 boolean folderNameAlreadyExists = (!newFolderName.equals(currentFolderName) && (folderExistsCursor.getCount() > 0));
@@ -211,53 +204,48 @@ public class EditBookmarkFolderDialog extends AppCompatDialogFragment {
                 boolean iconChanged = (!currentIconRadioButton.isChecked() && !folderNameAlreadyExists);
 
                 // Enable the create button if something has been edited and the new folder name is valid.
-                editButton.setEnabled(!folderNameEmpty && (folderRenamed || iconChanged));
+                editButton.setEnabled(folderNameNotEmpty && (folderRenamed || iconChanged));
             }
         });
 
         // Update the status of the edit button when the icon is changed.
-        iconRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
-                // Get the new folder name.
-                String newFolderName = folderNameEditText.getText().toString();
+        iconRadioGroup.setOnCheckedChangeListener((RadioGroup group, int checkedId) -> {
+            // Get the new folder name.
+            String newFolderName = folderNameEditText.getText().toString();
 
-                // Get a cursor for the new folder name if it exists.
-                Cursor folderExistsCursor = bookmarksDatabaseHelper.getFolderCursor(newFolderName);
+            // Get a cursor for the new folder name if it exists.
+            Cursor folderExistsCursor = bookmarksDatabaseHelper.getFolderCursor(newFolderName);
 
-                // Is the new folder name empty?
-                boolean folderNameEmpty = newFolderName.isEmpty();
+            // Is the new folder name empty?
+            boolean folderNameEmpty = newFolderName.isEmpty();
 
-                // Does the folder name already exist?
-                boolean folderNameAlreadyExists = (!newFolderName.equals(currentFolderName) && (folderExistsCursor.getCount() > 0));
+            // Does the folder name already exist?
+            boolean folderNameAlreadyExists = (!newFolderName.equals(currentFolderName) && (folderExistsCursor.getCount() > 0));
 
-                // Has the folder been renamed?
-                boolean folderRenamed = (!newFolderName.equals(currentFolderName) && !folderNameAlreadyExists);
+            // Has the folder been renamed?
+            boolean folderRenamed = (!newFolderName.equals(currentFolderName) && !folderNameAlreadyExists);
 
-                // Has the favorite icon changed?
-                boolean iconChanged = (!currentIconRadioButton.isChecked() && !folderNameAlreadyExists);
+            // Has the favorite icon changed?
+            boolean iconChanged = (!currentIconRadioButton.isChecked() && !folderNameAlreadyExists);
 
-                // Enable the create button if something has been edited and the new folder name is valid.
-                editButton.setEnabled(!folderNameEmpty && (folderRenamed || iconChanged));
-            }
+            // Enable the create button if something has been edited and the new folder name is valid.
+            editButton.setEnabled(!folderNameEmpty && (folderRenamed || iconChanged));
         });
 
         // Allow the `enter` key on the keyboard to save the bookmark from `edit_bookmark_name_edittext`.
-        folderNameEditText.setOnKeyListener(new View.OnKeyListener() {
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                // If the event is a key-down on the "enter" button, select the PositiveButton `Save`.
-                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
-                    // Trigger `editBookmarkListener` and return the DialogFragment to the parent activity.
-                    editBookmarkFolderListener.onSaveEditBookmarkFolder(EditBookmarkFolderDialog.this, selectedFolderDatabaseId);
-
-                    // Manually dismiss the `AlertDialog`.
-                    alertDialog.dismiss();
-
-                    // Consume the event.
-                    return true;
-                } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
-                    return false;
-                }
+        folderNameEditText.setOnKeyListener((View v, int keyCode, KeyEvent event) -> {
+            // If the event is a key-down on the "enter" button, select the PositiveButton `Save`.
+            if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER) && editButton.isEnabled()) {  // The enter key was pressed and the edit button is enabled.
+                // Trigger `editBookmarkListener` and return the DialogFragment to the parent activity.
+                editBookmarkFolderListener.onSaveBookmarkFolder(EditBookmarkFolderDialog.this, selectedFolderDatabaseId);
+
+                // Manually dismiss the `AlertDialog`.
+                alertDialog.dismiss();
+
+                // Consume the event.
+                return true;
+            } else {  // If any other key was pressed, or if the edit button is currently disabled, do not consume the event.
+                return false;
             }
         });
 
index eff2f7774a0614e18e67aff4b3f19fdd934be615..2a767d969cfaad375151882d11f34c66ed63e6c2 100644 (file)
@@ -54,6 +54,10 @@ import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
 import java.io.ByteArrayOutputStream;
 
 public class MoveToFolderDialog extends AppCompatDialogFragment {
+    // Instantiate class variables.
+    private BookmarksDatabaseHelper bookmarksDatabaseHelper;
+    private StringBuilder exceptFolders;
+
     // The public interface is used to send information back to the parent activity.
     public interface MoveToFolderListener {
         void onMoveToFolder(AppCompatDialogFragment dialogFragment);
@@ -73,14 +77,14 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
         }
     }
 
-    // `exceptFolders` is used in `onCreateDialog()` and `addSubfoldersToExceptFolders()`.
-    private String exceptFolders;
-
     // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
     @SuppressLint("InflateParams")
     @Override
     @NonNull
     public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Initialize the database helper.  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(getContext(), null, null, 0);
+
         // Use `AlertDialog.Builder` to create the `AlertDialog`.
         AlertDialog.Builder dialogBuilder;
 
@@ -98,20 +102,14 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
         dialogBuilder.setView(getActivity().getLayoutInflater().inflate(R.layout.move_to_folder_dialog, null));
 
         // Set an `onClick()` listener for the negative button.
-        dialogBuilder.setNegativeButton(R.string.cancel, new Dialog.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                // Do nothing.  The `AlertDialog` will close automatically.
-            }
+        dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
+            // Do nothing.  The `AlertDialog` will close automatically.
         });
 
         // Set the `onClick()` listener fo the positive button.
-        dialogBuilder.setPositiveButton(R.string.move, new Dialog.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                // Return the `DialogFragment` to the parent activity on save.
-                moveToFolderListener.onMoveToFolder(MoveToFolderDialog.this);
-            }
+        dialogBuilder.setPositiveButton(R.string.move, (DialogInterface dialog, int which) -> {
+            // Return the `DialogFragment` to the parent activity on save.
+            moveToFolderListener.onMoveToFolder(MoveToFolderDialog.this);
         });
 
         // Create an `AlertDialog` from the `AlertDialog.Builder`.
@@ -133,7 +131,7 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
         // Check to see if we are in the `Home Folder`.
         if (BookmarksActivity.currentFolder.isEmpty()) {  // Don't display `Home Folder` at the top of the `ListView`.
             // Initialize `exceptFolders`.
-            exceptFolders = "";
+            exceptFolders = new StringBuilder();
 
             // If a folder is selected, add it and all children to the list of folders not to display.
             long[] selectedBookmarksLongArray = BookmarksActivity.checkedItemIds;
@@ -142,16 +140,18 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
                 int databaseIdInt = (int) databaseIdLong;
 
                 // If `databaseIdInt` is a folder.
-                if (BookmarksActivity.bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
+                if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
                     // Get the name of the selected folder.
-                    String folderName = BookmarksActivity.bookmarksDatabaseHelper.getFolderName(databaseIdInt);
+                    String folderName = bookmarksDatabaseHelper.getFolderName(databaseIdInt);
 
-                    if (exceptFolders.isEmpty()){
+                    // Populate the list of folders not to get.
+                    if (exceptFolders.toString().isEmpty()){
                         // Add the selected folder to the list of folders not to display.
-                        exceptFolders = DatabaseUtils.sqlEscapeString(folderName);
+                        exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName));
                     } else {
                         // Add the selected folder to the end of the list of folders not to display.
-                        exceptFolders = exceptFolders + "," + DatabaseUtils.sqlEscapeString(folderName);
+                        exceptFolders.append(",");
+                        exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName));
                     }
 
                     // Add the selected folder's subfolders to the list of folders not to display.
@@ -160,7 +160,7 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
             }
 
             // Get a `Cursor` containing the folders to display.
-            foldersCursor = BookmarksActivity.bookmarksDatabaseHelper.getFoldersCursorExcept(exceptFolders);
+            foldersCursor = bookmarksDatabaseHelper.getFoldersCursorExcept(exceptFolders.toString());
 
             // Setup `foldersCursorAdaptor` with `this` context.  `false` disables autoRequery.
             foldersCursorAdapter = new CursorAdapter(alertDialog.getContext(), foldersCursor, false) {
@@ -177,12 +177,12 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
                     // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
                     Bitmap folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.length);
                     // Display `folderIconBitmap` in `move_to_folder_icon`.
-                    ImageView folderIconImageView = (ImageView) view.findViewById(R.id.move_to_folder_icon);
+                    ImageView folderIconImageView = view.findViewById(R.id.move_to_folder_icon);
                     folderIconImageView.setImageBitmap(folderIconBitmap);
 
                     // Get the folder name from `cursor` and display it in `move_to_folder_name_textview`.
                     String folderName = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
-                    TextView folderNameTextView = (TextView) view.findViewById(R.id.move_to_folder_name_textview);
+                    TextView folderNameTextView = view.findViewById(R.id.move_to_folder_name_textview);
                     folderNameTextView.setText(folderName);
                 }
             };
@@ -202,7 +202,7 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
             homeFolderMatrixCursor.addRow(new Object[]{0, getString(R.string.home_folder), homeFolderIconByteArray});
 
             // Add the parent folder to the list of folders not to display.
-            exceptFolders = DatabaseUtils.sqlEscapeString(BookmarksActivity.currentFolder);
+            exceptFolders.append(DatabaseUtils.sqlEscapeString(BookmarksActivity.currentFolder));
 
             // If a folder is selected, add it and all children to the list of folders not to display.
             long[] selectedBookmarksLongArray = BookmarksActivity.checkedItemIds;
@@ -211,12 +211,13 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
                 int databaseIdInt = (int) databaseIdLong;
 
                 // If `databaseIdInt` is a folder.
-                if (BookmarksActivity.bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
+                if (bookmarksDatabaseHelper.isFolder(databaseIdInt)) {
                     // Get the name of the selected folder.
-                    String folderName = BookmarksActivity.bookmarksDatabaseHelper.getFolderName(databaseIdInt);
+                    String folderName = bookmarksDatabaseHelper.getFolderName(databaseIdInt);
 
                     // Add the selected folder to the end of the list of folders not to display.
-                    exceptFolders = exceptFolders + "," + DatabaseUtils.sqlEscapeString(folderName);
+                    exceptFolders.append(",");
+                    exceptFolders.append(DatabaseUtils.sqlEscapeString(folderName));
 
                     // Add the selected folder's subfolders to the list of folders not to display.
                     addSubfoldersToExceptFolders(folderName);
@@ -224,7 +225,7 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
             }
 
             // Get a `Cursor` containing the folders to display.
-            foldersCursor = BookmarksActivity.bookmarksDatabaseHelper.getFoldersCursorExcept(exceptFolders);
+            foldersCursor = bookmarksDatabaseHelper.getFoldersCursorExcept(exceptFolders.toString());
 
             // Combine `homeFolderMatrixCursor` and `foldersCursor`.
             MergeCursor foldersMergeCursor = new MergeCursor(new Cursor[]{homeFolderMatrixCursor, foldersCursor});
@@ -244,28 +245,25 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
                     // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
                     Bitmap folderIconBitmap = BitmapFactory.decodeByteArray(folderIconByteArray, 0, folderIconByteArray.length);
                     // Display `folderIconBitmap` in `move_to_folder_icon`.
-                    ImageView folderIconImageView = (ImageView) view.findViewById(R.id.move_to_folder_icon);
+                    ImageView folderIconImageView = view.findViewById(R.id.move_to_folder_icon);
                     folderIconImageView.setImageBitmap(folderIconBitmap);
 
                     // Get the folder name from `cursor` and display it in `move_to_folder_name_textview`.
                     String folderName = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
-                    TextView folderNameTextView = (TextView) view.findViewById(R.id.move_to_folder_name_textview);
+                    TextView folderNameTextView = view.findViewById(R.id.move_to_folder_name_textview);
                     folderNameTextView.setText(folderName);
                 }
             };
         }
 
         // Display the ListView
-        ListView foldersListView = (ListView) alertDialog.findViewById(R.id.move_to_folder_listview);
+        ListView foldersListView = alertDialog.findViewById(R.id.move_to_folder_listview);
         foldersListView.setAdapter(foldersCursorAdapter);
 
         // Enable the move button when a folder is selected.
-        foldersListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-            @Override
-            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                // Enable the move button.
-                moveButton.setEnabled(true);
-            }
+        foldersListView.setOnItemClickListener((AdapterView<?> parent, View view, int position, long id) -> {
+            // Enable the move button.
+            moveButton.setEnabled(true);
         });
 
         // `onCreateDialog` requires the return of an `AlertDialog`.
@@ -274,7 +272,7 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
 
     private void addSubfoldersToExceptFolders(String folderName) {
         // Get a `Cursor` will all the immediate subfolders.
-        Cursor subfoldersCursor = BookmarksActivity.bookmarksDatabaseHelper.getSubfoldersCursor(folderName);
+        Cursor subfoldersCursor = bookmarksDatabaseHelper.getSubfoldersCursor(folderName);
 
         for (int i = 0; i < subfoldersCursor.getCount(); i++) {
             // Move `subfolderCursor` to the current item.
@@ -283,13 +281,12 @@ public class MoveToFolderDialog extends AppCompatDialogFragment {
             // Get the name of the subfolder.
             String subfolderName = subfoldersCursor.getString(subfoldersCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
 
+            // Add the subfolder to `exceptFolders`.
+            exceptFolders.append(",");
+            exceptFolders.append(DatabaseUtils.sqlEscapeString(subfolderName));
+
             // Run the same tasks for any subfolders of the subfolder.
             addSubfoldersToExceptFolders(subfolderName);
-
-            // Add the subfolder to `exceptFolders`.
-            subfolderName = DatabaseUtils.sqlEscapeString(subfolderName);
-            exceptFolders = exceptFolders + "," + subfolderName;
         }
-
     }
 }
index 4c7a0f4ec84116e3dc6536a97c7b25c72e663058..374f991511674f9ede24ab577ee1bafefb8ac546 100644 (file)
@@ -86,26 +86,26 @@ public class AboutTabFragment extends Fragment {
             tabLayout = inflater.inflate(R.layout.about_tab_version, container, false);
 
             // Get handles for the `TextViews`.
-            TextView versionNumberTextView = (TextView) tabLayout.findViewById(R.id.about_version_number);
-            TextView versionBrandTextView = (TextView) tabLayout.findViewById(R.id.about_version_brand);
-            TextView versionManufacturerTextView = (TextView) tabLayout.findViewById(R.id.about_version_manufacturer);
-            TextView versionModelTextView = (TextView) tabLayout.findViewById(R.id.about_version_model);
-            TextView versionDeviceTextView = (TextView) tabLayout.findViewById(R.id.about_version_device);
-            TextView versionBootloaderTextView = (TextView) tabLayout.findViewById(R.id.about_version_bootloader);
-            TextView versionRadioTextView = (TextView) tabLayout.findViewById(R.id.about_version_radio);
-            TextView versionAndroidTextView = (TextView) tabLayout.findViewById(R.id.about_version_android);
-            TextView versionBuildTextView = (TextView) tabLayout.findViewById(R.id.about_version_build);
-            TextView versionSecurityPatchTextView = (TextView) tabLayout.findViewById(R.id.about_version_securitypatch);
-            TextView versionWebKitTextView = (TextView) tabLayout.findViewById(R.id.about_version_webkit);
-            TextView versionChromeTextView = (TextView) tabLayout.findViewById(R.id.about_version_chrome);
-            TextView versionOrbotTextView = (TextView) tabLayout.findViewById(R.id.about_version_orbot);
-            TextView certificateIssuerDNTextView = (TextView) tabLayout.findViewById(R.id.about_version_certificate_issuer_dn);
-            TextView certificateSubjectDNTextView = (TextView) tabLayout.findViewById(R.id.about_version_certificate_subject_dn);
-            TextView certificateStartDateTextView = (TextView) tabLayout.findViewById(R.id.about_version_certificate_start_date);
-            TextView certificateEndDateTextView = (TextView) tabLayout.findViewById(R.id.about_version_certificate_end_date);
-            TextView certificateVersionTextView = (TextView) tabLayout.findViewById(R.id.about_version_certificate_version);
-            TextView certificateSerialNumberTextView = (TextView) tabLayout.findViewById(R.id.about_version_certificate_serial_number);
-            TextView certificateSignatureAlgorithmTextView = (TextView) tabLayout.findViewById(R.id.about_version_certificate_signature_algorithm);
+            TextView versionNumberTextView = tabLayout.findViewById(R.id.about_version_number);
+            TextView versionBrandTextView = tabLayout.findViewById(R.id.about_version_brand);
+            TextView versionManufacturerTextView = tabLayout.findViewById(R.id.about_version_manufacturer);
+            TextView versionModelTextView = tabLayout.findViewById(R.id.about_version_model);
+            TextView versionDeviceTextView = tabLayout.findViewById(R.id.about_version_device);
+            TextView versionBootloaderTextView = tabLayout.findViewById(R.id.about_version_bootloader);
+            TextView versionRadioTextView = tabLayout.findViewById(R.id.about_version_radio);
+            TextView versionAndroidTextView = tabLayout.findViewById(R.id.about_version_android);
+            TextView versionSecurityPatchTextView = tabLayout.findViewById(R.id.about_version_securitypatch);
+            TextView versionBuildTextView = tabLayout.findViewById(R.id.about_version_build);
+            TextView versionWebKitTextView = tabLayout.findViewById(R.id.about_version_webkit);
+            TextView versionChromeTextView = tabLayout.findViewById(R.id.about_version_chrome);
+            TextView versionOrbotTextView = tabLayout.findViewById(R.id.about_version_orbot);
+            TextView certificateIssuerDNTextView = tabLayout.findViewById(R.id.about_version_certificate_issuer_dn);
+            TextView certificateSubjectDNTextView = tabLayout.findViewById(R.id.about_version_certificate_subject_dn);
+            TextView certificateStartDateTextView = tabLayout.findViewById(R.id.about_version_certificate_start_date);
+            TextView certificateEndDateTextView = tabLayout.findViewById(R.id.about_version_certificate_end_date);
+            TextView certificateVersionTextView = tabLayout.findViewById(R.id.about_version_certificate_version);
+            TextView certificateSerialNumberTextView = tabLayout.findViewById(R.id.about_version_certificate_serial_number);
+            TextView certificateSignatureAlgorithmTextView = tabLayout.findViewById(R.id.about_version_certificate_signature_algorithm);
 
             // Setup the labels.
             String version = getString(R.string.version) + " " + BuildConfig.VERSION_NAME + " (" + getString(R.string.version_code) + " " + Integer.toString(BuildConfig.VERSION_CODE) + ")";
@@ -128,7 +128,7 @@ public class AboutTabFragment extends Fragment {
 
             // `webViewLayout` is only used to get the default user agent from `bare_webview`.  It is not used to render content on the screen.
             View webViewLayout = inflater.inflate(R.layout.bare_webview, container, false);
-            WebView tabLayoutWebView = (WebView) webViewLayout.findViewById(R.id.bare_webview);
+            WebView tabLayoutWebView = webViewLayout.findViewById(R.id.bare_webview);
             String userAgentString =  tabLayoutWebView.getSettings().getUserAgentString();
 
             // Get the device's information and store it in strings.
index 532c771d51e4c93d0b82ff6d6cc363e5b337f827..e152b14881afb8ff82c182da69ee439d6c2f5691 100644 (file)
@@ -32,10 +32,10 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
     private static final String BOOKMARKS_TABLE = "bookmarks";
 
     public static final String _ID = "_id";
-    public static final String DISPLAY_ORDER = "displayorder";
     public static final String BOOKMARK_NAME = "bookmarkname";
     public static final String BOOKMARK_URL = "bookmarkurl";
     public static final String PARENT_FOLDER = "parentfolder";
+    public static final String DISPLAY_ORDER = "displayorder";
     public static final String IS_FOLDER = "isfolder";
     public static final String FAVORITE_ICON = "favoriteicon";
 
@@ -49,10 +49,10 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
         // Setup the SQL string to create the `bookmarks` table.
         final String CREATE_BOOKMARKS_TABLE = "CREATE TABLE " + BOOKMARKS_TABLE + " (" +
                 _ID + " integer primary key, " +
-                DISPLAY_ORDER + " integer, " +
                 BOOKMARK_NAME + " text, " +
                 BOOKMARK_URL + " text, " +
                 PARENT_FOLDER + " text, " +
+                DISPLAY_ORDER + " integer, " +
                 IS_FOLDER + " boolean, " +
                 FAVORITE_ICON + " blob);";
 
@@ -66,15 +66,15 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
     }
 
     // Create a bookmark.
-    public void createBookmark(String bookmarkName, String bookmarkURL, int displayOrder, String parentFolder, byte[] favoriteIcon) {
+    public void createBookmark(String bookmarkName, String bookmarkURL, String parentFolder, int displayOrder, byte[] favoriteIcon) {
         // We need to store the bookmark data in a `ContentValues`.
         ContentValues bookmarkContentValues = new ContentValues();
 
         // ID is created automatically.
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
         bookmarkContentValues.put(BOOKMARK_URL, bookmarkURL);
         bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
         bookmarkContentValues.put(IS_FOLDER, false);
         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
 
@@ -89,13 +89,13 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
     }
 
     // Create a folder.
-    public void createFolder(String folderName, int displayOrder, String parentFolder, byte[] favoriteIcon) {
+    public void createFolder(String folderName, String parentFolder, byte[] favoriteIcon) {
         ContentValues bookmarkContentValues = new ContentValues();
 
-        // ID is created automatically.
-        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
+        // ID is created automatically.  Folders are always created at the top of the list.
         bookmarkContentValues.put(BOOKMARK_NAME, folderName);
         bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
+        bookmarkContentValues.put(DISPLAY_ORDER, 0);
         bookmarkContentValues.put(IS_FOLDER, true);
         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
 
@@ -146,6 +146,34 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
         return folderName;
     }
 
+    // The the database ID for the specified folder name.
+    public int getFolderDatabaseId (String folderName) {
+        // Get a readable database handle.
+        SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
+
+        // SQL escape `folderName`.
+        folderName = DatabaseUtils.sqlEscapeString(folderName);
+
+        // Prepare the SQL statement to get the `Cursor` for the folder.
+        final String GET_FOLDER = "SELECT * FROM " + BOOKMARKS_TABLE +
+                " WHERE " + BOOKMARK_NAME + " = " + folderName +
+                " AND " + IS_FOLDER + " = " + 1;
+
+        // Get `folderCursor`.  The second argument is `null` because there are no `selectionArgs`.
+        Cursor folderCursor = bookmarksDatabase.rawQuery(GET_FOLDER, null);
+
+        // Get the database ID.
+        folderCursor.moveToFirst();
+        int databaseId = folderCursor.getInt(folderCursor.getColumnIndex(_ID));
+
+        // Close the cursor and the database handle.
+        folderCursor.close();
+        bookmarksDatabase.close();
+
+        // Return the database ID.
+        return databaseId;
+    }
+
     // Get a `Cursor` for the specified folder name.
     public Cursor getFolderCursor(String folderName) {
         // Get a readable database handle.
@@ -290,15 +318,17 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
         // Get a readable database handle.
         SQLiteDatabase bookmarksDatabase = this.getReadableDatabase();
 
-        // Prepare a string that contains the comma-separated list of IDs not to get.
-        String doNotGetIdsString = "";
+        // Prepare a string builder that contains the comma-separated list of IDs not to get.
+        StringBuilder doNotGetIdsStringBuilder = new StringBuilder();
+
         // Extract the array to `doNotGetIdsString`.
         for (long databaseIdLong : exceptIdLongArray) {
             // If this is the first number, only add the number.
-            if (doNotGetIdsString.isEmpty()) {
-                doNotGetIdsString = String.valueOf(databaseIdLong);
-            } else {  // If there already is a number in the string, place a `,` before the number.
-                doNotGetIdsString = doNotGetIdsString + "," + databaseIdLong;
+            if (doNotGetIdsStringBuilder.toString().isEmpty()) {
+                doNotGetIdsStringBuilder.append(databaseIdLong);
+            } else {  // If there already is a number in the string, place a `,` before the new number.
+                doNotGetIdsStringBuilder.append(",");
+                doNotGetIdsStringBuilder.append(databaseIdLong);
             }
         }
 
@@ -308,7 +338,7 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
         // Prepare the SQL statement to select all items except those with the specified IDs.
         final String GET_All_BOOKMARKS_EXCEPT_SPECIFIED = "SELECT * FROM " + BOOKMARKS_TABLE +
                 " WHERE " + PARENT_FOLDER + " = " + folderName +
-                " AND " + _ID + " NOT IN (" + doNotGetIdsString +
+                " AND " + _ID + " NOT IN (" + doNotGetIdsStringBuilder.toString() +
                 ") ORDER BY " + DISPLAY_ORDER + " ASC";
 
         // Return the results as a `Cursor`.  The second argument is `null` because there are no `selectionArgs`.
@@ -341,9 +371,10 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
 
     // Update the bookmark name and URL.
     public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl) {
-        // Store the updated values in `bookmarkContentValues`.
+        // Initialize a `ContentValues`.
         ContentValues bookmarkContentValues = new ContentValues();
 
+        // Store the updated values.
         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
 
@@ -357,13 +388,57 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
         bookmarksDatabase.close();
     }
 
+    // Update the bookmark name, URL, parent folder, and display order.
+    public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl, String parentFolder, int displayOrder) {
+        // Initialize a `ContentValues`.
+        ContentValues bookmarkContentValues = new ContentValues();
+
+        // Store the updated values.
+        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
+        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
+        bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
+
+        // Get a writable database handle.
+        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
+
+        // Update the bookmark.  The last argument is `null` because there are no `whereArgs`.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
+
+        // Close the database handle.
+        bookmarksDatabase.close();
+    }
+
     // Update the bookmark name, URL, and favorite icon.
     public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl, byte[] favoriteIcon) {
-        // Store the updated values in `bookmarkContentValues`.
+        // Initialize a `ContentValues`.
+        ContentValues bookmarkContentValues = new ContentValues();
+
+        // Store the updated values.
+        bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
+        bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
+        bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
+
+        // Get a writable database handle.
+        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
+
+        // Update the bookmark.  The last argument is `null` because there are no `whereArgs`.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, _ID + " = " + databaseId, null);
+
+        // Close the database handle.
+        bookmarksDatabase.close();
+    }
+
+    // Update the bookmark name, URL, parent folder, display order, and favorite icon.
+    public void updateBookmark(int databaseId, String bookmarkName, String bookmarkUrl, String parentFolder, int displayOrder, byte[] favoriteIcon) {
+        // Initialize a `ContentValues`.
         ContentValues bookmarkContentValues = new ContentValues();
 
+        // Store the updated values.
         bookmarkContentValues.put(BOOKMARK_NAME, bookmarkName);
         bookmarkContentValues.put(BOOKMARK_URL, bookmarkUrl);
+        bookmarkContentValues.put(PARENT_FOLDER, parentFolder);
+        bookmarkContentValues.put(DISPLAY_ORDER, displayOrder);
         bookmarkContentValues.put(FAVORITE_ICON, favoriteIcon);
 
         // Get a writable database handle.
@@ -418,6 +493,34 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
         bookmarksDatabase.close();
     }
 
+    // Update the folder name, parent folder, and display order.
+    public void updateFolder(int databaseId, String oldFolderName, String newFolderName, String parentFolder, int displayOrder) {
+        // Get a writable database handle.
+        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
+
+        // Update the folder first.  Store the new folder name in `folderContentValues`.
+        ContentValues folderContentValues = new ContentValues();
+        folderContentValues.put(BOOKMARK_NAME, newFolderName);
+        folderContentValues.put(PARENT_FOLDER, parentFolder);
+        folderContentValues.put(DISPLAY_ORDER, displayOrder);
+
+        // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
+
+        // Update the bookmarks inside the folder.  Store the new parent folder name in `bookmarkContentValues`.
+        ContentValues bookmarkContentValues = new ContentValues();
+        bookmarkContentValues.put(PARENT_FOLDER, newFolderName);
+
+        // SQL escape `oldFolderName`.
+        oldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName);
+
+        // Run the update on all the bookmarks that currently list `oldFolderName` as their parent folder.  The last argument is `null` because there are no `whereArgs`.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, PARENT_FOLDER + " = " + oldFolderName, null);
+
+        // Close the database handle.
+        bookmarksDatabase.close();
+    }
+
     // Update the folder name and icon.
     public void updateFolder(int databaseId, String oldFolderName, String newFolderName, byte[] folderIcon) {
         // Get a writable database handle.
@@ -445,6 +548,35 @@ public class BookmarksDatabaseHelper extends SQLiteOpenHelper {
         bookmarksDatabase.close();
     }
 
+    // Update the folder name and icon.
+    public void updateFolder(int databaseId, String oldFolderName, String newFolderName, String parentFolder, int displayOrder, byte[] folderIcon) {
+        // Get a writable database handle.
+        SQLiteDatabase bookmarksDatabase = this.getWritableDatabase();
+
+        // Update the folder first.  Store the updated values in `folderContentValues`.
+        ContentValues folderContentValues = new ContentValues();
+        folderContentValues.put(BOOKMARK_NAME, newFolderName);
+        folderContentValues.put(PARENT_FOLDER, parentFolder);
+        folderContentValues.put(DISPLAY_ORDER, displayOrder);
+        folderContentValues.put(FAVORITE_ICON, folderIcon);
+
+        // Run the update on the folder.  The last argument is `null` because there are no `whereArgs`.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, folderContentValues, _ID + " = " + databaseId, null);
+
+        // Update the bookmarks inside the folder.  Store the new parent folder name in `bookmarkContentValues`.
+        ContentValues bookmarkContentValues = new ContentValues();
+        bookmarkContentValues.put(PARENT_FOLDER, newFolderName);
+
+        // SQL escape `oldFolderName`.
+        oldFolderName = DatabaseUtils.sqlEscapeString(oldFolderName);
+
+        // Run the update on all the bookmarks that currently list `oldFolderName` as their parent folder.  The last argument is `null` because there are no `whereArgs`.
+        bookmarksDatabase.update(BOOKMARKS_TABLE, bookmarkContentValues, PARENT_FOLDER + " = " + oldFolderName, null);
+
+        // Close the database handle.
+        bookmarksDatabase.close();
+    }
+
     // Update the display order for one bookmark or folder.
     public void updateDisplayOrder(int databaseId, int displayOrder) {
         // Get a writable database handle.
diff --git a/app/src/main/res/color/edit_bookmark_spinner_color_selector_dark.xml b/app/src/main/res/color/edit_bookmark_spinner_color_selector_dark.xml
new file mode 100644 (file)
index 0000000..6df2333
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser 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,
+  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/>. -->
+
+<!-- Change the dark theme enabled text from white to gray. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <!-- `#4DFFFFFF` comes from the built-in `@color/primary_text_disabled_material_dark`. -->
+    <item android:state_checked="false" android:color="#4DFFFFFF" />
+    <item android:color="@color/gray_300" />
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/color/edit_bookmark_spinner_color_selector_light.xml b/app/src/main/res/color/edit_bookmark_spinner_color_selector_light.xml
new file mode 100644 (file)
index 0000000..ef42d28
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser 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,
+  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/>. -->
+
+<!-- Change the dark theme enabled text from white to gray.-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:state_checked="false" android:color="@color/gray_600" />
+    <item android:color="@color/black" />
+</selector>
\ No newline at end of file
index 4ed8c1f8490518cfda54b8dc936420ebddbad001..e5a63d1cb791db31161c2bb29a6c377c09469183 100644 (file)
@@ -18,7 +18,7 @@
   You should have received a copy of the GNU General Public License
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
-<!-- Change the dark theme enabled text from white to gray. -->
+<!-- Change the dark theme enabled text from white to gray.-->
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item android:state_enabled="false" android:color="@color/gray_400" />
     <item android:color="@color/black" />
index 0d4cfcd284ce5b7f42f7345fcdf97c7922c7b559..1fb35abf97e985086a6acda7b885518212d605e4 100644 (file)
                 android:layout_width="wrap_content" />
 
             <TextView
-                android:id="@+id/about_version_build"
+                android:id="@+id/about_version_securitypatch"
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content" />
 
             <TextView
-                android:id="@+id/about_version_securitypatch"
+                android:id="@+id/about_version_build"
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content" />
 
index 207b19d999439e418b01aeaf02975df24d31d829..e5b6de8a7a4f0a958c6c2015497b7fa3bfcff466 100644 (file)
@@ -22,7 +22,7 @@
     When it is specified the theme should include `<item name="android:windowTranslucentStatus">true</item>`
     to make the status bar a transparent, darkened overlay. -->
 <android.support.design.widget.CoordinatorLayout
-    android:id="@+id/bookmarks_database_view_coordinatorlayout"
+    android:id="@+id/bookmarks_databaseview_coordinatorlayout"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
         android:orientation="vertical" >
 
         <android.support.design.widget.AppBarLayout
-            android:id="@+id/bookmarks_database_view_appbarlayout"
+            android:id="@+id/bookmarks_databaseview_appbarlayout"
             android:layout_height="wrap_content"
             android:layout_width="match_parent" >
 
             <android.support.v7.widget.Toolbar
-                android:id="@+id/bookmarks_database_view_toolbar"
+                android:id="@+id/bookmarks_databaseview_toolbar"
                 android:layout_height="wrap_content"
                 android:layout_width="match_parent"
                 android:background="?attr/colorPrimaryDark"
@@ -48,7 +48,7 @@
         </android.support.design.widget.AppBarLayout>
 
         <ListView
-            android:id="@+id/bookmarks_database_view_listview"
+            android:id="@+id/bookmarks_databaseview_listview"
             android:layout_height="match_parent"
             android:layout_width="match_parent" />
     </LinearLayout>
index 3c91b373a74870087ec8f55aec263564816bfb9b..bc00f7785f99aeab9899992020b7aabed2676a67 100644 (file)
@@ -19,7 +19,7 @@
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
 <LinearLayout
-    android:id="@+id/bookmarks_database_view_item_linearlayout"
+    android:id="@+id/bookmarks_databaseview_item_linearlayout"
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_height="wrap_content"
@@ -36,7 +36,7 @@
         android:orientation="horizontal" >
 
         <TextView
-            android:id="@+id/bookmarks_database_view_database_id"
+            android:id="@+id/bookmarks_databaseview_database_id"
             android:layout_height="wrap_content"
             android:layout_width="50dp"
             android:layout_marginEnd="10dp"
@@ -45,7 +45,7 @@
             android:textSize="22sp" />
 
         <ImageView
-            android:id="@+id/bookmarks_database_view_favorite_icon"
+            android:id="@+id/bookmarks_databaseview_favorite_icon"
             android:layout_width="30dp"
             android:layout_height="30dp"
             android:layout_gravity="center_vertical"
@@ -53,7 +53,7 @@
             tools:ignore="ContentDescription" />
 
         <TextView
-            android:id='@+id/bookmarks_database_view_bookmark_name'
+            android:id='@+id/bookmarks_databaseview_bookmark_name'
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             android:textColor="?android:attr/textColorPrimary"
     </LinearLayout>
 
     <!-- Second row. -->
+    <TextView
+        android:id="@+id/bookmarks_databaseview_bookmark_url"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_marginStart="13dp"
+        android:layout_marginEnd="10dp"
+        android:textColor="?android:attr/textColorPrimary"
+        android:textSize="22sp"
+        android:ellipsize="end"
+        android:maxLines="1" />
+
+    <!-- Third row. -->
     <LinearLayout
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:layout_marginStart="10dp"
         android:layout_marginEnd="10dp"
+        android:layout_marginBottom="10dp"
         android:orientation="horizontal" >
 
         <TextView
-            android:id="@+id/bookmarks_database_view_display_order"
+            android:id="@+id/bookmarks_databaseview_display_order"
             android:layout_height="wrap_content"
             android:layout_width="50dp"
             android:layout_marginEnd="10dp"
@@ -80,7 +93,7 @@
             android:textSize="22sp" />
 
         <ImageView
-            android:id="@+id/bookmarks_database_view_parent_folder_icon"
+            android:id="@+id/bookmarks_databaseview_parent_folder_icon"
             android:layout_height="30dp"
             android:layout_width="30dp"
             android:layout_gravity="center_vertical"
             tools:ignore="ContentDescription" />
 
         <TextView
-            android:id="@+id/bookmarks_database_view_parent_folder"
+            android:id="@+id/bookmarks_databaseview_parent_folder"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             android:textSize="22sp"
             android:ellipsize="end"
             android:maxLines="1" />
     </LinearLayout>
-
-    <!-- Third row. -->
-    <TextView
-        android:id="@+id/bookmarks_database_view_bookmark_url"
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:layout_marginBottom="10dp"
-        android:layout_marginStart="13dp"
-        android:layout_marginEnd="10dp"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="22sp"
-        android:ellipsize="end"
-        android:maxLines="1" />
 </LinearLayout>
\ No newline at end of file
index 804ac3ac049a01be97e619d159f6961faee50b74..9750d591b50bb5eb5504e319afe278f3c6f47232 100644 (file)
@@ -20,6 +20,6 @@
 
 <Spinner
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/bookmarks_database_view_spinner"
+    android:id="@+id/bookmarks_databaseview_spinner"
     android:layout_height="wrap_content"
     android:layout_width="wrap_content" />
diff --git a/app/src/main/res/layout/edit_bookmark_databaseview_dialog.xml b/app/src/main/res/layout/edit_bookmark_databaseview_dialog.xml
new file mode 100644 (file)
index 0000000..dbf367a
--- /dev/null
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser 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,
+  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/>. -->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent" >
+
+    <!-- Setting `android:focusable` and `android:focusableInTouchMode` prevent `edit_bookmark_display_order_edittext` from being autoselected. -->
+    <LinearLayout
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="vertical"
+        android:focusable="true"
+        android:focusableInTouchMode="true" >
+
+        <!-- Database ID. -->
+        <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_marginTop="12dp"
+            android:layout_marginBottom="6dp"
+            android:layout_marginStart="7dp"
+            android:layout_marginEnd="7dp" >
+
+            <TextView
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/database_id"
+                android:textSize="18sp"
+                android:textColor="?attr/primaryTextColorSelector"
+                android:layout_marginEnd="8dp" />
+
+            <TextView
+                android:id="@+id/edit_bookmark_database_id_textview"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:textSize="18sp"
+                android:textColor="@color/gray_500" />
+        </LinearLayout>
+
+        <!-- The icon selection rows. -->
+        <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:orientation="horizontal" >
+
+            <!-- The column displaying the icons. -->
+            <LinearLayout
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="10dp"
+                android:orientation="vertical" >
+
+                <ImageView
+                    android:id="@+id/edit_bookmark_current_icon"
+                    android:layout_width="30dp"
+                    android:layout_height="30dp"
+                    android:layout_marginTop="6dp"
+                    android:layout_marginBottom="6dp"
+                    android:layout_gravity="center_vertical"
+                    tools:ignore="ContentDescription" />
+
+                <ImageView
+                    android:id="@+id/edit_bookmark_webpage_favorite_icon"
+                    android:layout_width="30dp"
+                    android:layout_height="30dp"
+                    android:layout_marginTop="6dp"
+                    android:layout_marginBottom="6dp"
+                    tools:ignore="ContentDescription" />
+            </LinearLayout>
+
+            <!-- The column with the `RadioGroup`. -->
+            <RadioGroup
+                android:id="@+id/edit_bookmark_icon_radiogroup"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:checkedButton="@+id/edit_bookmark_current_icon_radiobutton" >
+
+                <RadioButton
+                    android:id="@id/edit_bookmark_current_icon_radiobutton"
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:layout_marginTop="6dp"
+                    android:layout_marginBottom="4dp"
+                    android:text="@string/current_bookmark_icon"
+                    android:textSize="18sp" />
+
+                <RadioButton
+                    android:id="@+id/edit_bookmark_webpage_favorite_icon_radiobutton"
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:layout_marginTop="5dp"
+                    android:text="@string/web_page_favorite_icon"
+                    android:textSize="18sp" />
+            </RadioGroup>
+        </LinearLayout>
+
+        <!-- Bookmark name.  `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
+        <android.support.design.widget.TextInputLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_marginTop="12dp"
+            android:layout_marginBottom="6dp"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="4dp" >
+
+            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the `EditText`. -->
+            <android.support.design.widget.TextInputEditText
+                android:id="@+id/edit_bookmark_name_edittext"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:hint="@string/bookmark_name"
+                android:imeOptions="actionGo"
+                android:inputType="textUri"
+                android:selectAllOnFocus="true" />
+        </android.support.design.widget.TextInputLayout>
+
+        <!-- URL.  `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
+        <android.support.design.widget.TextInputLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_marginTop="6dp"
+            android:layout_marginBottom="12dp"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="4dp" >
+
+            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the `EditText`. -->
+            <EditText
+                android:id="@+id/edit_bookmark_url_edittext"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:hint="@string/bookmark_url"
+                android:imeOptions="actionGo"
+                android:inputType="textUri"
+                android:selectAllOnFocus="true" />
+        </android.support.design.widget.TextInputLayout>
+
+        <!-- Folder. -->
+        <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:orientation="horizontal"
+            android:layout_marginTop="6dp"
+            android:layout_marginStart="7dp"
+            android:layout_marginEnd="7dp" >
+
+            <TextView
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/folder"
+                android:textSize="18sp"
+                android:textColor="?attr/primaryTextColorSelector" />
+
+            <Spinner
+                android:id="@+id/edit_bookmark_folder_spinner"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content" />
+        </LinearLayout>
+
+        <!-- Display order. -->
+        <LinearLayout
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:orientation="horizontal"
+            android:layout_marginTop="6dp"
+            android:layout_marginBottom="6dp"
+            android:layout_marginStart="7dp"
+            android:layout_marginEnd="7dp" >
+
+            <TextView
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/display_order"
+                android:textSize="18sp"
+                android:textColor="?attr/primaryTextColorSelector"
+                android:layout_marginEnd="6dp"
+                android:labelFor="@+id/edit_bookmark_display_order_edittext"/>
+
+            <EditText
+                android:id="@id/edit_bookmark_display_order_edittext"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:imeOptions="actionGo"
+                android:inputType="number"
+                android:selectAllOnFocus="true" />
+        </LinearLayout>
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/edit_bookmark_databaseview_spinner_dropdown_item.xml b/app/src/main/res/layout/edit_bookmark_databaseview_spinner_dropdown_item.xml
new file mode 100644 (file)
index 0000000..af46e36
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser 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,
+  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/>. -->
+
+<!-- A `CheckedTextView` allows the color of the text to be changed when it is selected (checked). -->
+<CheckedTextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/spinner_item_textview"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:maxLines="1"
+    android:ellipsize="end"
+    android:paddingStart="20dp"
+    android:paddingEnd="20dp"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:textSize="18sp"
+    android:textColor="?attr/editBookmarkSpinnerTextColorSelector" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/edit_bookmark_databaseview_spinner_item.xml b/app/src/main/res/layout/edit_bookmark_databaseview_spinner_item.xml
new file mode 100644 (file)
index 0000000..d06d7cf
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2017 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser 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,
+  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/>. -->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/spinner_item_textview"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:maxLines="1"
+    android:ellipsize="end"
+    android:paddingStart="10dp"
+    android:paddingEnd="10dp"
+    android:textSize="18sp"
+    android:textColor="?attr/primaryTextColorSelector" />
\ No newline at end of file
index a1b723b75dd572c0c7f96160d3aa99df2cf71c8b..47bb8f1fc0af884f13bc9962449e6748a6d0f231 100644 (file)
@@ -53,7 +53,7 @@
                     tools:ignore="ContentDescription" />
 
                 <ImageView
-                    android:id="@+id/edit_bookmark_web_page_favorite_icon"
+                    android:id="@+id/edit_bookmark_webpage_favorite_icon"
                     android:layout_width="30dp"
                     android:layout_height="30dp"
                     android:layout_marginTop="6dp"
@@ -78,7 +78,7 @@
                     android:textSize="18sp" />
 
                 <RadioButton
-                    android:id="@+id/edit_bookmark_web_page_favorite_icon_radiobutton"
+                    android:id="@+id/edit_bookmark_webpage_favorite_icon_radiobutton"
                     android:layout_height="wrap_content"
                     android:layout_width="wrap_content"
                     android:layout_marginTop="5dp"
@@ -96,8 +96,7 @@
             android:layout_marginStart="4dp"
             android:layout_marginEnd="4dp" >
 
-            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.
-                `android:inputType="textUri"` disables spell check in the `EditText`. -->
+            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the `EditText`. -->
             <android.support.design.widget.TextInputEditText
                 android:id="@+id/edit_bookmark_name_edittext"
                 android:layout_height="wrap_content"
             android:layout_marginStart="4dp"
             android:layout_marginEnd="4dp" >
 
-            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a "go" key instead of a "new line" key.
-                `android:inputType="textUri"` disables spell check in the EditText. -->
+            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the `EditText`. -->
             <EditText
                 android:id="@+id/edit_bookmark_url_edittext"
                 android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/edit_bookmark_folder_databaseview_dialog.xml b/app/src/main/res/layout/edit_bookmark_folder_databaseview_dialog.xml
new file mode 100644 (file)
index 0000000..30fc90e
--- /dev/null
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2016-2017 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+
+  Privacy Browser 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,
+  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/>. -->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent" >
+
+    <!-- Setting `android:focusable` and `android:focusableInTouchMode` prevent `edit_bookmark_display_order_edittext` from being autoselected. -->
+    <LinearLayout
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:orientation="vertical"
+        android:focusable="true"
+        android:focusableInTouchMode="true" >
+
+        <!-- Database ID. -->
+        <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_marginTop="12dp"
+            android:layout_marginBottom="6dp"
+            android:layout_marginStart="7dp"
+            android:layout_marginEnd="7dp" >
+
+            <TextView
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/database_id"
+                android:textSize="18sp"
+                android:textColor="?attr/primaryTextColorSelector"
+                android:layout_marginEnd="8dp" />
+
+            <TextView
+                android:id="@+id/edit_folder_database_id_textview"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:textSize="18sp"
+                android:textColor="@color/gray_500" />
+        </LinearLayout>
+
+        <!-- The icon selection rows. -->
+        <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:orientation="horizontal" >
+
+            <!-- The column displaying the icons. -->
+            <LinearLayout
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="10dp"
+                android:orientation="vertical" >
+
+                <ImageView
+                    android:id="@+id/edit_folder_current_icon_imageview"
+                    android:layout_width="30dp"
+                    android:layout_height="30dp"
+                    android:layout_marginTop="6dp"
+                    android:layout_marginBottom="6dp"
+                    android:layout_gravity="center_vertical"
+                    tools:ignore="ContentDescription" />
+
+                <ImageView
+                    android:id="@+id/edit_folder_default_icon_imageview"
+                    android:layout_height="30dp"
+                    android:layout_width="30dp"
+                    android:layout_marginTop="6dp"
+                    android:layout_marginBottom="6dp"
+                    android:layout_gravity="center_vertical"
+                    android:src="@drawable/folder_blue_bitmap"
+                    tools:ignore="ContentDescription" />
+
+                <ImageView
+                    android:id="@+id/edit_folder_webpage_favorite_icon_imageview"
+                    android:layout_width="30dp"
+                    android:layout_height="30dp"
+                    android:layout_marginTop="6dp"
+                    android:layout_marginBottom="6dp"
+                    tools:ignore="ContentDescription" />
+            </LinearLayout>
+
+            <!-- The column with the `RadioGroup`. -->
+            <RadioGroup
+                android:id="@+id/edit_folder_icon_radiogroup"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:checkedButton="@+id/edit_folder_current_icon_radiobutton" >
+
+                <RadioButton
+                    android:id="@id/edit_folder_current_icon_radiobutton"
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:layout_marginTop="6dp"
+                    android:layout_marginBottom="4dp"
+                    android:text="@string/current_bookmark_icon"
+                    android:textSize="18sp" />
+
+                <RadioButton
+                    android:id="@+id/edit_folder_default_icon_radiobutton"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="5dp"
+                    android:layout_marginBottom="4dp"
+                    android:text="@string/default_folder_icon"
+                    android:textSize="18sp" />
+
+                <RadioButton
+                    android:id="@+id/edit_folder_webpage_favorite_icon_radiobutton"
+                    android:layout_height="wrap_content"
+                    android:layout_width="wrap_content"
+                    android:layout_marginTop="5dp"
+                    android:text="@string/web_page_favorite_icon"
+                    android:textSize="18sp" />
+            </RadioGroup>
+        </LinearLayout>
+
+        <!-- Folder name.  `android.support.design.widget.TextInputLayout` makes the `android:hint` float above the `EditText`. -->
+        <android.support.design.widget.TextInputLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_marginTop="12dp"
+            android:layout_marginBottom="12dp"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="4dp" >
+
+            <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key.  `android:inputType="textUri"` disables spell check in the `EditText`. -->
+            <android.support.design.widget.TextInputEditText
+                android:id="@+id/edit_folder_name_edittext"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:hint="@string/folder_name"
+                android:imeOptions="actionGo"
+                android:inputType="textUri"
+                android:selectAllOnFocus="true" />
+        </android.support.design.widget.TextInputLayout>
+
+        <!-- Folder. -->
+        <LinearLayout
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:orientation="horizontal"
+            android:layout_marginTop="6dp"
+            android:layout_marginStart="7dp"
+            android:layout_marginEnd="7dp" >
+
+            <TextView
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/parent_folder"
+                android:textSize="18sp"
+                android:textColor="?attr/primaryTextColorSelector" />
+
+            <Spinner
+                android:id="@+id/edit_folder_parent_folder_spinner"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content" />
+        </LinearLayout>
+
+        <!-- Display order. -->
+        <LinearLayout
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:orientation="horizontal"
+            android:layout_marginTop="6dp"
+            android:layout_marginStart="7dp"
+            android:layout_marginEnd="7dp" >
+
+            <TextView
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/display_order"
+                android:textSize="18sp"
+                android:textColor="?attr/primaryTextColorSelector"
+                android:layout_marginEnd="6dp"
+                android:labelFor="@+id/edit_folder_display_order_edittext"/>
+
+            <EditText
+                android:id="@id/edit_folder_display_order_edittext"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:imeOptions="actionGo"
+                android:inputType="number"
+                android:selectAllOnFocus="true" />
+        </LinearLayout>
+
+        <TextView
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:gravity="center_horizontal"
+            android:text="@string/folder_names_must_be_unique" />
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
index 0ac3158635b5fdf132cb50f490394cf57bfe2274..dae0cbbe3c367a15441dcc41318fdac091f0cb06 100644 (file)
@@ -45,7 +45,7 @@
                 android:layout_marginEnd="10dp" >
 
                 <ImageView
-                    android:id="@+id/edit_folder_current_icon"
+                    android:id="@+id/edit_folder_current_icon_imageview"
                     android:layout_height="30dp"
                     android:layout_width="30dp"
                     android:layout_marginTop="12dp"
@@ -54,7 +54,7 @@
                     tools:ignore="ContentDescription" />
 
                 <ImageView
-                    android:id="@+id/edit_folder_default_icon"
+                    android:id="@+id/edit_folder_default_icon_imageview"
                     android:layout_height="30dp"
                     android:layout_width="30dp"
                     android:layout_marginTop="6dp"
@@ -64,7 +64,7 @@
                     tools:ignore="ContentDescription" />
 
                 <ImageView
-                    android:id="@+id/edit_folder_web_page_favorite_icon"
+                    android:id="@+id/edit_folder_web_page_favorite_icon_imageview"
                     android:layout_height="30dp"
                     android:layout_width="30dp"
                     android:layout_marginTop="6dp"
index e23ac447e5cc4201d29da35b42811a92486eb346..72e4c3e1a4382cb51f55937a5b293241f81c6cae 100644 (file)
@@ -35,6 +35,7 @@
     <attr name="bookmarksSpinnerHeaderTextColor" format="reference" />
     <attr name="bookmarksSpinnerTextColorSelector" format="reference" />
     <attr name="bookmarksSpinnerBackground" format="reference" />
+    <attr name="editBookmarkSpinnerTextColorSelector" format="reference" />
     <attr name="redText" format="reference" />
 
     <attr name="navigationHeaderBackground" format="reference" />
index 256bddbe5efff795eb6aad91660d931c79183714..36b265f9fb55ec8158f2831dceb35d9a34e89e91 100644 (file)
     <string name="bookmarks_database_view">Bookmarks Database View</string>
     <string name="all_folders">All Folders</string>
     <string name="home_folder">Home Folder</string>
+    <string name="database_id">Database ID:</string>
+    <string name="folder">Folder:</string>
+    <string name="parent_folder">Parent folder:</string>
+    <string name="display_order">Display order:</string>
 
     <!-- Domains. -->
     <string name="domains">Domains</string>
index 8b5027a62a337fa94d78fdce8bf22defa7233fce..343cebaa79dc0d9c6f3f6b3798a563d7f7e7d3d6 100644 (file)
@@ -49,6 +49,7 @@
         <item name="bookmarksSpinnerHeaderTextColor">@color/white</item>
         <item name="bookmarksSpinnerTextColorSelector">@color/bookmarks_spinner_color_selector_light</item>
         <item name="bookmarksSpinnerBackground">@color/blue_750</item>
+        <item name="editBookmarkSpinnerTextColorSelector">@color/edit_bookmark_spinner_color_selector_light</item>
         <item name="listSelectorDrawable">@drawable/list_selector_light</item>
         <item name="aboutTitle">@color/blue_900</item>
         <item name="aboutText">@color/blue_700</item>
         <item name="bookmarksSpinnerHeaderTextColor">@color/gray_300</item>
         <item name="bookmarksSpinnerTextColorSelector">@color/bookmarks_spinner_color_selector_dark</item>
         <item name="bookmarksSpinnerBackground">@color/blue_830</item>
+        <item name="editBookmarkSpinnerTextColorSelector">@color/edit_bookmark_spinner_color_selector_dark</item>
         <item name="aboutTitle">@color/blue_600</item>
         <item name="aboutText">@color/blue_400</item>
         <item name="aboutBackground">@color/gray_850</item>
index 477cd22475c89e8cfe07a611c377555372b0b130..5aa4870fbea023cc47691dcbab4c3e2f0a969ef4 100644 (file)
@@ -5,7 +5,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.3.3'
+        classpath 'com.android.tools.build:gradle:3.0.0'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -15,8 +15,6 @@ buildscript {
 allprojects {
     repositories {
         jcenter()
-        maven {
-            url "https://maven.google.com"
-        }
+        google()
     }
 }
index 92cecb13d7f2f103b5001e2ba2e61546f22a9cc4..7900fee993423ad151c2f03891f61be730b3f9e6 100644 (file)
@@ -1,6 +1,6 @@
-#Thu Mar 02 15:10:19 MST 2017
+#Wed Nov 01 01:37:59 MST 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip