X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=c6e065b18391a39bbd92e453f30640a9f3d4f405;hp=868b6d71a7c78b033e8980858410207a0e362755;hb=55169899d6454cd57e40d32a792735df51caee85;hpb=33bd447a83bd3d763ee26bbb3a3f4adb074776ed 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 868b6d71..c6e065b1 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -24,7 +24,6 @@ package com.stoutner.privacybrowser.activities; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; -import android.app.DialogFragment; import android.app.DownloadManager; import android.app.SearchManager; import android.content.ActivityNotFoundException; @@ -55,26 +54,6 @@ import android.os.Handler; import android.preference.PreferenceManager; 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; -import android.support.v4.app.FragmentManager; -import android.support.v4.content.ContextCompat; -// `ShortcutInfoCompat`, `ShortcutManagerCompat`, and `IconCompat` can be switched to the non-compat version once API >= 26. -import android.support.v4.content.pm.ShortcutInfoCompat; -import android.support.v4.content.pm.ShortcutManagerCompat; -import android.support.v4.graphics.drawable.IconCompat; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.app.AppCompatDialogFragment; -import android.support.v7.widget.Toolbar; import android.text.Editable; import android.text.Spanned; import android.text.TextWatcher; @@ -114,6 +93,23 @@ import android.widget.RadioButton; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.core.view.GravityCompat; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentManager; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.navigation.NavigationView; +import com.google.android.material.snackbar.Snackbar; + import com.stoutner.privacybrowser.BuildConfig; import com.stoutner.privacybrowser.R; import com.stoutner.privacybrowser.dialogs.AdConsentDialog; @@ -135,6 +131,7 @@ import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; import com.stoutner.privacybrowser.helpers.OrbotProxyHelper; import com.stoutner.privacybrowser.dialogs.DownloadFileDialog; import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog; +import com.stoutner.privacybrowser.views.NestedScrollWebView; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -158,9 +155,8 @@ import java.util.Set; // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21. public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, - CreateHomeScreenShortcutDialog.CreateHomeScreenShortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, - DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, - HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, + DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, + EditBookmarkFolderDialog.EditBookmarkFolderListener, HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener { // `darkTheme` is public static so it can be accessed from everywhere. @@ -171,13 +167,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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()`. + // `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcut()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`. public static Bitmap favoriteIconBitmap; // `favoriteIconDefaultBitmap` public static so it can be accessed from `PinnedMismatchDialog`. It is also used in `onCreate()` and `applyDomainSettings`. public static Bitmap favoriteIconDefaultBitmap; - // `formattedUrlString` is public static so it can be accessed from `AddDomainDialog`, `BookmarksActivity`, `DomainSettingsFragment`, `CreateBookmarkDialog`, and `PinnedMismatchDialog`. + // `formattedUrlString` is public static so it can be accessed from `AddDomainDialog`, `BookmarksActivity`, `DomainSettingsFragment`, `CreateBookmarkDialog`, + // and `PinnedMismatchDialog`. // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, `loadUrlFromTextBox()`, and `applyProxyThroughOrbot()`. public static String formattedUrlString; @@ -192,7 +189,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`. public static String orbotStatus; - // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`. It is also used in `onCreate()`. + // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog`. It is also used in `onCreate()`. public static String webViewTitle; // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`. It is also used in `applyDomainSettings()`. @@ -308,33 +305,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `ignorePinnedDomainInformation` is used in `onSslMismatchProceed()`, `applyDomainSettings()`, and `checkPinnedMismatch()`. private static boolean ignorePinnedDomainInformation; - // `supportFragmentManager` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateContextMenu()`, `onRequestPermissionResult()`, `viewSslCertificate()`, - // `applyAppSettings()`, and `checkPinnedMismatch()`. - private static FragmentManager supportFragmentManager; - + // The fragment manager is initialized in `onCreate()` and accessed from the static `checkPinnedMismatch()`. + private static FragmentManager fragmentManager; - // `appBar` is used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`. - private ActionBar appBar; // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`. private boolean navigatingHistory; - // `drawerLayout` is used in `onCreate()`, `onNewIntent()`, `onBackPressed()`, and `onRestart()`. - private DrawerLayout drawerLayout; - - // `rootCoordinatorLayout` is used in `onCreate()` and `applyAppSettings()`. - private CoordinatorLayout rootCoordinatorLayout; - // `mainWebView` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, and `applyProxyThroughOrbot()`. - private WebView mainWebView; + private NestedScrollWebView mainWebView; // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`. private FrameLayout fullScreenVideoFrameLayout; - // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `onRestart()`. - private SwipeRefreshLayout swipeRefreshLayout; - // `urlAppBarRelativeLayout` is used in `onCreate()` and `applyDomainSettings()`. private RelativeLayout urlAppBarRelativeLayout; @@ -414,11 +398,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `inFullScreenBrowsingMode` is used in `onCreate()`, `onConfigurationChanged()`, and `applyAppSettings()`. private boolean inFullScreenBrowsingMode; - // `hideSystemBarsOnFullscreen` is used in `onCreate()` and `applyAppSettings()`. - private boolean hideSystemBarsOnFullscreen; - - // `translucentNavigationBarOnFullscreen` is used in `onCreate()` and `applyAppSettings()`. - private boolean translucentNavigationBarOnFullscreen; + // Hide app bar is used in `onCreate()` and `applyAppSettings()`. + private boolean hideAppBar; // `reapplyDomainSettingsOnRestart` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, and `onAddDomain()`, . private boolean reapplyDomainSettingsOnRestart; @@ -453,20 +434,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`. private String privateDataDirectoryString; - // `findOnPageLinearLayout` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. - private LinearLayout findOnPageLinearLayout; - // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. private EditText findOnPageEditText; // `displayAdditionalAppBarIcons` is used in `onCreate()` and `onCreateOptionsMenu()`. private boolean displayAdditionalAppBarIcons; - // `drawerToggle` is used in `onCreate()`, `onPostCreate()`, `onConfigurationChanged()`, `onNewIntent()`, and `onNavigationItemSelected()`. - private ActionBarDrawerToggle drawerToggle; - - // `supportAppBar` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`. - private Toolbar supportAppBar; + // The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`. + private ActionBarDrawerToggle actionBarDrawerToggle; // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, `loadUrl()`, and `highlightUrlText()`. private EditText urlTextBox; @@ -490,9 +465,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `inputMethodManager` is used in `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `closeFindOnPage()`. private InputMethodManager inputMethodManager; - // `mainWebViewRelativeLayout` is used in `onCreate()` and `onNavigationItemSelected()`. - private RelativeLayout mainWebViewRelativeLayout; - // `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, // and `loadBookmarksFolder()`. private BookmarksDatabaseHelper bookmarksDatabaseHelper; @@ -561,26 +533,24 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook super.onCreate(savedInstanceState); // Set the content view. - setContentView(R.layout.main_drawerlayout); + setContentView(R.layout.main_framelayout); - // Get a handle for the resources and the support fragment manager. + // Get handles for views, resources, and managers. Resources resources = getResources(); - supportFragmentManager = getSupportFragmentManager(); - - // Get a handle for `inputMethodManager`. + fragmentManager = getSupportFragmentManager(); inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + Toolbar toolbar = findViewById(R.id.toolbar); - // `SupportActionBar` from `android.support.v7.app.ActionBar` must be used until the minimum API is >= 21. - supportAppBar = findViewById(R.id.app_bar); - setSupportActionBar(supportAppBar); - appBar = getSupportActionBar(); + // Set the action bar. `SupportActionBar` must be used until the minimum API is >= 21. + setSupportActionBar(toolbar); + ActionBar actionBar = getSupportActionBar(); - // This is needed to get rid of the Android Studio warning that `appBar` might be null. - assert appBar != null; + // This is needed to get rid of the Android Studio warning that the action bar might be null. + assert actionBar != null; // Add the custom `url_app_bar` layout, which shows the favorite icon and the URL text bar. - appBar.setCustomView(R.layout.url_app_bar); - appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); + actionBar.setCustomView(R.layout.url_app_bar); + actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23. redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_a700)); @@ -650,17 +620,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Register `orbotStatusBroadcastReceiver` on `this` context. this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS")); - // Get handles for views that need to be accessed. - drawerLayout = findViewById(R.id.drawerlayout); - rootCoordinatorLayout = findViewById(R.id.root_coordinatorlayout); + // Get handles for views that need to be modified. + FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout); + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout); + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); + mainWebView = findViewById(R.id.main_webview); 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); @@ -693,16 +663,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the create new bookmark folder FAB to display an alert dialog. createBookmarkFolderFab.setOnClickListener(v -> { - // Show the `CreateBookmarkFolderDialog` `AlertDialog` and name the instance `@string/create_folder`. - AppCompatDialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog(); - createBookmarkFolderDialog.show(supportFragmentManager, resources.getString(R.string.create_folder)); + // Show the create bookmark folder dialog and name the instance `@string/create_folder`. + DialogFragment createBookmarkFolderDialog = new CreateBookmarkFolderDialog(); + createBookmarkFolderDialog.show(fragmentManager, resources.getString(R.string.create_folder)); }); // Set the create new bookmark FAB to display an alert dialog. createBookmarkFab.setOnClickListener(view -> { - // Show the `CreateBookmarkDialog` `AlertDialog` and name the instance `@string/create_bookmark`. - AppCompatDialogFragment createBookmarkDialog = new CreateBookmarkDialog(); - createBookmarkDialog.show(supportFragmentManager, resources.getString(R.string.create_bookmark)); + // Show the create bookmark dialog and name the instance `@string/create_bookmark`. + DialogFragment createBookmarkDialog = new CreateBookmarkDialog(); + createBookmarkDialog.show(fragmentManager, resources.getString(R.string.create_bookmark)); }); // Create a double-tap listener to toggle full-screen mode. @@ -711,66 +681,47 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public boolean onDoubleTap(MotionEvent event) { if (fullScreenBrowsingModeEnabled) { // Only process the double-tap if full screen browsing mode is enabled. - // Toggle `inFullScreenBrowsingMode`. + // Toggle the full screen browsing mode tracker. inFullScreenBrowsingMode = !inFullScreenBrowsingMode; + // Toggle the full screen browsing mode. if (inFullScreenBrowsingMode) { // Switch to full screen mode. - // Hide the `appBar`. - appBar.hide(); + // Hide the app bar if specified. + if (hideAppBar) { + actionBar.hide(); + } // Hide the banner ad in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { - // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. AdHelper.hideAd(findViewById(R.id.adview)); } - // Modify the system bars. - if (hideSystemBarsOnFullscreen) { // Hide everything. - // Remove the translucent overlays. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - - // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command. - drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); - - /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. - * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. - * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. - */ - rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - - // Set `rootCoordinatorLayout` to fill the whole screen. - rootCoordinatorLayout.setFitsSystemWindows(false); - } else { // Hide everything except the status and navigation bars. - // Set `rootCoordinatorLayout` to fit under the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(false); - - // There is an Android Support Library bug that causes a scrim to print on the right side of the `Drawer Layout` when the navigation bar is displayed on the right of the screen. - if (translucentNavigationBarOnFullscreen) { - // Set the navigation bar to be translucent. - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - } - } + // Remove the translucent status flag. This is necessary so the root frame layout can fill the entire screen. + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + + /* Hide the system bars. + * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. + * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. + * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. + */ + rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } else { // Switch to normal viewing mode. - // Show the `appBar`. - appBar.show(); + // Show the app bar. + actionBar.show(); - // Show the `BannerAd` in the free flavor. + // Show the banner ad in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { - // Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. + // Reload the ad. AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id)); } - // Remove the translucent navigation bar flag if it is set. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + // Remove the `SYSTEM_UI` flags from the root frame layout. + rootFrameLayout.setSystemUiVisibility(0); - // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. + // Add the translucent status flag. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - - // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. - rootCoordinatorLayout.setSystemUiVisibility(0); - - // Constrain `rootCoordinatorLayout` inside the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(true); } // Consume the double-tap. @@ -782,9 +733,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); // Pass all touch events on `mainWebView` through `gestureDetector` to check for double-taps. - mainWebView.setOnTouchListener((View v, MotionEvent event) -> { + mainWebView.setOnTouchListener((View view, MotionEvent event) -> { // Call `performClick()` on the view, which is required for accessibility. - v.performClick(); + view.performClick(); // Send the `event` to `gestureDetector`. return gestureDetector.onTouchEvent(event); @@ -847,9 +798,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); // Implement swipe to refresh. - swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); swipeRefreshLayout.setOnRefreshListener(() -> mainWebView.reload()); + // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. + swipeRefreshLayout.setProgressViewOffset(false, swipeRefreshLayout.getProgressViewStartOffset() - 10, swipeRefreshLayout.getProgressViewEndOffset()); + // Set the swipe to refresh color according to the theme. if (darkTheme) { swipeRefreshLayout.setColorSchemeResources(R.color.blue_600); @@ -921,12 +874,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME)); // Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`. - AppCompatDialogFragment editFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId); - editFolderDialog.show(supportFragmentManager, resources.getString(R.string.edit_folder)); + DialogFragment editBookmarkFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId); + editBookmarkFolderDialog.show(fragmentManager, resources.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(supportFragmentManager, resources.getString(R.string.edit_bookmark)); + DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId); + editBookmarkDialog.show(fragmentManager, resources.getString(R.string.edit_bookmark)); } // Consume the event. @@ -992,8 +945,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } }); - // drawerToggle creates the hamburger icon at the start of the AppBar. - drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, supportAppBar, R.string.open_navigation_drawer, R.string.close_navigation_drawer); + // Create the hamburger icon at the start of the AppBar. + actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer); // Get a handle for the progress bar. final ProgressBar progressBar = findViewById(R.id.progress_bar); @@ -1072,7 +1025,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Enter full screen video. @Override - public void onShowCustomView(View view, CustomViewCallback callback) { + public void onShowCustomView(View video, CustomViewCallback callback) { // Set the full screen video flag. displayingFullScreenVideo = true; @@ -1082,26 +1035,34 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook AdHelper.pauseAd(findViewById(R.id.adview)); } - // Remove the translucent overlays. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // Hide the keyboard. + inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0); + + // Hide the main content relative layout. + mainContentRelativeLayout.setVisibility(View.GONE); // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command. drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); - /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + // Remove the translucent status flag. This is necessary so the root frame layout can fill the entire screen. + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + + /* Hide the system bars. + * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. */ - rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - - // Set `rootCoordinatorLayout` to fill the entire screen. - rootCoordinatorLayout.setFitsSystemWindows(false); + rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); // Disable the sliding drawers. drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - // Add `view` to `fullScreenVideoFrameLayout` and display it on the screen. - fullScreenVideoFrameLayout.addView(view); + // Add the video view to the full screen video frame layout. + fullScreenVideoFrameLayout.addView(video); + + // Show the full screen video frame layout. fullScreenVideoFrameLayout.setVisibility(View.VISIBLE); } @@ -1111,73 +1072,52 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Unset the full screen video flag. displayingFullScreenVideo = false; - // Hide `fullScreenVideoFrameLayout`. + // Remove all the views from the full screen video frame layout. fullScreenVideoFrameLayout.removeAllViews(); + + // Hide the full screen video frame layout. fullScreenVideoFrameLayout.setVisibility(View.GONE); // Enable the sliding drawers. drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); + // Show the main content relative layout. + mainContentRelativeLayout.setVisibility(View.VISIBLE); + // Apply the appropriate full screen mode the `SYSTEM_UI` flags. if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode. - if (hideSystemBarsOnFullscreen) { // Hide everything. - // Remove the translucent navigation setting if it is currently flagged. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - - // Remove the translucent status bar overlay. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - - // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command. - drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); - - /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. - * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. - * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. - */ - rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } else { // Hide everything except the status and navigation bars. - // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. - rootCoordinatorLayout.setSystemUiVisibility(0); - - // Add the translucent status flag if it is unset. - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - - if (translucentNavigationBarOnFullscreen) { - // Set the navigation bar to be translucent. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - } else { - // Set the navigation bar to be black. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - } - } - } else { // Switch to normal viewing mode. - // Show the `appBar` if `findOnPageLinearLayout` is not visible. - if (findOnPageLinearLayout.getVisibility() == View.GONE) { - appBar.show(); + // Hide the app bar if specified. + if (hideAppBar) { + actionBar.hide(); } - // Show the `BannerAd` in the free flavor. + // Hide the banner ad in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { - // Initialize the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. - AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id)); + AdHelper.hideAd(findViewById(R.id.adview)); } - // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. - rootCoordinatorLayout.setSystemUiVisibility(0); - - // Remove the translucent navigation bar flag if it is set. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + // Remove the translucent status flag. This is necessary so the root frame layout can fill the entire screen. + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + + /* Hide the system bars. + * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. + * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. + * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. + */ + rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } else { // Switch to normal viewing mode. + // Remove the `SYSTEM_UI` flags from the root frame layout. + rootFrameLayout.setSystemUiVisibility(0); - // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. + // Add the translucent status flag. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - - // Constrain `rootCoordinatorLayout` inside the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(true); } - // Show the ad if this is the free flavor. - if (BuildConfig.FLAVOR.contentEquals("free")) { - // Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. + // Reload the ad for the free flavor if not in full screen mode. + if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) { + // Reload the ad. AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id)); } } @@ -1224,17 +1164,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. - downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); + downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location)); } else { // Show the permission request directly. // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); } } else { // The storage permission has already been granted. // Get a handle for the download file alert dialog. - AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength); + DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength); // Show the download file alert dialog. - downloadFileDialogFragment.show(supportFragmentManager, getString(R.string.download)); + downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); } } }); @@ -1611,8 +1551,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook httpAuthHandler = handler; // Display the HTTP authentication dialog. - AppCompatDialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm); - httpAuthenticationDialogFragment.show(supportFragmentManager, getString(R.string.http_authentication)); + DialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm); + httpAuthenticationDialogFragment.show(fragmentManager, getString(R.string.http_authentication)); } // Update the URL in urlTextBox when the page starts to load. @@ -1642,7 +1582,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook mainWebView.setVisibility(View.INVISIBLE); } - // Hide the keyboard. `0` indicates no additional flags. + // Hide the keyboard. inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0); // Check to see if Privacy Browser is waiting on Orbot. @@ -1819,8 +1759,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook sslErrorHandler = handler; // Display the SSL error `AlertDialog`. - AppCompatDialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error); - sslCertificateErrorDialogFragment.show(supportFragmentManager, getString(R.string.ssl_certificate_error)); + DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error); + sslCertificateErrorDialogFragment.show(fragmentManager, getString(R.string.ssl_certificate_error)); } } }); @@ -1888,6 +1828,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Load the URL. loadUrl(formattedUrlString); + // Get a handle for the drawer layout. + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + // Close the navigation drawer if it is open. if (drawerLayout.isDrawerVisible(GravityCompat.START)) { drawerLayout.closeDrawer(GravityCompat.START); @@ -1957,6 +1900,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the bookmarks drawer if returning from the Bookmarks activity. if (restartFromBookmarksActivity) { + // Get a handle for the drawer layout. + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + // Close the bookmarks drawer. drawerLayout.closeDrawer(GravityCompat.END); @@ -1983,12 +1929,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Resume `mainWebView`. mainWebView.onResume(); - // Resume the adView for the free flavor. - if (BuildConfig.FLAVOR.contentEquals("free")) { - // Resume the ad. - AdHelper.resumeAd(findViewById(R.id.adview)); - } - // Display a message to the user if waiting for Orbot. if (waitingForOrbot && !orbotStatus.equals("ON")) { // Disable the wide view port so that the waiting for Orbot text is displayed correctly. @@ -1998,18 +1938,24 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook mainWebView.loadData(waitingForOrbotHtmlString, "text/html", null); } - if (displayingFullScreenVideo) { - // Remove the translucent overlays. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + if (displayingFullScreenVideo || inFullScreenBrowsingMode) { + // Get a handle for the root frame layouts. + FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout); - // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command. - drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + // Remove the translucent status flag. This is necessary so the root frame layout can fill the entire screen. + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + /* Hide the system bars. + * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. */ - rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } else if (BuildConfig.FLAVOR.contentEquals("free")) { // Resume the adView for the free flavor. + // Resume the ad. + AdHelper.resumeAd(findViewById(R.id.adview)); } } @@ -2118,6 +2064,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public boolean onPrepareOptionsMenu(Menu menu) { + // Get a handle for the swipe refresh layout. + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); + // Get handles for the menu items. MenuItem addOrEditDomain = menu.findItem(R.id.add_or_edit_domain); MenuItem toggleFirstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies); @@ -2309,6 +2258,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // removeAllCookies is deprecated, but it is required for API < 21. @SuppressWarnings("deprecation") public boolean onOptionsItemSelected(MenuItem menuItem) { + // Reenter full screen browsing mode if it was interrupted by the options menu. + if (inFullScreenBrowsingMode) { + // Remove the translucent status flag. This is necessary so the root frame layout can fill the entire screen. + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + + FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout); + + /* Hide the system bars. + * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. + * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. + * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. + */ + rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + // Get the selected menu item ID. int menuItemId = menuItem.getItemId(); @@ -2483,21 +2449,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Do nothing because everything will be handled by `onDismissed()` below. }) .addCallback(new Snackbar.Callback() { + @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`. @Override public void onDismissed(Snackbar snackbar, int event) { switch (event) { - // The user pushed the `Undo` button. + // The user pushed the undo button. case Snackbar.Callback.DISMISS_EVENT_ACTION: // Do nothing. break; - // The `Snackbar` was dismissed without the `Undo` button being pushed. + // The snackbar was dismissed without the undo button being pushed. default: // `cookieManager.removeAllCookie()` varies by SDK. if (Build.VERSION.SDK_INT < 21) { cookieManager.removeAllCookie(); } else { - // `null` indicates no callback. cookieManager.removeAllCookies(null); } } @@ -2512,15 +2478,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Do nothing because everything will be handled by `onDismissed()` below. }) .addCallback(new Snackbar.Callback() { + @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`. @Override public void onDismissed(Snackbar snackbar, int event) { switch (event) { - // The user pushed the `Undo` button. + // The user pushed the undo button. case Snackbar.Callback.DISMISS_EVENT_ACTION: // Do nothing. break; - // The `Snackbar` was dismissed without the `Undo` button being pushed. + // The snackbar was dismissed without the undo button being pushed. default: // Delete the DOM Storage. WebStorage webStorage = WebStorage.getInstance(); @@ -2567,15 +2534,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Do nothing because everything will be handled by `onDismissed()` below. }) .addCallback(new Snackbar.Callback() { + @SuppressLint("SwitchIntDef") // Ignore the lint warning about not handling the other possible events as they are covered by `default:`. @Override public void onDismissed(Snackbar snackbar, int event) { switch (event) { - // The user pushed the `Undo` button. + // The user pushed the undo button. case Snackbar.Callback.DISMISS_EVENT_ACTION: // Do nothing. break; - // The `Snackbar` was dismissed without the `Undo` button being pushed. + // The snackbar was dismissed without the `Undo` button being pushed. default: // Delete the form data. WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext()); @@ -2793,6 +2761,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.swipe_to_refresh: + // Get a handle for the swipe refresh layout. + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); + // Toggle swipe to refresh. swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled()); return true; @@ -2836,13 +2807,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.find_on_page: - // Hide the URL app bar. - supportAppBar.setVisibility(View.GONE); + // Get a handle for the views. + Toolbar toolbar = findViewById(R.id.toolbar); + LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); + + // Hide the toolbar. + toolbar.setVisibility(View.GONE); - // Show the Find on Page `RelativeLayout`. + // Show the find on page linear layout. findOnPageLinearLayout.setVisibility(View.VISIBLE); - // Display the keyboard. We have to wait 200 ms before running the command to work around a bug in Android. + // Display the keyboard. The app must 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(() -> { // Set the focus on `findOnPageEditText`. @@ -2895,11 +2870,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.add_to_homescreen: - // Show the alert dialog. - AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog(); - createHomeScreenShortcutDialogFragment.show(supportFragmentManager, getString(R.string.create_shortcut)); + // Instantiate the create home screen shortcut dialog. + DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(mainWebView.getTitle(), formattedUrlString, favoriteIconBitmap); - //Everything else will be handled by the alert dialog and the associated listener below. + // Show the create home screen shortcut dialog. + createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); return true; case R.id.proxy_through_orbot: @@ -2923,7 +2898,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case R.id.ad_consent: // Display the ad consent dialog. DialogFragment adConsentDialogFragment = new AdConsentDialog(); - adConsentDialogFragment.show(getFragmentManager(), getString(R.string.ad_consent)); + adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent)); return true; default: @@ -2973,9 +2948,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the `WebBackForwardList`. WebBackForwardList webBackForwardList = mainWebView.copyBackForwardList(); - // Show the `UrlHistoryDialog` `AlertDialog` and name this instance `R.string.history`. `this` is the `Context`. - AppCompatDialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(this, webBackForwardList); - urlHistoryDialogFragment.show(supportFragmentManager, getString(R.string.history)); + // Show the URL history dialog and name this instance `R.string.history`. + DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(this, webBackForwardList); + urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history)); break; case R.id.requests: @@ -3156,9 +3131,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Clear `customHeaders`. customHeaders.clear(); - // Detach all views from `mainWebViewRelativeLayout`. - mainWebViewRelativeLayout.removeAllViews(); - // Destroy the internal state of `mainWebView`. mainWebView.destroy(); @@ -3188,6 +3160,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook break; } + // Get a handle for the drawer layout. + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + // Close the navigation drawer. drawerLayout.closeDrawer(GravityCompat.START); return true; @@ -3195,14 +3170,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onPostCreate(Bundle savedInstanceState) { + // Run the default commands. super.onPostCreate(savedInstanceState); - // Sync the state of the DrawerToggle after onRestoreInstanceState has finished. - drawerToggle.syncState(); + // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished. This creates the navigation drawer icon. + actionBarDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { + // Run the default commands. super.onConfigurationChanged(newConfig); // Get the status bar pixel size. @@ -3237,8 +3214,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook final String imageUrl; final String linkUrl; - // Get a handle for the `ClipboardManager`. + // Get a handle for the the clipboard and fragment managers. final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + FragmentManager fragmentManager = getSupportFragmentManager(); // Remove the lint errors below that `clipboardManager` might be `null`. assert clipboardManager != null; @@ -3287,17 +3265,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_FILE); // Show the download location permission alert dialog. The permission will be requested when the the dialog is closed. - downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); + downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location)); } else { // Show the permission request directly. // Request the permission. The download dialog will be launched by `onRequestPermissionResult()`. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE); } } else { // The storage permission has already been granted. // Get a handle for the download file alert dialog. - AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(linkUrl, "none", -1); + DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(linkUrl, "none", -1); // Show the download file alert dialog. - downloadFileDialogFragment.show(supportFragmentManager, getString(R.string.download)); + downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); } } return false; @@ -3387,17 +3365,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. - downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); + downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location)); } else { // Show the permission request directly. // Request the permission. The download dialog will be launched by `onRequestPermissionResult(). ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); } } else { // The storage permission has already been granted. // Get a handle for the download image alert dialog. - AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); + DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); // Show the download image alert dialog. - downloadImageDialogFragment.show(supportFragmentManager, getString(R.string.download)); + downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); } } return false; @@ -3461,17 +3439,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook DialogFragment downloadLocationPermissionDialogFragment = DownloadLocationPermissionDialog.downloadType(DownloadLocationPermissionDialog.DOWNLOAD_IMAGE); // Show the download location permission alert dialog. The permission will be requested when the dialog is closed. - downloadLocationPermissionDialogFragment.show(getFragmentManager(), getString(R.string.download_location)); + downloadLocationPermissionDialogFragment.show(fragmentManager, getString(R.string.download_location)); } else { // Show the permission request directly. // Request the permission. The download dialog will be launched by `onRequestPermissionResult(). ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_IMAGE_REQUEST_CODE); } } else { // The storage permission has already been granted. // Get a handle for the download image alert dialog. - AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); + DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); // Show the download image alert dialog. - downloadImageDialogFragment.show(supportFragmentManager, getString(R.string.download)); + downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); } } return false; @@ -3506,18 +3484,30 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onCreateBookmark(AppCompatDialogFragment dialogFragment) { - // Get the `EditTexts` from the `dialogFragment`. + public void onCreateBookmark(DialogFragment dialogFragment) { + // Get the views from the dialog fragment. 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`. + // Extract the strings from the edit texts. String bookmarkNameString = createBookmarkNameEditText.getText().toString(); String bookmarkUrlString = createBookmarkUrlEditText.getText().toString(); - // Convert the favoriteIcon Bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + // Get a copy of the favorite icon bitmap. + Bitmap favoriteIcon = favoriteIconBitmap; + + // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. + if ((favoriteIcon.getHeight() > 256) || (favoriteIcon.getWidth() > 256)) { + favoriteIcon = Bitmap.createScaledBitmap(favoriteIcon, 256, 256, true); + } + + // Create a favorite icon byte array output stream. ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); - favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); + + // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + favoriteIcon.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream); + + // Convert the favorite icon byte array stream to a byte array. byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray(); // Display the new bookmark below the current items in the (0 indexed) list. @@ -3529,7 +3519,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the bookmarks cursor with the current contents of this folder. bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder); - // Update the `ListView`. + // Update the list view. bookmarksCursorAdapter.changeCursor(bookmarksCursor); // Scroll to the new bookmark. @@ -3537,8 +3527,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onCreateBookmarkFolder(AppCompatDialogFragment dialogFragment) { - // Get handles for the views in `dialogFragment`. + public void onCreateBookmarkFolder(DialogFragment dialogFragment) { + // Get handles for the views in the dialog fragment. 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); @@ -3546,20 +3536,36 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get new folder name string. String folderNameString = createFolderNameEditText.getText().toString(); - // Get the new folder icon `Bitmap`. + // Create a folder icon bitmap. Bitmap folderIconBitmap; + + // Set the folder icon bitmap according to the dialog. if (defaultFolderIconRadioButton.isChecked()) { // Use the default folder icon. - // Get the default folder icon and convert it to a `Bitmap`. + // Get the default folder icon drawable. Drawable folderIconDrawable = folderIconImageView.getDrawable(); + + // Convert the folder icon drawable to a bitmap drawable. BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; + + // Convert the folder icon bitmap drawable to a bitmap. folderIconBitmap = folderIconBitmapDrawable.getBitmap(); - } else { // Use the `WebView` favorite icon. + } else { // Use the WebView favorite icon. + // Get a copy of the favorite icon bitmap. folderIconBitmap = favoriteIconBitmap; + + // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. + if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) { + folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true); + } } - // Convert `folderIconBitmap` to a byte array. `0` is for lossless compression (the only option for a PNG). + // Create a folder icon byte array output stream. ByteArrayOutputStream folderIconByteArrayOutputStream = new ByteArrayOutputStream(); + + // Convert the folder icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream); + + // Convert the folder icon byte array stream to a byte array. byte[] folderIconByteArray = folderIconByteArrayOutputStream.toByteArray(); // Move all the bookmarks down one in the display order. @@ -3582,28 +3588,138 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onCreateHomeScreenShortcut(AppCompatDialogFragment dialogFragment) { - // Get the shortcut name. - EditText shortcutNameEditText = dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext); - String shortcutNameString = shortcutNameEditText.getText().toString(); + public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId) { + // Get handles for the views from `dialogFragment`. + 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(); + String bookmarkUrlString = editBookmarkUrlEditText.getText().toString(); + + // Update the bookmark. + if (currentBookmarkIconRadioButton.isChecked()) { // Update the bookmark without changing the favorite icon. + bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString); + } else { // Update the bookmark using the `WebView` favorite icon. + // Get a copy of the favorite icon bitmap. + Bitmap favoriteIcon = favoriteIconBitmap; + + // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. + if ((favoriteIcon.getHeight() > 256) || (favoriteIcon.getWidth() > 256)) { + favoriteIcon = Bitmap.createScaledBitmap(favoriteIcon, 256, 256, true); + } + + // Create a favorite icon byte array output stream. + ByteArrayOutputStream newFavoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); + + // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + favoriteIcon.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream); + + // Convert the favorite icon byte array stream to a byte array. + byte[] newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray(); + + // Update the bookmark and the favorite icon. + bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, newFavoriteIconByteArray); + } + + // Update the bookmarks cursor with the current contents of this folder. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder); + + // Update the list view. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); + } + + @Override + public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId) { + // Get handles for the views from `dialogFragment`. + 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 defaultFolderIconImageView = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_imageview); + + // Get the new folder name. + String newFolderNameString = editFolderNameEditText.getText().toString(); + + // Check if the favorite icon has changed. + if (currentFolderIconRadioButton.isChecked()) { // Only the name has changed. + // Update the name in the database. + bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString); + } else if (!currentFolderIconRadioButton.isChecked() && newFolderNameString.equals(oldFolderNameString)) { // Only the icon has changed. + // Create the new folder icon Bitmap. + Bitmap folderIconBitmap; + + // Populate the new folder icon bitmap. + if (defaultFolderIconRadioButton.isChecked()) { + // Get the default folder icon drawable. + Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable(); + + // Convert the folder icon drawable to a bitmap drawable. + BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; + + // Convert the folder icon bitmap drawable to a bitmap. + folderIconBitmap = folderIconBitmapDrawable.getBitmap(); + } else { // Use the `WebView` favorite icon. + // Get a copy of the favorite icon bitmap. + folderIconBitmap = MainWebViewActivity.favoriteIconBitmap; + + // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. + if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) { + folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true); + } + } + + // Create a folder icon byte array output stream. + ByteArrayOutputStream newFolderIconByteArrayOutputStream = new ByteArrayOutputStream(); - // Convert the favorite icon bitmap to an `Icon`. `IconCompat` is required until API >= 26. - IconCompat favoriteIcon = IconCompat.createWithBitmap(favoriteIconBitmap); + // Convert the folder icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream); - // Setup the shortcut intent. - Intent shortcutIntent = new Intent(Intent.ACTION_VIEW); - shortcutIntent.setData(Uri.parse(formattedUrlString)); + // Convert the folder icon byte array stream to a byte array. + byte[] newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray(); - // Create a shortcut info builder. The shortcut name becomes the shortcut ID. - ShortcutInfoCompat.Builder shortcutInfoBuilder = new ShortcutInfoCompat.Builder(this, shortcutNameString); + // Update the folder icon in the database. + bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, newFolderIconByteArray); + } else { // The folder icon and the name have changed. + // Get the new folder icon `Bitmap`. + Bitmap folderIconBitmap; + if (defaultFolderIconRadioButton.isChecked()) { + // Get the default folder icon drawable. + Drawable folderIconDrawable = defaultFolderIconImageView.getDrawable(); - // Add the required fields to the shortcut info builder. - shortcutInfoBuilder.setIcon(favoriteIcon); - shortcutInfoBuilder.setIntent(shortcutIntent); - shortcutInfoBuilder.setShortLabel(shortcutNameString); + // Convert the folder icon drawable to a bitmap drawable. + BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; - // Request the pin. `ShortcutManagerCompat` can be switched to `ShortcutManager` once API >= 26. - ShortcutManagerCompat.requestPinShortcut(this, shortcutInfoBuilder.build(), null); + // Convert the folder icon bitmap drawable to a bitmap. + folderIconBitmap = folderIconBitmapDrawable.getBitmap(); + } else { // Use the `WebView` favorite icon. + // Get a copy of the favorite icon bitmap. + folderIconBitmap = MainWebViewActivity.favoriteIconBitmap; + + // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation. + if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) { + folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true); + } + } + + // Create a folder icon byte array output stream. + ByteArrayOutputStream newFolderIconByteArrayOutputStream = new ByteArrayOutputStream(); + + // Convert the folder icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG). + folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFolderIconByteArrayOutputStream); + + // Convert the folder icon byte array stream to a byte array. + byte[] newFolderIconByteArray = newFolderIconByteArrayOutputStream.toByteArray(); + + // Update the folder name and icon in the database. + bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString, newFolderIconByteArray); + } + + // Update the bookmarks cursor with the current contents of this folder. + bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder); + + // Update the `ListView`. + bookmarksCursorAdapter.changeCursor(bookmarksCursor); } @Override @@ -3623,16 +3739,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + // Get a handle for the fragment manager. + FragmentManager fragmentManager = getSupportFragmentManager(); + switch (requestCode) { case DOWNLOAD_FILE_REQUEST_CODE: // Show the download file alert dialog. When the dialog closes, the correct command will be used based on the permission status. - AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(downloadUrl, downloadContentDisposition, downloadContentLength); + DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(downloadUrl, downloadContentDisposition, downloadContentLength); // On API 23, displaying the fragment must be delayed or the app will crash. if (Build.VERSION.SDK_INT == 23) { - new Handler().postDelayed(() -> downloadFileDialogFragment.show(supportFragmentManager, getString(R.string.download)), 500); + new Handler().postDelayed(() -> downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)), 500); } else { - downloadFileDialogFragment.show(supportFragmentManager, getString(R.string.download)); + downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); } // Reset the download variables. @@ -3643,13 +3762,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook case DOWNLOAD_IMAGE_REQUEST_CODE: // Show the download image alert dialog. When the dialog closes, the correct command will be used based on the permission status. - AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(downloadImageUrl); + DialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(downloadImageUrl); // On API 23, displaying the fragment must be delayed or the app will crash. if (Build.VERSION.SDK_INT == 23) { - new Handler().postDelayed(() -> downloadImageDialogFragment.show(supportFragmentManager, getString(R.string.download)), 500); + new Handler().postDelayed(() -> downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)), 500); } else { - downloadImageDialogFragment.show(supportFragmentManager, getString(R.string.download)); + downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); } // Reset the image URL variable. @@ -3659,7 +3778,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onDownloadImage(AppCompatDialogFragment dialogFragment, String imageUrl) { + public void onDownloadImage(DialogFragment dialogFragment, String imageUrl) { // Download the image if it has an HTTP or HTTPS URI. if (imageUrl.startsWith("http")) { // Get a handle for the system `DOWNLOAD_SERVICE`. @@ -3711,7 +3830,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onDownloadFile(AppCompatDialogFragment dialogFragment, String downloadUrl) { + public void onDownloadFile(DialogFragment dialogFragment, String downloadUrl) { // Download the file if it has an HTTP or HTTPS URI. if (downloadUrl.startsWith("http")) { // Get a handle for the system `DOWNLOAD_SERVICE`. @@ -3762,99 +3881,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - @Override - public void onSaveBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId) { - // Get handles for the views from `dialogFragment`. - 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(); - String bookmarkUrlString = editBookmarkUrlEditText.getText().toString(); - - // Update the bookmark. - if (currentBookmarkIconRadioButton.isChecked()) { // Update the bookmark without changing the favorite icon. - bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString); - } else { // Update the bookmark using the `WebView` favorite icon. - // Convert the favorite icon to a byte array. `0` is for lossless compression (the only option for a PNG). - ByteArrayOutputStream newFavoriteIconByteArrayOutputStream = new ByteArrayOutputStream(); - favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream); - byte[] newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray(); - - // Update the bookmark and the favorite icon. - bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString, newFavoriteIconByteArray); - } - - // Update the bookmarks cursor with the current contents of this folder. - bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder); - - // Update the `ListView`. - bookmarksCursorAdapter.changeCursor(bookmarksCursor); - } - - @Override - public void onSaveBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId) { - // Get handles for the views from `dialogFragment`. - 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(); - - // Check if the favorite icon has changed. - if (currentFolderIconRadioButton.isChecked()) { // Only the name has changed. - // Update the name in the database. - bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString); - } else if (!currentFolderIconRadioButton.isChecked() && newFolderNameString.equals(oldFolderNameString)) { // Only the icon has changed. - // Get the new folder icon `Bitmap`. - Bitmap folderIconBitmap; - if (defaultFolderIconRadioButton.isChecked()) { - // Get the default folder icon and convert it to a `Bitmap`. - Drawable folderIconDrawable = folderIconImageView.getDrawable(); - BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; - folderIconBitmap = folderIconBitmapDrawable.getBitmap(); - } else { // Use the `WebView` favorite icon. - folderIconBitmap = favoriteIconBitmap; - } - - // Convert the folder `Bitmap` to a byte array. `0` is for lossless compression (the only option for a PNG). - ByteArrayOutputStream folderIconByteArrayOutputStream = new ByteArrayOutputStream(); - folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream); - byte[] folderIconByteArray = folderIconByteArrayOutputStream.toByteArray(); - - // 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`. - Bitmap folderIconBitmap; - if (defaultFolderIconRadioButton.isChecked()) { - // Get the default folder icon and convert it to a `Bitmap`. - Drawable folderIconDrawable = folderIconImageView.getDrawable(); - BitmapDrawable folderIconBitmapDrawable = (BitmapDrawable) folderIconDrawable; - folderIconBitmap = folderIconBitmapDrawable.getBitmap(); - } else { // Use the `WebView` favorite icon. - folderIconBitmap = MainWebViewActivity.favoriteIconBitmap; - } - - // Convert the folder `Bitmap` to a byte array. `0` is for lossless compression (the only option for a PNG). - ByteArrayOutputStream folderIconByteArrayOutputStream = new ByteArrayOutputStream(); - folderIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, folderIconByteArrayOutputStream); - byte[] folderIconByteArray = folderIconByteArrayOutputStream.toByteArray(); - - // Update the folder name and icon in the database. - bookmarksDatabaseHelper.updateFolder(selectedFolderDatabaseId, oldFolderNameString, newFolderNameString, folderIconByteArray); - } - - // Update the bookmarks cursor with the current contents of this folder. - bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder); - - // Update the `ListView`. - bookmarksCursorAdapter.changeCursor(bookmarksCursor); - } - @Override public void onHttpAuthenticationCancel() { // Cancel the `HttpAuthHandler`. @@ -3862,7 +3888,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onHttpAuthenticationProceed(AppCompatDialogFragment dialogFragment) { + public void onHttpAuthenticationProceed(DialogFragment dialogFragment) { // Get handles for the `EditTexts`. EditText usernameEditText = dialogFragment.getDialog().findViewById(R.id.http_authentication_username); EditText passwordEditText = dialogFragment.getDialog().findViewById(R.id.http_authentication_password); @@ -3874,7 +3900,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook public void viewSslCertificate(View view) { // Show the `ViewSslCertificateDialog` `AlertDialog` and name this instance `@string/view_ssl_certificate`. DialogFragment viewSslCertificateDialogFragment = new ViewSslCertificateDialog(); - viewSslCertificateDialogFragment.show(getFragmentManager(), getString(R.string.view_ssl_certificate)); + viewSslCertificateDialogFragment.show(getSupportFragmentManager(), getString(R.string.view_ssl_certificate)); } @Override @@ -3931,6 +3957,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Override `onBackPressed` to handle the navigation drawer and `mainWebView`. @Override public void onBackPressed() { + // Get a handle for the drawer layout. + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open. // Close the navigation drawer. drawerLayout.closeDrawer(GravityCompat.START); @@ -4064,19 +4093,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } public void closeFindOnPage(View view) { + // Get a handle for the views. + Toolbar toolbar = findViewById(R.id.toolbar); + LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); + // Delete the contents of `find_on_page_edittext`. findOnPageEditText.setText(null); // Clear the highlighted phrases. mainWebView.clearMatches(); - // Hide the Find on Page `RelativeLayout`. + // Hide the find on page linear layout. findOnPageLinearLayout.setVisibility(View.GONE); - // Show the URL app bar. - supportAppBar.setVisibility(View.VISIBLE); + // Show the toolbar. + toolbar.setVisibility(View.VISIBLE); - // Hide the keyboard so we can see the webpage. `0` indicates no additional flags. + // Hide the keyboard. inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0); } @@ -4089,10 +4122,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false); proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false); fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false); - hideSystemBarsOnFullscreen = sharedPreferences.getBoolean("hide_system_bars", false); - translucentNavigationBarOnFullscreen = sharedPreferences.getBoolean("translucent_navigation_bar", true); + hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true); downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false); + // Get handles for the views that need to be modified. `getSupportActionBar()` must be used until the minimum API >= 21. + FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout); + ActionBar actionBar = getSupportActionBar(); + + // Remove the incorrect lint warnings below that the action bar might be null. + assert actionBar != null; + // Apply the proxy through Orbot settings. applyProxyThroughOrbot(false); @@ -4103,67 +4142,56 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook customHeaders.remove("DNT"); } - // Apply the appropriate full screen mode the `SYSTEM_UI` flags. - if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode. - if (hideSystemBarsOnFullscreen) { // Hide everything. - // Remove the translucent navigation setting if it is currently flagged. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + // Set the app bar scrolling. + mainWebView.setNestedScrollingEnabled(sharedPreferences.getBoolean("scroll_app_bar", true)); - // Remove the translucent status bar overlay. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - - // Remove the translucent status bar overlay on the `Drawer Layout`, which is special and needs its own command. - drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + // Update the full screen browsing mode settings. + if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode. + // Update the visibility of the app bar, which might have changed in the settings. + if (hideAppBar) { + actionBar.hide(); + } else { + actionBar.show(); + } - /* SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. - * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. - * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. - */ - rootCoordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } else { // Hide everything except the status and navigation bars. - // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. - rootCoordinatorLayout.setSystemUiVisibility(0); + // Hide the banner ad in the free flavor. + if (BuildConfig.FLAVOR.contentEquals("free")) { + AdHelper.hideAd(findViewById(R.id.adview)); + } - // Add the translucent status flag if it is unset. - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // Remove the translucent status flag. This is necessary so the root frame layout can fill the entire screen. + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - if (translucentNavigationBarOnFullscreen) { - // Set the navigation bar to be translucent. - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - } else { - // Set the navigation bar to be black. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - } - } + /* Hide the system bars. + * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen. + * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar. + * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen. + * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown. + */ + rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } else { // Privacy Browser is not in full screen browsing mode. // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled. inFullScreenBrowsingMode = false; - // Show the `appBar` if `findOnPageLinearLayout` is not visible. - if (findOnPageLinearLayout.getVisibility() == View.GONE) { - appBar.show(); - } + // Show the app bar. + actionBar.show(); - // Show the `BannerAd` in the free flavor. + // Show the banner ad in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { - // Initialize the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations. - AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id)); + // Initialize the ads. If this isn't the first run, `loadAd()` will be automatically called instead. + AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), fragmentManager, getString(R.string.google_app_id), getString(R.string.ad_unit_id)); } - // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. - rootCoordinatorLayout.setSystemUiVisibility(0); + // Remove the `SYSTEM_UI` flags from the root frame layout. + rootFrameLayout.setSystemUiVisibility(0); - // Remove the translucent navigation bar flag if it is set. - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); - - // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. + // Add the translucent status flag. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - - // Constrain `rootCoordinatorLayout` inside the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(true); } } + // `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled. // The deprecated `.getDrawable()` must be used until the minimum API >= 21. @SuppressWarnings("deprecation") @@ -4211,6 +4239,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook favoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(favoriteIconBitmap, 64, 64, true)); } + // Get a handle for the swipe refresh layout. + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); + // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0); @@ -4579,6 +4610,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value)); String searchCustomUrlString = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value)); + // Get a handle for the action bar. `getSupportActionBar()` must be used until the minimum API >= 21. + ActionBar actionBar = getSupportActionBar(); + + // Remove the incorrect lint warning later that the action bar might be null. + assert actionBar != null; + // Set the homepage, search, and proxy options. if (proxyThroughOrbot) { // Set the Tor options. // Set `torHomepageString` as `homepage`. @@ -4601,9 +4638,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the `appBar` background to indicate proxying through Orbot is enabled. `this` refers to the context. if (darkTheme) { - appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30)); + actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30)); } else { - appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50)); + actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50)); } // Check to see if Orbot is ready. @@ -4641,9 +4678,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the default `appBar` background. `this` refers to the context. if (darkTheme) { - appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900)); + actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900)); } else { - appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_100)); + actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_100)); } // Reset `waitingForOrbot. @@ -4923,10 +4960,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook !currentWebsiteSslEndDateString.equals(pinnedSslEndDateString)))) { // Get a handle for the pinned mismatch alert dialog. - AppCompatDialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(pinnedSslCertificate, pinnedIpAddresses); + DialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(pinnedSslCertificate, pinnedIpAddresses); // Show the pinned mismatch alert dialog. - pinnedMismatchDialogFragment.show(supportFragmentManager, "Pinned Mismatch"); + pinnedMismatchDialogFragment.show(fragmentManager, "Pinned Mismatch"); } } }