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=d2881d7ebe8887c6d299ca1ec9b1b55cdc6b7e44;hp=ed25a37d28b3d7689ec18fa45285ff4ebe85de80;hb=cbf3ebe084eaf5004b74b7f1907b0a1e1a22791f;hpb=d0d1b8d83a433a323d01ab8c745ab385043ffc54 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 ed25a37d..d2881d7e 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -107,6 +107,7 @@ import androidx.core.content.res.ResourcesCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.viewpager.widget.ViewPager; import androidx.webkit.WebSettingsCompat; @@ -126,6 +127,7 @@ import com.stoutner.privacybrowser.asynctasks.PopulateBlocklists; import com.stoutner.privacybrowser.asynctasks.PrepareSaveDialog; import com.stoutner.privacybrowser.asynctasks.SaveUrl; import com.stoutner.privacybrowser.asynctasks.SaveWebpageImage; +import com.stoutner.privacybrowser.definitions.PendingDialog; import com.stoutner.privacybrowser.dialogs.AdConsentDialog; import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog; import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog; @@ -158,12 +160,16 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; + import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; + import java.text.NumberFormat; + import java.util.ArrayList; +import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -191,6 +197,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onRestart()`. public static boolean restartFromBookmarksActivity; + // Define the public static variables. + public static ArrayList pendingDialogsArrayList = new ArrayList<>(); + // `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. public static String currentBookmarksFolder; @@ -247,21 +256,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private ArrayList> ultraList; private ArrayList> ultraPrivacy; - // Declare the class variables - private BroadcastReceiver orbotStatusBroadcastReceiver; - private String webViewDefaultUserAgent; - private boolean incognitoModeEnabled; - private boolean fullScreenBrowsingModeEnabled; - private boolean inFullScreenBrowsingMode; - private boolean downloadWithExternalApp; - private boolean hideAppBar; - private boolean scrollAppBar; - private boolean loadingNewIntent; - private boolean reapplyDomainSettingsOnRestart; - private boolean reapplyAppSettingsOnRestart; - private boolean displayingFullScreenVideo; - private boolean waitingForProxy; - // The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`. private ActionBarDrawerToggle actionBarDrawerToggle; @@ -296,6 +290,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook private boolean sanitizeFacebookClickIds; private boolean sanitizeTwitterAmpRedirects; + // Declare the class variables + private BroadcastReceiver orbotStatusBroadcastReceiver; + private String webViewDefaultUserAgent; + private boolean incognitoModeEnabled; + private boolean fullScreenBrowsingModeEnabled; + private boolean inFullScreenBrowsingMode; + private boolean downloadWithExternalApp; + private boolean hideAppBar; + private boolean scrollAppBar; + private boolean bottomAppBar; + private boolean loadingNewIntent; + private boolean reapplyDomainSettingsOnRestart; + private boolean reapplyAppSettingsOnRestart; + private boolean displayingFullScreenVideo; + private boolean waitingForProxy; + + // Define the class variables. + private long lastScrollUpdate = 0; + // Declare the class views. private FrameLayout rootFrameLayout; private DrawerLayout drawerLayout; @@ -385,9 +398,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - // Get the screenshot preference. + // Get the preferences. String appTheme = sharedPreferences.getString("app_theme", getString(R.string.app_theme_default_value)); boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false); + bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false); // Get the theme entry values string array. String[] appThemeEntryValuesStringArray = getResources().getStringArray(R.array.app_theme_entry_values); @@ -423,7 +437,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook setTheme(R.style.PrivacyBrowser); // Set the content view. - setContentView(R.layout.main_framelayout); + if (bottomAppBar) { + setContentView(R.layout.main_framelayout_bottom_appbar); + } else { + setContentView(R.layout.main_framelayout_top_appbar); + } // Get handles for the views. rootFrameLayout = findViewById(R.id.root_framelayout); @@ -690,6 +708,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Resume the ad. AdHelper.resumeAd(adView); } + + // Show any pending dialogs. + for (int i = 0; i < pendingDialogsArrayList.size(); i++) { + // Get the pending dialog from the array list. + PendingDialog pendingDialog = pendingDialogsArrayList.get(i); + + // Show the pending dialog. + pendingDialog.dialogFragment.show(getSupportFragmentManager(), pendingDialog.tag); + } + + // Clear the pending dialogs array list. + pendingDialogsArrayList.clear(); } // `onStop()` runs after `onPause()`. It is used instead of `onPause()` so the commands are not called every time the screen is partially hidden. @@ -1392,7 +1422,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the menu checkbox. menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); - // Update the staus of Fanboy's Social Blocking List. + // Update the status of Fanboy's Social Blocking List. optionsFanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)); // Reload the current WebView. @@ -2115,7 +2145,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]}; // Add the blocklist versions to the intent. - aboutIntent.putExtra("blocklist_versions", blocklistVersions); + aboutIntent.putExtra(AboutActivity.BLOCKLIST_VERSIONS, blocklistVersions); // Make it so. startActivity(aboutIntent); @@ -3196,12 +3226,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Reset the waiting for proxy status. waitingForProxy = false; - // Get a handle for the waiting for proxy dialog. - DialogFragment waitingForProxyDialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.waiting_for_proxy_dialog)); + // Get a list of the current fragments. + List fragmentList = getSupportFragmentManager().getFragments(); + + // Check each fragment to see if it is a waiting for proxy dialog. Sometimes more than one is displayed. + for (int i = 0; i < fragmentList.size(); i++) { + // Get the fragment tag. + String fragmentTag = fragmentList.get(i).getTag(); - // Dismiss the waiting for proxy dialog if it is displayed. - if (waitingForProxyDialogFragment != null) { - waitingForProxyDialogFragment.dismiss(); + // Check to see if it is the waiting for proxy dialog. + if ((fragmentTag!= null) && fragmentTag.equals(getString(R.string.waiting_for_proxy_dialog))) { + // Dismiss the waiting for proxy dialog. + ((DialogFragment) fragmentList.get(i)).dismiss(); + } } // Reload existing URLs and load any URLs that are waiting for the proxy. @@ -3395,7 +3432,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); // Implement swipe to refresh. - swipeRefreshLayout.setOnRefreshListener(() -> currentWebView.reload()); + swipeRefreshLayout.setOnRefreshListener(() -> { + // Check the visibility of the bottom app bar. Sometimes it is hidden if the WebView is the same size as the visible screen. + if (bottomAppBar && scrollAppBar && (appBarLayout.getVisibility() == View.GONE)) { // The bottom app bar is currently hidden. + // Show the app bar. + appBarLayout.setVisibility(View.VISIBLE); + + // Disable the refreshing animation. + swipeRefreshLayout.setRefreshing(false); + } else { // A bottom app bar is not currently hidden. + // Reload the website. + currentWebView.reload(); + } + }); // Store the default progress view offsets for use later in `initializeWebView()`. defaultProgressViewStartOffset = swipeRefreshLayout.getProgressViewStartOffset(); @@ -3587,51 +3636,48 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply the proxy. applyProxy(false); - // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command. - CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); - AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); - AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams(); - AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams(); - - // Add the scrolling behavior to the layout parameters. - if (scrollAppBar) { - // Enable scrolling of the app bar. - swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); - toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); - findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); - tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); - } else { - // Disable scrolling of the app bar. - swipeRefreshLayoutParams.setBehavior(null); - toolbarLayoutParams.setScrollFlags(0); - findOnPageLayoutParams.setScrollFlags(0); - tabsLayoutParams.setScrollFlags(0); - - // Expand the app bar if it is currently collapsed. - appBarLayout.setExpanded(true); - } - - // Apply the modified layout parameters. - swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams); - toolbar.setLayoutParams(toolbarLayoutParams); - findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams); - tabsLinearLayout.setLayoutParams(tabsLayoutParams); + // Adjust the layout and scrolling parameters if the app bar is at the top of the screen. + if (!bottomAppBar) { + // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command. + CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); + AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams(); + AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams(); + AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams(); + + // Add the scrolling behavior to the layout parameters. + if (scrollAppBar) { + // Enable scrolling of the app bar. + swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); + toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); + findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); + tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); + } else { + // Disable scrolling of the app bar. + swipeRefreshLayoutParams.setBehavior(null); + toolbarLayoutParams.setScrollFlags(0); + findOnPageLayoutParams.setScrollFlags(0); + tabsLayoutParams.setScrollFlags(0); + + // Expand the app bar if it is currently collapsed. + appBarLayout.setExpanded(true); + } - // Set the app bar scrolling for each WebView. - for (int i = 0; i < webViewPagerAdapter.getCount(); i++) { - // Get the WebView tab fragment. - WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i); + // Set the app bar scrolling for each WebView. + for (int i = 0; i < webViewPagerAdapter.getCount(); i++) { + // Get the WebView tab fragment. + WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i); - // Get the fragment view. - View fragmentView = webViewTabFragment.getView(); + // Get the fragment view. + View fragmentView = webViewTabFragment.getView(); - // Only modify the WebViews if they exist. - if (fragmentView != null) { - // Get the nested scroll WebView from the tab fragment. - NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview); + // Only modify the WebViews if they exist. + if (fragmentView != null) { + // Get the nested scroll WebView from the tab fragment. + NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview); - // Set the app bar scrolling. - nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar); + // Set the app bar scrolling. + nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar); + } } } @@ -4282,8 +4328,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the waiting for proxy alert dialog. DialogFragment waitingForProxyDialogFragment = new WaitingForProxyDialog(); - // Display the waiting for proxy alert dialog. - waitingForProxyDialogFragment.show(getSupportFragmentManager(), getString(R.string.waiting_for_proxy_dialog)); + // Try to show the dialog. Sometimes the window is not yet active if returning from Settings. + try { + // Show the waiting for proxy alert dialog. + waitingForProxyDialogFragment.show(getSupportFragmentManager(), getString(R.string.waiting_for_proxy_dialog)); + } catch (Exception waitingForTorException) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(waitingForProxyDialogFragment, getString(R.string.waiting_for_proxy_dialog))); + } } } } catch (PackageManager.NameNotFoundException exception) { // Orbot is not installed. @@ -4292,8 +4344,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the Orbot not installed alert dialog. DialogFragment orbotNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode); - // Display the Orbot not installed alert dialog. - orbotNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog)); + // Try to show the dialog. Sometimes the window is not yet active if returning from Settings. + try { + // Display the Orbot not installed alert dialog. + orbotNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog)); + } catch (Exception orbotNotInstalledException) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(orbotNotInstalledDialogFragment, getString(R.string.proxy_not_installed_dialog))); + } } } break; @@ -4319,8 +4377,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get a handle for the waiting for proxy alert dialog. DialogFragment i2pNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode); - // Display the I2P not installed alert dialog. - i2pNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog)); + // Try to show the dialog. Sometimes the window is not yet active if returning from Settings. + try { + // Display the I2P not installed alert dialog. + i2pNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog)); + } catch (Exception i2pNotInstalledException) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(i2pNotInstalledDialogFragment, getString(R.string.proxy_not_installed_dialog))); + } } } break; @@ -4741,6 +4805,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add the new WebView page. webViewPagerAdapter.addPage(newTabNumber, webViewPager, url, moveToTab); + + // Show the app bar if it is at the bottom of the screen and the new tab is taking focus. + if (bottomAppBar && moveToTab) { + appBarLayout.setVisibility(View.VISIBLE); + } } public void closeTab(View view) { @@ -5238,19 +5307,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Hide the action bar. actionBar.hide(); - // Check to see if the app bar is normally scrolled. - if (scrollAppBar) { // The app bar is scrolled when it is displayed. - // Get the swipe refresh layout parameters. - CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); - - // Remove the off-screen scrolling layout. - swipeRefreshLayoutParams.setBehavior(null); - } else { // The app bar is not scrolled when it is displayed. - // Remove the padding from the top of the swipe refresh layout. - swipeRefreshLayout.setPadding(0, 0, 0, 0); - - // The swipe refresh circle must be moved above the now removed status bar location. - swipeRefreshLayout.setProgressViewOffset(false, -200, defaultProgressViewEndOffset); + // Set layout and scrolling parameters if the app bar is at the top of the screen. + if (!bottomAppBar) { + // Check to see if the app bar is normally scrolled. + if (scrollAppBar) { // The app bar is scrolled when it is displayed. + // Get the swipe refresh layout parameters. + CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); + + // Remove the off-screen scrolling layout. + swipeRefreshLayoutParams.setBehavior(null); + } else { // The app bar is not scrolled when it is displayed. + // Remove the padding from the top of the swipe refresh layout. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + + // The swipe refresh circle must be moved above the now removed status bar location. + swipeRefreshLayout.setProgressViewOffset(false, -200, defaultProgressViewEndOffset); + } } } @@ -5280,19 +5352,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Show the action bar. actionBar.show(); - // Check to see if the app bar is normally scrolled. - if (scrollAppBar) { // The app bar is scrolled when it is displayed. - // Get the swipe refresh layout parameters. - CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); - - // Add the off-screen scrolling layout. - swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); - } else { // The app bar is not scrolled when it is displayed. - // The swipe refresh layout must be manually moved below the app bar layout. - swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); - - // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. - swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); + // Set layout and scrolling parameters if the app bar is at the top of the screen. + if (!bottomAppBar) { + // Check to see if the app bar is normally scrolled. + if (scrollAppBar) { // The app bar is scrolled when it is displayed. + // Get the swipe refresh layout parameters. + CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams(); + + // Add the off-screen scrolling layout. + swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior()); + } else { // The app bar is not scrolled when it is displayed. + // The swipe refresh layout must be manually moved below the app bar layout. + swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); + + // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. + swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); + } } } @@ -5350,23 +5425,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the file name from the content disposition. String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl); - // Prevent the dialog from displaying if the app window is not visible. - // The download listener continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. - while (!activity.getWindow().isActive()) { - try { - // The window is not active. Wait 1 second. - wait(1000); - } catch (InterruptedException e) { - // Do nothing. - } - } - // Instantiate the save dialog. DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(SaveWebpageDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent, nestedScrollWebView.getAcceptCookies()); - // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. - saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + // Try to show the dialog. The download listener continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. + try { + // Show the save dialog. It must be named `save_dialog` so that the file picker can update the file name. + saveDialogFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog)); + } catch (Exception exception) { // The dialog could not be shown. + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(saveDialogFragment, getString(R.string.save_dialog))); + } } }); @@ -5396,7 +5466,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the status of swipe to refresh based on the scroll position of the nested scroll WebView. Also reinforce full screen browsing mode. // On API < 23, `getViewTreeObserver().addOnScrollChangedListener()` must be used, but it is a little bit buggy and appears to get garbage collected from time to time. if (Build.VERSION.SDK_INT >= 23) { - nestedScrollWebView.setOnScrollChangeListener((view, i, i1, i2, i3) -> { + nestedScrollWebView.setOnScrollChangeListener((view, scrollX, scrollY, oldScrollX, oldScrollY) -> { + // Set the swipe to refresh status. if (nestedScrollWebView.getSwipeToRefresh()) { // Only enable swipe to refresh if the WebView is scrolled to the top. swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0); @@ -5405,6 +5476,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook swipeRefreshLayout.setEnabled(false); } + // Set the visibility of the bottom app bar. + if (bottomAppBar && scrollAppBar && (Calendar.getInstance().getTimeInMillis() - lastScrollUpdate > 100)) { + if (scrollY - oldScrollY > 25) { // The WebView was scrolled down. + appBarLayout.setVisibility(View.GONE); + } else if (scrollY - oldScrollY < -25) { // The WebView was scrolled up. + appBarLayout.setVisibility(View.VISIBLE); + } + + // Update the last scroll update variable. This prevents the app bar from flashing on and off at the bottom of the screen. + lastScrollUpdate = Calendar.getInstance().getTimeInMillis(); + } + // Reinforce the system UI visibility flags if in full screen browsing mode. // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard. if (inFullScreenBrowsingMode) { @@ -5428,7 +5511,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook swipeRefreshLayout.setEnabled(false); } - // Reinforce the system UI visibility flags if in full screen browsing mode. // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard. if (inFullScreenBrowsingMode) { @@ -5806,7 +5888,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook String[] ultraListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraList); // Process the UltraList results. - if (ultraListResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched UltraLists's blacklist. + if (ultraListResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) { // The resource request matched UltraList's blacklist. // Add the result to the resource requests. nestedScrollWebView.addResourceRequest(new String[] {ultraListResults[0], ultraListResults[1], ultraListResults[2], ultraListResults[3], ultraListResults[4], ultraListResults[5]}); @@ -6060,25 +6142,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { - // Get the preferences. - boolean scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true); - - // Set the top padding of the swipe refresh layout according to the app bar scrolling preference. This can't be done in `appAppSettings()` because the app bar is not yet populated there. - if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) { - // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior. - swipeRefreshLayout.setPadding(0, 0, 0, 0); - - // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. - swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10, defaultProgressViewEndOffset); - } else { - // Get the app bar layout height. This can't be done in `applyAppSettings()` because the app bar is not yet populated there. - appBarHeight = appBarLayout.getHeight(); - - // The swipe refresh layout must be manually moved below the app bar layout. - swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); - - // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. - swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); + // Set the padding and layout settings if the app bar is at the top of the screen. + if (!bottomAppBar) { + // Set the top padding of the swipe refresh layout according to the app bar scrolling preference. This can't be done in `appAppSettings()` because the app bar is not yet populated there. + if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) { + // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior. + swipeRefreshLayout.setPadding(0, 0, 0, 0); + + // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. + swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10, defaultProgressViewEndOffset); + } else { + // Get the app bar layout height. This can't be done in `applyAppSettings()` because the app bar is not yet populated there. + appBarHeight = appBarLayout.getHeight(); + + // The swipe refresh layout must be manually moved below the app bar layout. + swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0); + + // The swipe to refresh circle doesn't always hide itself completely unless it is moved up 10 pixels. + swipeRefreshLayout.setProgressViewOffset(false, defaultProgressViewStartOffset - 10 + appBarHeight, defaultProgressViewEndOffset + appBarHeight); + } } // Reset the list of resource requests. @@ -6095,7 +6177,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Display the formatted URL text. urlEditText.setText(url); - // Apply text highlighting to `urlTextBox`. + // Apply text highlighting to the URL text box. highlightUrlText(); // Hide the keyboard. @@ -6306,22 +6388,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store the SSL error handler. nestedScrollWebView.setSslErrorHandler(handler); - // Prevent the dialog from displaying if the app window is not visible. - // The SSL error handler continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. - while (!activity.getWindow().isActive()) { - try { - // The window is not active. Wait 1 second. - wait(1000); - } catch (InterruptedException e) { - // Do nothing. - } - } - // Instantiate an SSL certificate error alert dialog. DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error, nestedScrollWebView.getWebViewFragmentId()); - // Show the SSL certificate error dialog. - sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error)); + // Try to show the dialog. The SSL error handler continues to function even when the WebView is paused. Attempting to display a dialog in that state leads to a crash. + try { + // Show the SSL certificate error dialog. + sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error)); + } catch (Exception exception) { + // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`. + pendingDialogsArrayList.add(new PendingDialog(sslCertificateErrorDialogFragment, getString(R.string.ssl_certificate_error))); + } } } });