From 9f551f25b53a30cca7b19b6e6bfc2d2520d9aa1b Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Fri, 3 Nov 2017 15:52:31 -0700 Subject: [PATCH] Add editing functionality to the bookmarks database view. https://redmine.stoutner.com/issues/45 --- .idea/dictionaries/soren.xml | 2 + .idea/misc.xml | 12 +- app/build.gradle | 23 +- .../activities/BookmarksActivity.java | 169 +++--- .../BookmarksDatabaseViewActivity.java | 272 +++++++-- .../activities/MainWebViewActivity.java | 551 ++++++++---------- .../EditBookmarkDatabaseViewDialog.java | 428 ++++++++++++++ .../dialogs/EditBookmarkDialog.java | 106 ++-- .../EditBookmarkFolderDatabaseViewDialog.java | 419 +++++++++++++ .../dialogs/EditBookmarkFolderDialog.java | 94 ++- .../dialogs/MoveToFolderDialog.java | 83 ++- .../fragments/AboutTabFragment.java | 42 +- .../helpers/BookmarksDatabaseHelper.java | 164 +++++- ...t_bookmark_spinner_color_selector_dark.xml | 26 + ..._bookmark_spinner_color_selector_light.xml | 25 + .../primary_text_color_selector_light.xml | 2 +- app/src/main/res/layout/about_tab_version.xml | 4 +- ...okmarks_databaseview_coordinatorlayout.xml | 8 +- ...okmarks_databaseview_item_linearlayout.xml | 40 +- .../layout/bookmarks_databaseview_spinner.xml | 2 +- .../edit_bookmark_databaseview_dialog.xml | 208 +++++++ ...ark_databaseview_spinner_dropdown_item.xml | 34 ++ ...dit_bookmark_databaseview_spinner_item.xml | 31 + .../main/res/layout/edit_bookmark_dialog.xml | 10 +- ...it_bookmark_folder_databaseview_dialog.xml | 212 +++++++ .../layout/edit_bookmark_folder_dialog.xml | 6 +- app/src/main/res/values/attrs.xml | 1 + app/src/main/res/values/strings.xml | 4 + app/src/main/res/values/styles.xml | 2 + build.gradle | 6 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 31 files changed, 2294 insertions(+), 696 deletions(-) create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.java create mode 100644 app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.java create mode 100644 app/src/main/res/color/edit_bookmark_spinner_color_selector_dark.xml create mode 100644 app/src/main/res/color/edit_bookmark_spinner_color_selector_light.xml create mode 100644 app/src/main/res/layout/edit_bookmark_databaseview_dialog.xml create mode 100644 app/src/main/res/layout/edit_bookmark_databaseview_spinner_dropdown_item.xml create mode 100644 app/src/main/res/layout/edit_bookmark_databaseview_spinner_item.xml create mode 100644 app/src/main/res/layout/edit_bookmark_folder_databaseview_dialog.xml diff --git a/.idea/dictionaries/soren.xml b/.idea/dictionaries/soren.xml index 39f061db..c6d78c7e 100644 --- a/.idea/dictionaries/soren.xml +++ b/.idea/dictionaries/soren.xml @@ -11,6 +11,7 @@ anonymized appbarlayout aren + autoselected bebdb becerra beeb @@ -27,6 +28,7 @@ commitdiff coordinatorlayout customuseragent + databaseview deeplinks didn displayimages diff --git a/.idea/misc.xml b/.idea/misc.xml index 95f0f031..4a0c9c49 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -27,17 +27,7 @@ - - - - - - - - - - - + diff --git a/app/build.gradle b/app/build.gradle index aa47ffcb..75ed74f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,11 +20,11 @@ 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. diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java index e640b792..763f6385 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java @@ -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)); diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java index 1725cac4..a61391db 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.java @@ -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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index bb7ae1de..c6f04a0f 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -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() { - @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 index 00000000..d75cf6c0 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDatabaseViewDialog.java @@ -0,0 +1,428 @@ +/* + * Copyright © 2016-2017 Soren Stoutner . + * + * This file is part of 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 . + */ + +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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java index a25864ba..e37dacdf 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkDialog.java @@ -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 index 00000000..41bfbd05 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDatabaseViewDialog.java @@ -0,0 +1,419 @@ +/* + * Copyright © 2016-2017 Soren Stoutner . + * + * This file is part of 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 . + */ + +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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.java index 4bd1a689..ab3e690d 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/EditBookmarkFolderDialog.java @@ -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; } }); diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java index eff2f777..2a767d96 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/MoveToFolderDialog.java @@ -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; } - } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutTabFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutTabFragment.java index 4c7a0f4e..374f9915 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutTabFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutTabFragment.java @@ -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. diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java index 532c771d..e152b148 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/BookmarksDatabaseHelper.java @@ -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 index 00000000..6df23339 --- /dev/null +++ b/app/src/main/res/color/edit_bookmark_spinner_color_selector_dark.xml @@ -0,0 +1,26 @@ + + + + + + + + + + \ 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 index 00000000..ef42d28b --- /dev/null +++ b/app/src/main/res/color/edit_bookmark_spinner_color_selector_light.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/primary_text_color_selector_light.xml b/app/src/main/res/color/primary_text_color_selector_light.xml index 4ed8c1f8..e5a63d1c 100644 --- a/app/src/main/res/color/primary_text_color_selector_light.xml +++ b/app/src/main/res/color/primary_text_color_selector_light.xml @@ -18,7 +18,7 @@ You should have received a copy of the GNU General Public License along with Privacy Browser. If not, see . --> - + diff --git a/app/src/main/res/layout/about_tab_version.xml b/app/src/main/res/layout/about_tab_version.xml index 0d4cfcd2..1fb35abf 100644 --- a/app/src/main/res/layout/about_tab_version.xml +++ b/app/src/main/res/layout/about_tab_version.xml @@ -131,12 +131,12 @@ android:layout_width="wrap_content" /> diff --git a/app/src/main/res/layout/bookmarks_databaseview_coordinatorlayout.xml b/app/src/main/res/layout/bookmarks_databaseview_coordinatorlayout.xml index 207b19d9..e5b6de8a 100644 --- a/app/src/main/res/layout/bookmarks_databaseview_coordinatorlayout.xml +++ b/app/src/main/res/layout/bookmarks_databaseview_coordinatorlayout.xml @@ -22,7 +22,7 @@ When it is specified the theme should include `true` to make the status bar a transparent, darkened overlay. --> diff --git a/app/src/main/res/layout/bookmarks_databaseview_item_linearlayout.xml b/app/src/main/res/layout/bookmarks_databaseview_item_linearlayout.xml index 3c91b373..bc00f778 100644 --- a/app/src/main/res/layout/bookmarks_databaseview_item_linearlayout.xml +++ b/app/src/main/res/layout/bookmarks_databaseview_item_linearlayout.xml @@ -19,7 +19,7 @@ along with Privacy Browser. If not, see . --> + + + - - - \ No newline at end of file diff --git a/app/src/main/res/layout/bookmarks_databaseview_spinner.xml b/app/src/main/res/layout/bookmarks_databaseview_spinner.xml index 804ac3ac..9750d591 100644 --- a/app/src/main/res/layout/bookmarks_databaseview_spinner.xml +++ b/app/src/main/res/layout/bookmarks_databaseview_spinner.xml @@ -20,6 +20,6 @@ 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 index 00000000..dbf367aa --- /dev/null +++ b/app/src/main/res/layout/edit_bookmark_databaseview_dialog.xml @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 index 00000000..af46e36f --- /dev/null +++ b/app/src/main/res/layout/edit_bookmark_databaseview_spinner_dropdown_item.xml @@ -0,0 +1,34 @@ + + + + + + \ 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 index 00000000..d06d7cfd --- /dev/null +++ b/app/src/main/res/layout/edit_bookmark_databaseview_spinner_item.xml @@ -0,0 +1,31 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/edit_bookmark_dialog.xml b/app/src/main/res/layout/edit_bookmark_dialog.xml index a1b723b7..47bb8f1f 100644 --- a/app/src/main/res/layout/edit_bookmark_dialog.xml +++ b/app/src/main/res/layout/edit_bookmark_dialog.xml @@ -53,7 +53,7 @@ tools:ignore="ContentDescription" /> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/edit_bookmark_folder_dialog.xml b/app/src/main/res/layout/edit_bookmark_folder_dialog.xml index 0ac31586..dae0cbbe 100644 --- a/app/src/main/res/layout/edit_bookmark_folder_dialog.xml +++ b/app/src/main/res/layout/edit_bookmark_folder_dialog.xml @@ -45,7 +45,7 @@ android:layout_marginEnd="10dp" > + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 256bddbe..36b265f9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -197,6 +197,10 @@ Bookmarks Database View All Folders Home Folder + Database ID: + Folder: + Parent folder: + Display order: Domains diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 8b5027a6..343cebaa 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -49,6 +49,7 @@ @color/white @color/bookmarks_spinner_color_selector_light @color/blue_750 + @color/edit_bookmark_spinner_color_selector_light @drawable/list_selector_light @color/blue_900 @color/blue_700 @@ -133,6 +134,7 @@ @color/gray_300 @color/bookmarks_spinner_color_selector_dark @color/blue_830 + @color/edit_bookmark_spinner_color_selector_dark @color/blue_600 @color/blue_400 @color/gray_850 diff --git a/build.gradle b/build.gradle index 477cd224..5aa4870f 100644 --- a/build.gradle +++ b/build.gradle @@ -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() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 92cecb13..7900fee9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 -- 2.45.2