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=bbdf85308e4233836b2347e52608dace7a0d2c92;hp=4f15fbc32adc8b85cae1cdcfb67be1f77f3de57c;hb=ba40295dffd761ccdc95d3b46ca7acbad1f00d5e;hpb=38005c949c68d157785f7be40ea27cb8e18e1867 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 4f15fbc3..bbdf8530 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,25 +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.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; @@ -113,6 +93,28 @@ 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.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +// `ShortcutInfoCompat`, `ShortcutManagerCompat`, and `IconCompat` can be switched to the non-compat versions once API >= 26. +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; +import androidx.core.graphics.drawable.IconCompat; +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; @@ -181,7 +183,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook public static String formattedUrlString; // `sslCertificate` is public static so it can be accessed from `DomainsActivity`, `DomainsListFragment`, `DomainSettingsFragment`, `PinnedMismatchDialog`, and `ViewSslCertificateDialog`. - // It is also used in `onCreate()`. + // It is also used in `onCreate()` and `checkPinnedMismatch()`. public static SslCertificate sslCertificate; // `currentHostIpAddresses` is public static so it can be accessed from `DomainSettingsFragment` and `ViewSslCertificateDialog`. @@ -271,7 +273,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `domainSettingsDatabaseId` is public static so it can be accessed from `PinnedMismatchDialog`. It is also used in `onCreate()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. public static int domainSettingsDatabaseId; - // The pinned variables are public static so they can be accessed from `PinnedMismatchDialog`. They are also used in `onCreate()` and `applyDomainSettings()`. + // The pinned variables are public static so they can be accessed from `PinnedMismatchDialog`. They are also used in `onCreate()`, `applyDomainSettings()`, and `checkPinnedMismatch()`. public static String pinnedSslIssuedToCName; public static String pinnedSslIssuedToOName; public static String pinnedSslIssuedToUName; @@ -291,17 +293,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook public final static int DOMAINS_CUSTOM_USER_AGENT = 13; - // `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; + // `urlIsLoading` is used in `onCreate()`, `onCreateOptionsMenu()`, `loadUrl()`, `applyDomainSettings()`, and `GetHostIpAddresses`. + private static boolean urlIsLoading; + + // `gettingIpAddresses` is used in `onCreate() and `GetHostIpAddresses`. + private static boolean gettingIpAddresses; + + // `pinnedDomainSslCertificate` is used in `onCreate()`, `applyDomainSettings()`, and `checkPinnedMismatch()`. + private static boolean pinnedSslCertificate; - // `drawerLayout` is used in `onCreate()`, `onNewIntent()`, `onBackPressed()`, and `onRestart()`. - private DrawerLayout drawerLayout; + // `pinnedIpAddress` is used in `applyDomainSettings()` and `checkPinnedMismatch()`. + private static boolean pinnedIpAddresses; - // `rootCoordinatorLayout` is used in `onCreate()` and `applyAppSettings()`. - private CoordinatorLayout rootCoordinatorLayout; + // `ignorePinnedDomainInformation` is used in `onSslMismatchProceed()`, `applyDomainSettings()`, and `checkPinnedMismatch()`. + private static boolean ignorePinnedDomainInformation; + + // The fragment manager is initialized in `onCreate()` and accessed from the static `checkPinnedMismatch()`. + private static FragmentManager fragmentManager; + + + // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`. + private boolean navigatingHistory; // `mainWebView` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, and `applyProxyThroughOrbot()`. @@ -310,9 +323,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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; @@ -413,15 +423,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`. private String currentDomainName; - // `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`. - private boolean pinnedSslCertificate; - - // `pinnedIpAddress` is used in `onCreate()` and `applyDomainSettings()`. - private boolean pinnedIpAddresses; - - // `ignorePinnedDomainInformation` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. - private boolean ignorePinnedDomainInformation; - // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`. private BroadcastReceiver orbotStatusBroadcastReceiver; @@ -449,11 +450,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `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 `onPostCreate()`. + private ActionBarDrawerToggle actionBarDrawerToggle; // `urlTextBox` is used in `onCreate()`, `onOptionsItemSelected()`, `loadUrlFromTextBox()`, `loadUrl()`, and `highlightUrlText()`. private EditText urlTextBox; @@ -477,12 +475,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; - - // `urlIsLoading` is used in `onCreate()`, `onCreateOptionsMenu()`, `loadUrl()`, and `applyDomainSettings()`. - private boolean urlIsLoading; - // `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, // and `loadBookmarksFolder()`. private BookmarksDatabaseHelper bookmarksDatabaseHelper; @@ -553,23 +545,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the content view. setContentView(R.layout.main_drawerlayout); - // Get a handle for the resources. + // Get handles for views, resources, and managers. Resources resources = getResources(); - - // 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)); @@ -639,15 +630,15 @@ 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. + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + CoordinatorLayout coordinatorLayout = findViewById(R.id.coordinatorlayout); bookmarksListView = findViewById(R.id.bookmarks_drawer_listview); bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview); FloatingActionButton launchBookmarksActivityFab = findViewById(R.id.launch_bookmarks_activity_fab); FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab); FloatingActionButton createBookmarkFab = findViewById(R.id.create_bookmark_fab); - mainWebViewRelativeLayout = findViewById(R.id.main_webview_relativelayout); + SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); mainWebView = findViewById(R.id.main_webview); findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout); findOnPageEditText = findViewById(R.id.find_on_page_edittext); @@ -682,16 +673,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(getSupportFragmentManager(), 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(getSupportFragmentManager(), 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. @@ -704,8 +695,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook inFullScreenBrowsingMode = !inFullScreenBrowsingMode; if (inFullScreenBrowsingMode) { // Switch to full screen mode. - // Hide the `appBar`. - appBar.hide(); + // Hide the action bar. + actionBar.hide(); // Hide the banner ad in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { @@ -725,13 +716,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook * 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); + coordinatorLayout.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); + // Set the coordinator layout to fill the entire screen. + coordinatorLayout.setFitsSystemWindows(false); } else { // Hide everything except the status and navigation bars. - // Set `rootCoordinatorLayout` to fit under the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(false); + // Set the coordinator layout to fill the entire screen. + coordinatorLayout.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) { @@ -740,8 +731,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } } else { // Switch to normal viewing mode. - // Show the `appBar`. - appBar.show(); + // Show the action bar. + actionBar.show(); // Show the `BannerAd` in the free flavor. if (BuildConfig.FLAVOR.contentEquals("free")) { @@ -755,11 +746,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - // Remove any `SYSTEM_UI` flags from `rootCoordinatorLayout`. - rootCoordinatorLayout.setSystemUiVisibility(0); + // Remove any `SYSTEM_UI` flags from the coordinator layout. + coordinatorLayout.setSystemUiVisibility(0); - // Constrain `rootCoordinatorLayout` inside the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(true); + // Constrain the coordinator layout inside the status and navigation bars. + coordinatorLayout.setFitsSystemWindows(true); } // Consume the double-tap. @@ -771,9 +762,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); @@ -836,7 +827,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }); // Implement swipe to refresh. - swipeRefreshLayout = findViewById(R.id.swiperefreshlayout); swipeRefreshLayout.setOnRefreshListener(() -> mainWebView.reload()); // Set the swipe to refresh color according to the theme. @@ -910,12 +900,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(getSupportFragmentManager(), 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(getSupportFragmentManager(), resources.getString(R.string.edit_bookmark)); + DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId); + editBookmarkDialog.show(fragmentManager, resources.getString(R.string.edit_bookmark)); } // Consume the event. @@ -981,8 +971,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); @@ -1081,10 +1071,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook * 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); + coordinatorLayout.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); + // Set the coordinator layout to fill the entire screen. + coordinatorLayout.setFitsSystemWindows(false); + + // Hide the action bar. + actionBar.hide(); // Disable the sliding drawers. drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); @@ -1123,10 +1116,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook * 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); + coordinatorLayout.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); + // Remove any `SYSTEM_UI` flags from the coordinator layout. + coordinatorLayout.setSystemUiVisibility(0); // Add the translucent status flag if it is unset. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); @@ -1140,19 +1133,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } } else { // Switch to normal viewing mode. - // Show the `appBar` if `findOnPageLinearLayout` is not visible. + // Show the action bar if the find on page linear layout is not visible. if (findOnPageLinearLayout.getVisibility() == View.GONE) { - appBar.show(); + actionBar.show(); } // Show the `BannerAd` 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.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 any `SYSTEM_UI` flags from the coordinator layout. + coordinatorLayout.setSystemUiVisibility(0); // Remove the translucent navigation bar flag if it is set. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); @@ -1160,8 +1153,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - // Constrain `rootCoordinatorLayout` inside the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(true); + // Constrain the coordinator layout inside the status and navigation bars. + coordinatorLayout.setFitsSystemWindows(true); + + // Show the action bar. + actionBar.show(); } // Show the ad if this is the free flavor. @@ -1213,17 +1209,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(getSupportFragmentManager(), getString(R.string.download)); + downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); } } }); @@ -1600,13 +1596,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook httpAuthHandler = handler; // Display the HTTP authentication dialog. - AppCompatDialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm); - httpAuthenticationDialogFragment.show(getSupportFragmentManager(), 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. @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { + // Set `urlIsLoading` to `true`, so that redirects while loading do not trigger changes in the user agent, which forces another reload of the existing page. + // This is also used to determine when to check for pinned mismatches. + urlIsLoading = true; + // Reset the list of host IP addresses. currentHostIpAddresses = ""; @@ -1631,7 +1631,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0); // Check to see if Privacy Browser is waiting on Orbot. - if (!waitingForOrbot) { // We are not waiting on Orbot, so we need to process the URL. + if (!waitingForOrbot) { // Process the URL. // The formatted URL string must be updated at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded. formattedUrlString = url; @@ -1661,9 +1661,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Set `urlIsLoading` to `true`, so that redirects while loading do not trigger changes in the user agent, which forces another reload of the existing page. - urlIsLoading = true; - // Replace Refresh with Stop if the menu item has been created. (The WebView typically begins loading before the menu items are instantiated.) if (refreshMenuItem != null) { // Set the title. @@ -1710,8 +1707,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Reset `urlIsLoading`, which is used to prevent reloads on redirect if the user agent changes. - urlIsLoading = false; + // Clear the cache and history if Incognito Mode is enabled. if (incognitoModeEnabled) { @@ -1768,69 +1764,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Store the SSL certificate so it can be accessed from `ViewSslCertificateDialog` and `PinnedMismatchDialog`. sslCertificate = mainWebView.getCertificate(); - // Check the current website information against any pinned domain information. - if ((pinnedSslCertificate || pinnedIpAddresses) && !ignorePinnedDomainInformation) { - // Initialize the current SSL certificate variables. - String currentWebsiteIssuedToCName = ""; - String currentWebsiteIssuedToOName = ""; - String currentWebsiteIssuedToUName = ""; - String currentWebsiteIssuedByCName = ""; - String currentWebsiteIssuedByOName = ""; - String currentWebsiteIssuedByUName = ""; - Date currentWebsiteSslStartDate = null; - Date currentWebsiteSslEndDate = null; - - - // Extract the individual pieces of information from the current website SSL certificate if it is not null. - if (sslCertificate != null) { - currentWebsiteIssuedToCName = sslCertificate.getIssuedTo().getCName(); - currentWebsiteIssuedToOName = sslCertificate.getIssuedTo().getOName(); - currentWebsiteIssuedToUName = sslCertificate.getIssuedTo().getUName(); - currentWebsiteIssuedByCName = sslCertificate.getIssuedBy().getCName(); - currentWebsiteIssuedByOName = sslCertificate.getIssuedBy().getOName(); - currentWebsiteIssuedByUName = sslCertificate.getIssuedBy().getUName(); - currentWebsiteSslStartDate = sslCertificate.getValidNotBeforeDate(); - currentWebsiteSslEndDate = sslCertificate.getValidNotAfterDate(); - } - - // Initialize string variables to store the SSL certificate dates. Strings are needed to compare the values below, which doesn't work with `Dates` if they are `null`. - String currentWebsiteSslStartDateString = ""; - String currentWebsiteSslEndDateString = ""; - String pinnedSslStartDateString = ""; - String pinnedSslEndDateString = ""; - - // Convert the `Dates` to `Strings` if they are not `null`. - if (currentWebsiteSslStartDate != null) { - currentWebsiteSslStartDateString = currentWebsiteSslStartDate.toString(); - } - - if (currentWebsiteSslEndDate != null) { - currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString(); - } - - if (pinnedSslStartDate != null) { - pinnedSslStartDateString = pinnedSslStartDate.toString(); - } - - if (pinnedSslEndDate != null) { - pinnedSslEndDateString = pinnedSslEndDate.toString(); - } - - // Check to see if the pinned information matches the current information. - if ((pinnedIpAddresses && !currentHostIpAddresses.equals(pinnedHostIpAddresses)) || (pinnedSslCertificate && (!currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) || - !currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) || !currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) || - !currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) || !currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) || - !currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) || !currentWebsiteSslStartDateString.equals(pinnedSslStartDateString) || - !currentWebsiteSslEndDateString.equals(pinnedSslEndDateString)))) { - - // Get a handle for the pinned mismatch alert dialog. - AppCompatDialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(pinnedSslCertificate, pinnedIpAddresses); - - // Show the pinned mismatch alert dialog. - pinnedMismatchDialogFragment.show(getSupportFragmentManager(), getString(R.string.pinned_mismatch)); - } + // Check the current website information against any pinned domain information if the current IP addresses have been loaded. + if (!gettingIpAddresses) { + checkPinnedMismatch(); } } + + // Reset `urlIsLoading`, which is used to prevent reloads on redirect if the user agent changes. It is also used to determine when to check for pinned mismatches. + urlIsLoading = false; } // Handle SSL Certificate errors. @@ -1863,8 +1804,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook sslErrorHandler = handler; // Display the SSL error `AlertDialog`. - AppCompatDialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error); - sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error)); + DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error); + sslCertificateErrorDialogFragment.show(fragmentManager, getString(R.string.ssl_certificate_error)); } } }); @@ -1932,6 +1873,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); @@ -2001,6 +1945,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); @@ -2043,6 +1990,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } if (displayingFullScreenVideo) { + // Get handles for the layouts that need to be modified. + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + CoordinatorLayout coordinatorLayout = findViewById(R.id.coordinatorlayout); + // Remove the translucent overlays. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); @@ -2053,7 +2004,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook * 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); + coordinatorLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); } } @@ -2090,7 +2041,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook @Override public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. + // Inflate the menu. This adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.webview_options_menu, menu); // Set mainMenu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons`. @@ -2162,6 +2113,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); @@ -2356,7 +2310,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Get the selected menu item ID. int menuItemId = menuItem.getItemId(); - // Set the commands that relate to the menu entries. + // Run the commands that correlate to the selected menu item. switch (menuItemId) { case R.id.toggle_javascript: // Switch the status of javaScriptEnabled. @@ -2527,21 +2481,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); } } @@ -2556,15 +2510,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(); @@ -2576,15 +2531,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Setup a runnable to manually delete the DOM storage files and directories. Runnable deleteDomStorageRunnable = () -> { try { - // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. - privacyBrowserRuntime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); + // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. + Process deleteLocalStorageProcess = privacyBrowserRuntime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); // Multiple commands must be used because `Runtime.exec()` does not like `*`. - privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); - privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); - privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); - privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); - } catch (IOException e) { + Process deleteIndexProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); + Process deleteQuotaManagerProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); + Process deleteQuotaManagerJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); + Process deleteDatabasesProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); + + // Wait for the processes to finish. + deleteLocalStorageProcess.waitFor(); + deleteIndexProcess.waitFor(); + deleteQuotaManagerProcess.waitFor(); + deleteQuotaManagerJournalProcess.waitFor(); + deleteDatabasesProcess.waitFor(); + } catch (Exception exception) { // Do nothing if an error is thrown. } }; @@ -2604,15 +2566,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()); @@ -2830,6 +2793,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; @@ -2873,8 +2839,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.find_on_page: + // Get a handle for the toolbar. + Toolbar toolbar = findViewById(R.id.toolbar); + // Hide the URL app bar. - supportAppBar.setVisibility(View.GONE); + toolbar.setVisibility(View.GONE); // Show the Find on Page `RelativeLayout`. findOnPageLinearLayout.setVisibility(View.VISIBLE); @@ -2924,36 +2893,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return true; case R.id.open_with_app: - // Create the open with intent with `ACTION_VIEW`. - Intent openWithAppIntent = new Intent(Intent.ACTION_VIEW); - - // Set the URI but not the MIME type. This should open all available apps. - openWithAppIntent.setData(Uri.parse(formattedUrlString)); - - // Flag the intent to open in a new task. - openWithAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - // Show the chooser. - startActivity(Intent.createChooser(openWithAppIntent, getString(R.string.open_with))); + openWithApp(formattedUrlString); return true; case R.id.open_with_browser: - // Create the open with intent with `ACTION_VIEW`. - Intent openWithBrowserIntent = new Intent(Intent.ACTION_VIEW); - - // Set the URI and the MIME type. `"text/html"` should load browser options. - openWithBrowserIntent.setDataAndType(Uri.parse(formattedUrlString), "text/html"); - - // Flag the intent to open in a new task. - openWithBrowserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - // Show the chooser. - startActivity(Intent.createChooser(openWithBrowserIntent, getString(R.string.open_with))); + openWithBrowser(formattedUrlString); return true; case R.id.add_to_homescreen: // Show the alert dialog. - AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog(); + DialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog(); createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); //Everything else will be handled by the alert dialog and the associated listener below. @@ -2980,7 +2929,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: @@ -3030,8 +2979,8 @@ 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); + // 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; @@ -3080,6 +3029,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook startActivity(importExportIntent); break; + case R.id.logcat: + // Launch the logcat activity. + Intent logcatIntent = new Intent(this, LogcatActivity.class); + startActivity(logcatIntent); + break; + case R.id.guide: // Launch `GuideActivity`. Intent guideIntent = new Intent(this, GuideActivity.class); @@ -3092,14 +3047,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook startActivity(aboutIntent); break; - case R.id.clearAndExit: + case R.id.clear_and_exit: // Close the bookmarks cursor and database. bookmarksCursor.close(); bookmarksDatabaseHelper.close(); - // Get a handle for `sharedPreferences`. `this` references the current context. + // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + // Get the status of the clear everything preference. boolean clearEverything = sharedPreferences.getBoolean("clear_everything", true); // Clear cookies. @@ -3113,10 +3069,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete the cookies database, as `CookieManager` sometimes will not flush its changes to disk before `System.exit(0)` is run. try { - // We have to use two commands because `Runtime.exec()` does not like `*`. - privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies"); - privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal"); - } catch (IOException e) { + // Two commands must be used because `Runtime.exec()` does not like `*`. + Process deleteCookiesProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies"); + Process deleteCookiesJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal"); + + // Wait until the processes have finished. + deleteCookiesProcess.waitFor(); + deleteCookiesJournalProcess.waitFor(); + } catch (Exception exception) { // Do nothing if an error is thrown. } } @@ -3130,14 +3090,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete the DOM storage files and directories, as `WebStorage` sometimes will not flush its changes to disk before `System.exit(0)` is run. try { // A `String[]` must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. - privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); + Process deleteLocalStorageProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"}); // Multiple commands must be used because `Runtime.exec()` does not like `*`. - privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); - privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); - privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); - privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); - } catch (IOException e) { + Process deleteIndexProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB"); + Process deleteQuotaManagerProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager"); + Process deleteQuotaManagerJournalProcess = privacyBrowserRuntime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal"); + Process deleteDatabaseProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases"); + + // Wait until the processes have finished. + deleteLocalStorageProcess.waitFor(); + deleteIndexProcess.waitFor(); + deleteQuotaManagerProcess.waitFor(); + deleteQuotaManagerJournalProcess.waitFor(); + deleteDatabaseProcess.waitFor(); + } catch (Exception exception) { // Do nothing if an error is thrown. } } @@ -3149,10 +3116,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete the form data database, as `WebViewDatabase` sometimes will not flush its changes to disk before `System.exit(0)` is run. try { - // We have to use a `String[]` because the database contains a space and `Runtime.exec` will not escape the string correctly otherwise. - privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"}); - privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"}); - } catch (IOException e) { + // A string array must be used because the database contains a space and `Runtime.exec` will not otherwise escape the string correctly. + Process deleteWebDataProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"}); + Process deleteWebDataJournalProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"}); + + // Wait until the processes have finished. + deleteWebDataProcess.waitFor(); + deleteWebDataJournalProcess.waitFor(); + } catch (Exception exception) { // Do nothing if an error is thrown. } } @@ -3165,12 +3136,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Manually delete the cache directories. try { // Delete the main cache directory. - privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache"); + Process deleteCacheProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache"); // Delete the secondary `Service Worker` cache directory. - // A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise. - privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"}); - } catch (IOException e) { + // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly. + Process deleteServiceWorkerProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"}); + + // Wait until the processes have finished. + deleteCacheProcess.waitFor(); + deleteServiceWorkerProcess.waitFor(); + } catch (Exception exception) { // Do nothing if an error is thrown. } } @@ -3187,9 +3162,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(); @@ -3197,8 +3169,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`. if (clearEverything) { try { - privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview"); - } catch (IOException e) { + // Delete the folder. + Process deleteAppWebviewProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview"); + + // Wait until the process has finished. + deleteAppWebviewProcess.waitFor(); + } catch (Exception exception) { // Do nothing if an error is thrown. } } @@ -3215,6 +3191,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; @@ -3222,10 +3201,11 @@ 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(); + actionBarDrawerToggle.syncState(); } @Override @@ -3264,8 +3244,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; @@ -3314,23 +3295,35 @@ 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(getSupportFragmentManager(), getString(R.string.download)); + downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); } } return false; }); - // Add a `Cancel` entry, which by default closes the `ContextMenu`. + // Add an Open with App entry. + menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> { + openWithApp(linkUrl); + return false; + }); + + // Add an Open with Browser entry. + menu.add(R.string.open_with_browser).setOnMenuItemClickListener((MenuItem item) -> { + openWithBrowser(linkUrl); + return false; + }); + + // Add a Cancel entry, which by default closes the context menu. menu.add(R.string.cancel); break; @@ -3341,7 +3334,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the target URL as the title of the `ContextMenu`. menu.setHeaderTitle(linkUrl); - // Add a `Write Email` entry. + // Add a Write Email entry. menu.add(R.string.write_email).setOnMenuItemClickListener(item -> { // Use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched. Intent emailIntent = new Intent(Intent.ACTION_SENDTO); @@ -3357,7 +3350,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return false; }); - // Add a `Copy Email Address` entry. + // Add a Copy Email Address entry. menu.add(R.string.copy_email_address).setOnMenuItemClickListener(item -> { // Save the email address in a `ClipData`. ClipData srcEmailTypeClipData = ClipData.newPlainText(getString(R.string.email_address), linkUrl); @@ -3379,13 +3372,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the image URL as the title of the `ContextMenu`. menu.setHeaderTitle(imageUrl); - // Add a `View Image` entry. + // Add a View Image entry. menu.add(R.string.view_image).setOnMenuItemClickListener(item -> { loadUrl(imageUrl); return false; }); - // Add a `Download Image` entry. + // Add a Download Image entry. menu.add(R.string.download_image).setOnMenuItemClickListener((MenuItem item) -> { // Check if the download should be processed by an external app. if (downloadWithExternalApp) { // Download with an external app. @@ -3402,23 +3395,23 @@ 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(getSupportFragmentManager(), getString(R.string.download)); + downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); } } return false; }); - // Add a `Copy URL` entry. + // Add a Copy URL entry. menu.add(R.string.copy_url).setOnMenuItemClickListener(item -> { // Save the image URL in a `ClipData`. ClipData srcImageTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl); @@ -3428,6 +3421,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return false; }); + // Add an Open with App entry. + menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> { + openWithApp(imageUrl); + return false; + }); + + // Add an Open with Browser entry. + menu.add(R.string.open_with_browser).setOnMenuItemClickListener((MenuItem item) -> { + openWithBrowser(imageUrl); + return false; + }); + // Add a `Cancel` entry, which by default closes the `ContextMenu`. menu.add(R.string.cancel); break; @@ -3464,17 +3469,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(getSupportFragmentManager(), getString(R.string.download)); + downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); } } return false; @@ -3490,6 +3495,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook return false; }); + // Add an Open with App entry. + menu.add(R.string.open_with_app).setOnMenuItemClickListener((MenuItem item) -> { + openWithApp(imageUrl); + return false; + }); + + // Add an Open with Browser entry. + menu.add(R.string.open_with_browser).setOnMenuItemClickListener((MenuItem item) -> { + openWithBrowser(imageUrl); + return false; + }); + // Add a `Cancel` entry, which by default closes the `ContextMenu`. menu.add(R.string.cancel); break; @@ -3497,7 +3514,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onCreateBookmark(AppCompatDialogFragment dialogFragment) { + public void onCreateBookmark(DialogFragment dialogFragment) { // Get the `EditTexts` from the `dialogFragment`. EditText createBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext); EditText createBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext); @@ -3528,7 +3545,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onCreateBookmarkFolder(AppCompatDialogFragment dialogFragment) { + public void onCreateBookmarkFolder(DialogFragment dialogFragment) { // Get handles for the views in `dialogFragment`. EditText createFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext); RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton); @@ -3573,7 +3590,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onCreateHomeScreenShortcut(AppCompatDialogFragment dialogFragment) { + public void onCreateHomeScreenShortcut(DialogFragment dialogFragment) { // Get the shortcut name. EditText shortcutNameEditText = dialogFragment.getDialog().findViewById(R.id.shortcut_name_edittext); String shortcutNameString = shortcutNameEditText.getText().toString(); @@ -3614,16 +3631,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(getSupportFragmentManager(), getString(R.string.download)), 500); + new Handler().postDelayed(() -> downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)), 500); } else { - downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + downloadFileDialogFragment.show(fragmentManager, getString(R.string.download)); } // Reset the download variables. @@ -3634,13 +3654,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(getSupportFragmentManager(), getString(R.string.download)), 500); + new Handler().postDelayed(() -> downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)), 500); } else { - downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); + downloadImageDialogFragment.show(fragmentManager, getString(R.string.download)); } // Reset the image URL variable. @@ -3650,7 +3670,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`. @@ -3702,7 +3722,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`. @@ -3754,7 +3774,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSaveBookmark(AppCompatDialogFragment dialogFragment, int selectedBookmarkDatabaseId) { + 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); @@ -3785,7 +3805,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSaveBookmarkFolder(AppCompatDialogFragment dialogFragment, int selectedFolderDatabaseId) { + 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); @@ -3853,7 +3873,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); @@ -3865,7 +3885,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 @@ -3922,6 +3942,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,8 +4087,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Hide the Find on Page `RelativeLayout`. findOnPageLinearLayout.setVisibility(View.GONE); - // Show the URL app bar. - supportAppBar.setVisibility(View.VISIBLE); + // Get a handle for the toolbar. + Toolbar toolbar = findViewById(R.id.toolbar); + + // Show the toolbar. + toolbar.setVisibility(View.VISIBLE); // Hide the keyboard so we can see the webpage. `0` indicates no additional flags. inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0); @@ -4094,6 +4120,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook customHeaders.remove("DNT"); } + // Get handles for the views that need to be modified. `getSupportActionBar()` must be used until the minimum API >= 21. + DrawerLayout drawerLayout = findViewById(R.id.drawerlayout); + CoordinatorLayout coordinatorLayout = findViewById(R.id.coordinatorlayout); + ActionBar actionBar = getSupportActionBar(); + // 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. @@ -4110,10 +4141,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook * 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); + coordinatorLayout.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); + // Remove any `SYSTEM_UI` flags from the coordinator layout. + coordinatorLayout.setSystemUiVisibility(0); // Add the translucent status flag if it is unset. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); @@ -4130,19 +4161,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // 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. + // Remove the incorrect lint warning below that the action bar might be null. + assert actionBar != null; + + // Show the action bar if the find on page linear layout is not visible. if (findOnPageLinearLayout.getVisibility() == View.GONE) { - appBar.show(); + actionBar.show(); } // Show the `BannerAd` 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.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 any `SYSTEM_UI` flags from the coordinator layout. + coordinatorLayout.setSystemUiVisibility(0); // Remove the translucent navigation bar flag if it is set. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); @@ -4150,8 +4184,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Add the translucent status flag if it is unset. This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`. getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - // Constrain `rootCoordinatorLayout` inside the status and navigation bars. - rootCoordinatorLayout.setFitsSystemWindows(true); + // Constrain the coordinator layout inside the status and navigation bars. + coordinatorLayout.setFitsSystemWindows(true); } } @@ -4202,6 +4236,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); @@ -4547,6 +4584,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook if (mainMenu != null) { updatePrivacyIcons(true); } + + // TODO. + swipeRefreshLayout.setEnabled(false); } // Reload the website if returning from the Domains activity. @@ -4570,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`. @@ -4592,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. @@ -4632,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. @@ -4830,6 +4876,98 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } + private void openWithApp(String url) { + // Create the open with intent with `ACTION_VIEW`. + Intent openWithAppIntent = new Intent(Intent.ACTION_VIEW); + + // Set the URI but not the MIME type. This should open all available apps. + openWithAppIntent.setData(Uri.parse(url)); + + // Flag the intent to open in a new task. + openWithAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Show the chooser. + startActivity(openWithAppIntent); + } + + private void openWithBrowser(String url) { + // Create the open with intent with `ACTION_VIEW`. + Intent openWithBrowserIntent = new Intent(Intent.ACTION_VIEW); + + // Set the URI and the MIME type. `"text/html"` should load browser options. + openWithBrowserIntent.setDataAndType(Uri.parse(url), "text/html"); + + // Flag the intent to open in a new task. + openWithBrowserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + // Show the chooser. + startActivity(openWithBrowserIntent); + } + + private static void checkPinnedMismatch() { + if ((pinnedSslCertificate || pinnedIpAddresses) && !ignorePinnedDomainInformation) { + // Initialize the current SSL certificate variables. + String currentWebsiteIssuedToCName = ""; + String currentWebsiteIssuedToOName = ""; + String currentWebsiteIssuedToUName = ""; + String currentWebsiteIssuedByCName = ""; + String currentWebsiteIssuedByOName = ""; + String currentWebsiteIssuedByUName = ""; + Date currentWebsiteSslStartDate = null; + Date currentWebsiteSslEndDate = null; + + + // Extract the individual pieces of information from the current website SSL certificate if it is not null. + if (sslCertificate != null) { + currentWebsiteIssuedToCName = sslCertificate.getIssuedTo().getCName(); + currentWebsiteIssuedToOName = sslCertificate.getIssuedTo().getOName(); + currentWebsiteIssuedToUName = sslCertificate.getIssuedTo().getUName(); + currentWebsiteIssuedByCName = sslCertificate.getIssuedBy().getCName(); + currentWebsiteIssuedByOName = sslCertificate.getIssuedBy().getOName(); + currentWebsiteIssuedByUName = sslCertificate.getIssuedBy().getUName(); + currentWebsiteSslStartDate = sslCertificate.getValidNotBeforeDate(); + currentWebsiteSslEndDate = sslCertificate.getValidNotAfterDate(); + } + + // Initialize string variables to store the SSL certificate dates. Strings are needed to compare the values below, which doesn't work with `Dates` if they are `null`. + String currentWebsiteSslStartDateString = ""; + String currentWebsiteSslEndDateString = ""; + String pinnedSslStartDateString = ""; + String pinnedSslEndDateString = ""; + + // Convert the `Dates` to `Strings` if they are not `null`. + if (currentWebsiteSslStartDate != null) { + currentWebsiteSslStartDateString = currentWebsiteSslStartDate.toString(); + } + + if (currentWebsiteSslEndDate != null) { + currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString(); + } + + if (pinnedSslStartDate != null) { + pinnedSslStartDateString = pinnedSslStartDate.toString(); + } + + if (pinnedSslEndDate != null) { + pinnedSslEndDateString = pinnedSslEndDate.toString(); + } + + // Check to see if the pinned information matches the current information. + if ((pinnedIpAddresses && !currentHostIpAddresses.equals(pinnedHostIpAddresses)) || (pinnedSslCertificate && (!currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) || + !currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) || !currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) || + !currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) || !currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) || + !currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) || !currentWebsiteSslStartDateString.equals(pinnedSslStartDateString) || + !currentWebsiteSslEndDateString.equals(pinnedSslEndDateString)))) { + + // Get a handle for the pinned mismatch alert dialog. + DialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(pinnedSslCertificate, pinnedIpAddresses); + + // Show the pinned mismatch alert dialog. + pinnedMismatchDialogFragment.show(fragmentManager, "Pinned Mismatch"); + } + } + } + // This must run asynchronously because it involves a network request. `String` declares the parameters. `Void` does not declare progress units. `String` contains the results. private static class GetHostIpAddresses extends AsyncTask { // The weak references are used to determine if the activity have disappeared while the AsyncTask is running. @@ -4840,12 +4978,28 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook activityWeakReference = new WeakReference<>(activity); } + // `onPreExecute()` operates on the UI thread. + @Override + protected void onPreExecute() { + // Get a handle for the activity. + Activity activity = activityWeakReference.get(); + + // Abort if the activity is gone. + if ((activity == null) || activity.isFinishing()) { + return; + } + + // Set the getting IP addresses tracker. + gettingIpAddresses = true; + } + + @Override protected String doInBackground(String... domainName) { - // Get handles for the activity and the alert dialog. + // Get a handle for the activity. Activity activity = activityWeakReference.get(); - // Abort if the activity or the dialog is gone. + // Abort if the activity is gone. if ((activity == null) || activity.isFinishing()) { // Return an empty spannable string builder. return ""; @@ -4883,16 +5037,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `onPostExecute()` operates on the UI thread. @Override protected void onPostExecute(String ipAddresses) { - // Get handles for the activity and the alert dialog. + // Get a handle for the activity. Activity activity = activityWeakReference.get(); - // Abort if the activity or the alert dialog is gone. + // Abort if the activity is gone. if ((activity == null) || activity.isFinishing()) { return; } // Store the IP addresses. currentHostIpAddresses = ipAddresses; + + if (!urlIsLoading) { + checkPinnedMismatch(); + } + + // Reset the getting IP addresses tracker. + gettingIpAddresses = false; } } } \ No newline at end of file