From 8252c110e3a97bc83c5f14c446edde00dfef32c9 Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Fri, 6 Oct 2017 10:29:56 -0700 Subject: [PATCH] Partial bookmarks drawer implemention. --- .../activities/BookmarksActivity.java | 4 +- .../activities/MainWebViewActivity.java | 168 ++++++++++++++++-- .../bookmarks_activity_item_linearlayout.xml | 49 +++++ ...=> bookmarks_drawer_item_linearlayout.xml} | 5 +- app/src/main/res/layout/main_drawerlayout.xml | 50 ++++++ app/src/main/res/layout/navigation_header.xml | 2 +- 6 files changed, 258 insertions(+), 20 deletions(-) create mode 100644 app/src/main/res/layout/bookmarks_activity_item_linearlayout.xml rename app/src/main/res/layout/{bookmarks_item_linearlayout.xml => bookmarks_drawer_item_linearlayout.xml} (86%) 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 8a69e2be..6ea013c9 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksActivity.java @@ -132,7 +132,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma // Display the home arrow on `SupportActionBar`. appBar.setDisplayHomeAsUpEnabled(true); - // Initialize the database helper and the `ListView`. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`. + // Initialize the database helper. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`. // The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`. bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0); @@ -931,7 +931,7 @@ public class BookmarksActivity extends AppCompatActivity implements CreateBookma @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { // Inflate the individual item layout. `false` does not attach it to the root. - return getLayoutInflater().inflate(R.layout.bookmarks_item_linearlayout, parent, false); + return getLayoutInflater().inflate(R.layout.bookmarks_activity_item_linearlayout, parent, false); } @Override 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 7549f5ca..9dc39130 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -34,6 +34,8 @@ import android.content.SharedPreferences; import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -47,6 +49,7 @@ import android.print.PrintDocumentAdapter; import android.print.PrintManager; import android.support.annotation.NonNull; import android.support.design.widget.CoordinatorLayout; +import android.support.design.widget.FloatingActionButton; import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; @@ -71,6 +74,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.webkit.CookieManager; @@ -85,10 +89,13 @@ 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; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; @@ -102,6 +109,7 @@ import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog; import com.stoutner.privacybrowser.dialogs.PinnedSslCertificateMismatchDialog; import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog; import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog; +import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper; import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; import com.stoutner.privacybrowser.helpers.OrbotProxyHelper; import com.stoutner.privacybrowser.dialogs.DownloadFileDialog; @@ -328,6 +336,17 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`. private boolean pinnedDomainSslCertificate; + // `bookmarksDatabaseHelper` is used in `onCreate()` and `loadBookmarksFolder()`. + private BookmarksDatabaseHelper bookmarksDatabaseHelper; + + // `bookmarksListView` is used in `onCreate()` and `loadBookmarksFolder()`. + private ListView bookmarksListView; + + // `currentBookmarksFolder` is used in `onCreate()`, `onBackPressed()`, and `loadBookmarksFolder()`. + private String currentBookmarksFolder; + + // `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`. + private TextView bookmarksTitleTextView; @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. @@ -357,7 +376,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Get a handle for `inputMethodManager`. inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - // We need to use the `SupportActionBar` from `android.support.v7.app.ActionBar` until the minimum API is >= 21. + // `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21. supportAppBar = (Toolbar) findViewById(R.id.app_bar); setSupportActionBar(supportAppBar); appBar = getSupportActionBar(); @@ -446,6 +465,10 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // 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 createBookmarksFolderFab = (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); @@ -454,6 +477,17 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation urlAppBarRelativeLayout = (RelativeLayout) findViewById(R.id.url_app_bar_relativelayout); favoriteIconImageView = (ImageView) 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) { + createBookmarksFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_dark)); + createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_dark)); + bookmarksListView.setBackgroundColor(getResources().getColor(R.color.gray_850)); + } else { + createBookmarksFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_light)); + createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_light)); + bookmarksListView.setBackgroundColor(getResources().getColor(R.color.white)); + } + // Create a double-tap listener to toggle full-screen mode. final GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { // Override `onDoubleTap()`. All other events are handled using the default settings. @@ -606,8 +640,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation } }); - // `DrawerTitle` identifies the `DrawerLayout` in accessibility mode. + // `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); @@ -619,6 +654,49 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation final MenuItem navigationForwardMenuItem = navigationMenu.getItem(2); final MenuItem navigationHistoryMenuItem = navigationMenu.getItem(3); + // Initialize the bookmarks database helper. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`. + // The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`. + bookmarksDatabaseHelper = new BookmarksDatabaseHelper(this, null, null, 0); + + // Initialize `currentBookmarksFolder`. `""` is the home folder in the database. + currentBookmarksFolder = ""; + + // 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. + // Get the bookmark URL and assign it to `formattedUrlString`. + String bookmarkUrl = bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)); + + // Load the bookmark URL. + loadUrl(bookmarkUrl); + + // Close the bookmarks drawer. + drawerLayout.closeDrawer(GravityCompat.END); + } + + // Close the `Cursor`. + bookmarkCursor.close(); + } + }); + // The `DrawerListener` allows us to update the Navigation Menu. drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() { @Override @@ -2324,21 +2402,30 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Override `onBackPressed` to handle the navigation drawer and `mainWebView`. @Override public void onBackPressed() { - // Close the navigation drawer if it is available. GravityCompat.START is the drawer on the left on Left-to-Right layout text. - if (drawerLayout.isDrawerVisible(GravityCompat.START)) { + if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open. + // Close the navigation drawer. drawerLayout.closeDrawer(GravityCompat.START); - } else { - // Load the previous URL if available. - if (mainWebView.canGoBack()) { - // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded. - navigatingHistory = true; - - // Go back. - mainWebView.goBack(); - } else { - // Pass `onBackPressed()` to the system. - super.onBackPressed(); + } else if (drawerLayout.isDrawerVisible(GravityCompat.END)){ // The bookmarks drawer is open. + if (currentBookmarksFolder.isEmpty()) { // The home folder is displayed. + // close the bookmarks drawer. + drawerLayout.closeDrawer(GravityCompat.END); + } else { // A subfolder is displayed. + // Place the former parent folder in `currentFolder`. + currentBookmarksFolder = bookmarksDatabaseHelper.getParentFolder(currentBookmarksFolder); + + // Load the new folder. + loadBookmarksFolder(); } + + } else if (mainWebView.canGoBack()) { // There is at least one item in the `WebView` history. + // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded. + navigatingHistory = true; + + // Go back. + mainWebView.goBack(); + } else { // There isn't anything to do in Privacy Browser. + // Pass `onBackPressed()` to the system. + super.onBackPressed(); } } @@ -2967,4 +3054,55 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation urlTextBox.getText().setSpan(finalGrayColorSpan, endOfDomainName, urlString.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); } } + + private void loadBookmarksFolder() { + // Update `bookmarksCursor` with the contents of the bookmarks database for the current folder. + Cursor bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(currentBookmarksFolder); + + // Setup a `CursorAdapter`. `this` specifies the `Context`. `false` disables `autoRequery`. + CursorAdapter bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) { + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + // Inflate the individual item layout. `false` does not attach it to the root. + return getLayoutInflater().inflate(R.layout.bookmarks_drawer_item_linearlayout, parent, false); + } + + @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); + + // Get the favorite icon byte array from the `Cursor`. + byte[] favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON)); + + // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last. + Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length); + + // Display the bitmap in `bookmarkFavoriteIcon`. + bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap); + + // Get the bookmark name from the cursor and display it in `bookmarkNameTextView`. + String bookmarkNameString = cursor.getString(cursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); + bookmarkNameTextView.setText(bookmarkNameString); + + // Make the font bold for folders. + if (cursor.getInt(cursor.getColumnIndex(BookmarksDatabaseHelper.IS_FOLDER)) == 1) { + bookmarkNameTextView.setTypeface(Typeface.DEFAULT_BOLD); + } else { // Reset the font to default for normal bookmarks. + bookmarkNameTextView.setTypeface(Typeface.DEFAULT); + } + } + }; + + // Populate the `ListView` with the adapter. + bookmarksListView.setAdapter(bookmarksCursorAdapter); + + // Set the bookmarks drawer title. + if (currentBookmarksFolder.isEmpty()) { + bookmarksTitleTextView.setText(R.string.bookmarks); + } else { + bookmarksTitleTextView.setText(currentBookmarksFolder); + } + } } diff --git a/app/src/main/res/layout/bookmarks_activity_item_linearlayout.xml b/app/src/main/res/layout/bookmarks_activity_item_linearlayout.xml new file mode 100644 index 00000000..6cd123d1 --- /dev/null +++ b/app/src/main/res/layout/bookmarks_activity_item_linearlayout.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/bookmarks_item_linearlayout.xml b/app/src/main/res/layout/bookmarks_drawer_item_linearlayout.xml similarity index 86% rename from app/src/main/res/layout/bookmarks_item_linearlayout.xml rename to app/src/main/res/layout/bookmarks_drawer_item_linearlayout.xml index 9a27fb90..3009a352 100644 --- a/app/src/main/res/layout/bookmarks_item_linearlayout.xml +++ b/app/src/main/res/layout/bookmarks_drawer_item_linearlayout.xml @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with Privacy Browser. If not, see . --> + + tools:ignore="UseCompoundDrawables" > + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/navigation_header.xml b/app/src/main/res/layout/navigation_header.xml index b1229d8b..757cde42 100644 --- a/app/src/main/res/layout/navigation_header.xml +++ b/app/src/main/res/layout/navigation_header.xml @@ -21,8 +21,8 @@