import android.webkit.CookieManager;
import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
+import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.WebViewDatabase;
+import android.widget.ArrayAdapter;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.stoutner.privacybrowser.BannerAd;
import com.stoutner.privacybrowser.BuildConfig;
import com.stoutner.privacybrowser.R;
-import com.stoutner.privacybrowser.dialogs.AddDomainDialog;
+import com.stoutner.privacybrowser.dialogs.AdConsentDialog;
import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog;
import com.stoutner.privacybrowser.dialogs.CreateBookmarkFolderDialog;
import com.stoutner.privacybrowser.dialogs.CreateHomeScreenShortcutDialog;
import com.stoutner.privacybrowser.dialogs.PinnedSslCertificateMismatchDialog;
import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog;
+import com.stoutner.privacybrowser.helpers.AdHelper;
import com.stoutner.privacybrowser.helpers.BlockListHelper;
import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
import java.util.Set;
// AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
-public class MainWebViewActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener, CreateBookmarkDialog.CreateBookmarkListener,
- CreateBookmarkFolderDialog.CreateBookmarkFolderListener, CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener, DownloadFileDialog.DownloadFileListener,
- DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
- EditBookmarkFolderDialog.EditBookmarkFolderListener, HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener,
- PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener {
+public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
+ CreateHomeScreenShortcutDialog.CreateHomeScreenShortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener,
+ DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener,
+ HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener,
+ SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener {
// `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`,
// `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`,
// and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
public static boolean darkTheme;
+ // `allowScreenshots` is public static so it can be accessed from everywhere. It is also used in `onCreate()`.
+ public static boolean allowScreenshots;
+
// `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`,
// `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `EditBookmarkDatabaseViewDialog`, and `ViewSslCertificateDialog`. It is also used in `onCreate()`,
// `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcutCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`.
// `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`
public static boolean reloadOnRestart;
- // `reloadUrlOnRestart` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`.
+ // `reloadUrlOnRestart` is public static so it can be accessed from `SettingsFragment` and `BookmarksActivity`. It is also used in `onRestart()`.
public static boolean loadUrlOnRestart;
// `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onRestart()`.
public static Date pinnedDomainSslStartDate;
public static Date pinnedDomainSslEndDate;
+ // The user agent constants are public static so they can be accessed from `SettingsFragment`, `DomainsActivity`, and `DomainSettingsFragment`.
+ public final static int UNRECOGNIZED_USER_AGENT = -1;
+ public final static int SETTINGS_WEBVIEW_DEFAULT_USER_AGENT = 1;
+ public final static int SETTINGS_CUSTOM_USER_AGENT = 12;
+ public final static int DOMAINS_SYSTEM_DEFAULT_USER_AGENT = 0;
+ public final static int DOMAINS_WEBVIEW_DEFAULT_USER_AGENT = 2;
+ public final static int DOMAINS_CUSTOM_USER_AGENT = 13;
+
// `appBar` is used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, and `applyAppSettings()`.
private ActionBar appBar;
// `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`.
private FrameLayout fullScreenVideoFrameLayout;
- // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu`, and `onRestart()`.
+ // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsMenuSelected()`, and `onRestart()`.
private SwipeRefreshLayout swipeRefreshLayout;
// `urlAppBarRelativeLayout` is used in `onCreate()` and `applyDomainSettings()`.
// `nightMode` is used in `onCreate()` and `applyDomainSettings()`.
private boolean nightMode;
- // `swipeToRefreshEnabled` is used in `onPrepareOptionsMenu()` and `applyAppSettings()`.
- private boolean swipeToRefreshEnabled;
-
// `displayWebpageImagesBoolean` is used in `applyAppSettings()` and `applyDomainSettings()`.
private boolean displayWebpageImagesBoolean;
private Runtime privacyBrowserRuntime;
// `proxyThroughOrbot` is used in `onRestart()` and `applyAppSettings()`.
- boolean proxyThroughOrbot;
+ private boolean proxyThroughOrbot;
// `incognitoModeEnabled` is used in `onCreate()` and `applyAppSettings()`.
private boolean incognitoModeEnabled;
private ForegroundColorSpan initialGrayColorSpan;
private ForegroundColorSpan finalGrayColorSpan;
- // `adView` is used in `onCreate()` and `onConfigurationChanged()`.
- private View adView;
-
// `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`.
private SslErrorHandler sslErrorHandler;
// `oldFolderNameString` is used in `onCreate()` and `onSaveEditBookmarkFolder()`.
private String oldFolderNameString;
+ // `fileChooserCallback` is used in `onCreate()` and `onActivityResult()`.
+ private ValueCallback<Uri[]> fileChooserCallback;
+
// The download strings are used in `onCreate()` and `onRequestPermissionResult()`.
private String downloadUrl;
private String downloadContentDisposition;
// `downloadImageUrl` is used in `onCreateContextMenu()` and `onRequestPermissionResult()`.
private String downloadImageUrl;
+ // The user agent variables are used in `onCreate()` and `applyDomainSettings()`.
+ private ArrayAdapter<CharSequence> userAgentNamesArray;
+ private String[] userAgentDataArray;
+
// The request codes are used in `onCreate()`, `onCreateContextMenu()`, `onCloseDownloadLocationPermissionDialog()`, and `onRequestPermissionResult()`.
private final int DOWNLOAD_FILE_REQUEST_CODE = 1;
private final int DOWNLOAD_IMAGE_REQUEST_CODE = 2;
// Remove Android Studio's warning about deprecations. We have to use the deprecated `getColor()` until API >= 23.
@SuppressWarnings("deprecation")
protected void onCreate(Bundle savedInstanceState) {
- // Get a handle for `sharedPreferences`. `this` references the current context.
+ // Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Get the theme preference.
+ // Get the theme and screenshot preferences.
darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+ allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+
+ // Disable screenshots if not allowed.
+ if (!allowScreenshots) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+ }
// Set the activity theme.
if (darkTheme) {
startActivity(bookmarksIntent);
});
- // Set the create new bookmark folder FAB to display the `AlertDialog`.
+ // 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(), getResources().getString(R.string.create_folder));
});
- // Set the create new bookmark FAB to display the `AlertDialog`.
+ // 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();
// Hide the `appBar`.
appBar.hide();
- // Hide the `BannerAd` in the free flavor.
+ // Hide the banner ad in the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- BannerAd.hideAd(adView);
+ // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ AdHelper.hideAd(findViewById(R.id.adview));
}
// Modify the system bars.
// Show the `BannerAd` in the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- // Reload the ad. Because the screen may have rotated, we need to use `reloadAfterRotate`.
- BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
-
- // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
- adView = findViewById(R.id.adview);
+ // Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id));
}
// Remove the translucent navigation bar flag if it is set.
// Convert the id from long to int to match the format of the bookmarks database.
int databaseID = (int) id;
- // Get the bookmark `Cursor` for this ID and move it to the first row.
+ // Get the bookmark cursor for this ID and move it to the first row.
Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmarkCursor(databaseID);
bookmarkCursor.moveToFirst();
mainWebView.setVisibility(View.VISIBLE);
}
- //Stop the `SwipeToRefresh` indicator if it is running
+ //Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.setRefreshing(false);
}
}
webViewTitle = title;
}
- // Enter full screen video
+ // Enter full screen video.
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
// Pause the ad if this is the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- BannerAd.pauseAd(adView);
+ // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ AdHelper.pauseAd(findViewById(R.id.adview));
}
// Remove the translucent overlays.
fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
}
- // Exit full screen video
+ // Exit full screen video.
+ @Override
public void onHideCustomView() {
// Hide `fullScreenVideoFrameLayout`.
fullScreenVideoFrameLayout.removeAllViews();
// Show the ad if this is the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- // Reload the ad. Because the screen may have rotated, we need to use `reloadAfterRotate`.
- BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
+ // Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id));
+ }
+ }
- // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
- adView = findViewById(R.id.adview);
+ // Upload files.
+ @Override
+ public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
+ // Show the file chooser if the device is running API >= 21.
+ if (Build.VERSION.SDK_INT >= 21) {
+ // Store the file path callback.
+ fileChooserCallback = filePathCallback;
+
+ // Create an intent to open a chooser based ont the file chooser parameters.
+ Intent fileChooserIntent = fileChooserParams.createIntent();
+
+ // Open the file chooser. Currently only one `startActivityForResult` exists in this activity, so the request code, used to differentiate them, is simply `0`.
+ startActivityForResult(fileChooserIntent, 0);
}
+ return true;
}
});
// Initialize `inFullScreenBrowsingMode`, which is always false at this point because Privacy Browser never starts in full screen browsing mode.
inFullScreenBrowsingMode = false;
- // Initialize AdView for the free flavor.
- adView = findViewById(R.id.adview);
-
// Initialize the privacy settings variables.
javaScriptEnabled = false;
firstPartyCookiesEnabled = false;
saveFormDataEnabled = false;
nightMode = false;
- // Initialize `webViewTitle`.
+ // Initialize the WebView title.
webViewTitle = getString(R.string.no_title);
- // Initialize `favoriteIconBitmap`. `ContextCompat` must be used until API >= 21.
+ // Initialize the favorite icon bitmap. `ContextCompat` must be used until API >= 21.
Drawable favoriteIconDrawable = ContextCompat.getDrawable(getApplicationContext(), R.drawable.world);
BitmapDrawable favoriteIconBitmapDrawable = (BitmapDrawable) favoriteIconDrawable;
assert favoriteIconBitmapDrawable != null;
favoriteIconBitmap = favoriteIconDefaultBitmap;
}
+ // Initialize the user agent array adapter and string array.
+ userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.domain_settings_spinner_item);
+ userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
+
// Apply the app settings from the shared preferences.
applyAppSettings();
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http")) { // Load the URL in Privacy Browser.
// Apply the domain settings for the new URL.
- applyDomainSettings(url, true);
+ applyDomainSettings(url, true, false);
// Returning false causes the current `WebView` to handle the URL and prevents it from adding redirects to the history list.
return false;
// Apply any custom domain settings if the URL was loaded by navigating history.
if (navigatingHistory) {
- applyDomainSettings(url, true);
+ applyDomainSettings(url, true, false);
}
// 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.
inputMethodManager.showSoftInput(urlTextBox, 0);
// Apply the domain settings. This clears any settings from the previous domain.
- applyDomainSettings(formattedUrlString, true);
+ applyDomainSettings(formattedUrlString, true, false);
} else { // `WebView` has loaded a webpage.
// Set `formattedUrlString`.
formattedUrlString = url;
// Apply the domain settings if returning from the Domains activity.
if (reapplyDomainSettingsOnRestart) {
// Reapply the domain settings.
- applyDomainSettings(formattedUrlString, false);
+ applyDomainSettings(formattedUrlString, false, true);
// Reset `reapplyDomainSettingsOnRestart`.
reapplyDomainSettingsOnRestart = false;
// Resume the adView for the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- BannerAd.resumeAd(adView);
+ // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ AdHelper.resumeAd(findViewById(R.id.adview));
}
}
// Pause the adView or it will continue to consume resources in the background on the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- BannerAd.pauseAd(adView);
+ // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ AdHelper.pauseAd(findViewById(R.id.adview));
}
super.onPause();
MenuItem toggleFirstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies);
MenuItem toggleThirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
- MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);
+ MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
// Only display third-party cookies if SDK >= 21
toggleThirdPartyCookiesMenuItem.setVisible(Build.VERSION.SDK_INT >= 21);
// Get the shared preference values. `this` references the current context.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Set the status of the additional app bar icons. The default is `false`.
+ // Set the status of the additional app bar icons. Setting the refresh menu item to `SHOW_AS_ACTION_ALWAYS` makes it appear even on small devices like phones.
if (sharedPreferences.getBoolean("display_additional_app_bar_icons", false)) {
toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
- toggleSaveFormDataMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
} else { //Do not display the additional icons.
toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- toggleSaveFormDataMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
}
return true;
MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);
MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
+ MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images);
- MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
+ MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
// Set the text for the domain menu item.
if (domainSettingsApplied) {
toggleThirdPartyCookiesMenuItem.setChecked(thirdPartyCookiesEnabled);
toggleDomStorageMenuItem.setChecked(domStorageEnabled);
toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled);
+ swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled());
displayImagesMenuItem.setChecked(mainWebView.getSettings().getLoadsImagesAutomatically());
// Enable third-party cookies if first-party cookies are enabled.
fontSizeMenuItem.setTitle(fontSizeTitle);
selectedFontSizeMenuItem.setChecked(true);
- // Only show `Refresh` if `swipeToRefresh` is disabled.
- refreshMenuItem.setVisible(!swipeToRefreshEnabled);
+ // Only show Ad Consent if this is the free flavor.
+ adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
// Run all the other default commands.
super.onPrepareOptionsMenu(menu);
// Set the commands that relate to the menu entries.
switch (menuItemId) {
- case R.id.add_or_edit_domain:
- if (domainSettingsApplied) { // Edit the current domain settings.
- // Reapply the domain settings on returning to `MainWebViewActivity`.
- reapplyDomainSettingsOnRestart = true;
- currentDomainName = "";
-
- // Create an intent to launch the domains activity.
- Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
- // Put extra information instructing the domains activity to directly load the current domain.
- domainsIntent.putExtra("LoadDomain", domainSettingsDatabaseId);
-
- // Make it so.
- startActivity(domainsIntent);
- } else { // Add a new domain.
- // Show the add domain `AlertDialog`.
- AppCompatDialogFragment addDomainDialog = new AddDomainDialog();
- addDomainDialog.show(getSupportFragmentManager(), getResources().getString(R.string.add_domain));
- }
- return true;
-
case R.id.toggle_javascript:
// Switch the status of javaScriptEnabled.
javaScriptEnabled = !javaScriptEnabled;
mainWebView.reload();
return true;
+ case R.id.add_or_edit_domain:
+ if (domainSettingsApplied) { // Edit the current domain settings.
+ // Reapply the domain settings on returning to `MainWebViewActivity`.
+ reapplyDomainSettingsOnRestart = true;
+ currentDomainName = "";
+
+ // Create an intent to launch the domains activity.
+ Intent domainsIntent = new Intent(this, DomainsActivity.class);
+
+ // Put extra information instructing the domains activity to directly load the current domain and close on back instead of returning to the domains list.
+ domainsIntent.putExtra("loadDomain", domainSettingsDatabaseId);
+ domainsIntent.putExtra("closeOnBack", true);
+
+ // Make it so.
+ startActivity(domainsIntent);
+ } else { // Add a new domain.
+ // Apply the new domain settings on returning to `MainWebViewActivity`.
+ reapplyDomainSettingsOnRestart = true;
+ currentDomainName = "";
+
+ // Get the current domain
+ Uri currentUri = Uri.parse(formattedUrlString);
+ String currentDomain = currentUri.getHost();
+
+ // 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);
+
+ // Create the domain and store the database ID.
+ int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
+
+ // Create an intent to launch the domains activity.
+ Intent domainsIntent = new Intent(this, DomainsActivity.class);
+
+ // Put extra information instructing the domains activity to directly load the new domain and close on back instead of returning to the domains list.
+ domainsIntent.putExtra("loadDomain", newDomainDatabaseId);
+ domainsIntent.putExtra("closeOnBack", true);
+
+ // Make it so.
+ startActivity(domainsIntent);
+ }
+ return true;
+
case R.id.toggle_first_party_cookies:
// Switch the status of firstPartyCookiesEnabled.
firstPartyCookiesEnabled = !firstPartyCookiesEnabled;
// Display a `Snackbar`.
if (firstPartyCookiesEnabled) { // First-party cookies are enabled.
Snackbar.make(findViewById(R.id.main_webview), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
- } else if (javaScriptEnabled){ // JavaScript is still enabled.
+ } else if (javaScriptEnabled) { // JavaScript is still enabled.
Snackbar.make(findViewById(R.id.main_webview), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
} else { // Privacy mode.
Snackbar.make(findViewById(R.id.main_webview), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
mainWebView.getSettings().setTextZoom(200);
return true;
+ case R.id.swipe_to_refresh:
+ // Toggle swipe to refresh.
+ swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled());
+ return true;
+
case R.id.display_images:
if (mainWebView.getSettings().getLoadsImagesAutomatically()) { // Images are currently loaded automatically.
mainWebView.getSettings().setLoadsImagesAutomatically(false);
onTheFlyDisplayImagesSet = true;
return true;
+ case R.id.view_source:
+ // Launch the View Source activity.
+ Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+ startActivity(viewSourceIntent);
+ return true;
+
case R.id.share:
// Setup the share string.
String shareString = webViewTitle + " – " + urlTextBox.getText().toString();
printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
return true;
- case R.id.view_source:
- // Launch the Vew Source activity.
- Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
- startActivity(viewSourceIntent);
- return true;
-
case R.id.add_to_homescreen:
// Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
mainWebView.reload();
return true;
+ case R.id.ad_consent:
+ // Display the ad consent dialog.
+ DialogFragment adConsentDialogFragment = new AdConsentDialog();
+ adConsentDialogFragment.show(getFragmentManager(), getString(R.string.ad_consent));
+ return true;
+
default:
// Don't consume the event.
return super.onOptionsItemSelected(menuItem);
// Reload the ad for the free flavor if we are not in full screen mode.
if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
- // Reload the ad.
- BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
-
- // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
- adView = findViewById(R.id.adview);
+ // Reload the ad. The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
+ AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_id));
}
// `invalidateOptionsMenu` should recalculate the number of action buttons from the menu to display on the app bar, but it doesn't because of the this bug:
// Set the target URL as the title of the `ContextMenu`.
menu.setHeaderTitle(linkUrl);
- // Add a `Load URL` entry.
- menu.add(R.string.load_url).setOnMenuItemClickListener(item -> {
+ // Add a Load URL entry.
+ menu.add(R.string.load_url).setOnMenuItemClickListener((MenuItem item) -> {
loadUrl(linkUrl);
return false;
});
- // Add a `Copy URL` entry.
- menu.add(R.string.copy_url).setOnMenuItemClickListener(item -> {
+ // Add a Copy URL entry.
+ menu.add(R.string.copy_url).setOnMenuItemClickListener((MenuItem item) -> {
// Save the link URL in a `ClipData`.
ClipData srcAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), linkUrl);
return false;
});
+ // Add a Download URL entry.
+ menu.add(R.string.download_url).setOnMenuItemClickListener((MenuItem item) -> {
+ // Check to see if the WRITE_EXTERNAL_STORAGE permission has already been granted.
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
+ // The WRITE_EXTERNAL_STORAGE permission needs to be requested.
+
+ // Store the variables for future use by `onRequestPermissionsResult()`.
+ downloadUrl = linkUrl;
+ downloadContentDisposition = "none";
+ downloadContentLength = -1;
+
+ // Show a dialog if the user has previously denied the permission.
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
+ // Get a handle for the download location permission alert dialog and set the download type to DOWNLOAD_FILE.
+ 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));
+ } 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 WRITE_EXTERNAL_STORAGE permission has already been granted.
+ // Get a handle for the download file alert dialog.
+ AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(linkUrl, "none", -1);
+
+ // Show the download file alert dialog.
+ downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
+ }
+ return false;
+ });
+
// Add a `Cancel` entry, which by default closes the `ContextMenu`.
menu.add(R.string.cancel);
break;
}
}
- @Override
- public void onAddDomain(AppCompatDialogFragment dialogFragment) {
- // Reapply the domain settings on returning to `MainWebViewActivity`.
- reapplyDomainSettingsOnRestart = true;
- currentDomainName = "";
-
- // Get the new domain name `String` from `dialogFragment`.
- EditText domainNameEditText = dialogFragment.getDialog().findViewById(R.id.domain_name_edittext);
- String domainNameString = domainNameEditText.getText().toString();
-
- // Initialize the database handler. `this` specifies the context. The two `nulls` do not specify the database name or a `CursorFactory`.
- // 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);
-
- // Create the domain and store the database ID in `currentDomainDatabaseId`.
- int newDomainDatabaseId = domainsDatabaseHelper.addDomain(domainNameString);
-
- // Create an intent to launch the domains activity.
- Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
- // Put extra information instructing the domains activity to directly load the current domain.
- domainsIntent.putExtra("LoadDomain", newDomainDatabaseId);
-
- // Make it so.
- startActivity(domainsIntent);
- }
-
@Override
public void onCreateBookmark(AppCompatDialogFragment dialogFragment) {
// Get the `EditTexts` from the `dialogFragment`.
}
}
+ // Process the results of an upload file chooser. Currently there is only one `startActivityForResult` in this activity, so the request code, used to differentiate them, is ignored.
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // File uploads only work on API >= 21.
+ if (Build.VERSION.SDK_INT >= 21) {
+ // Pass the file to the WebView.
+ fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
+ }
+ }
+
private void loadUrlFromTextBox() throws UnsupportedEncodingException {
// Get the text from urlTextBox and convert it to a string. trim() removes white spaces from the beginning and end of the string.
String unformattedUrlString = urlTextBox.getText().toString().trim();
private void loadUrl(String url) {
// Apply any custom domain settings.
- applyDomainSettings(url, true);
+ applyDomainSettings(url, true, false);
// Load the URL.
mainWebView.loadUrl(url, customHeaders);
fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
hideSystemBarsOnFullscreen = sharedPreferences.getBoolean("hide_system_bars", false);
translucentNavigationBarOnFullscreen = sharedPreferences.getBoolean("translucent_navigation_bar", true);
- swipeToRefreshEnabled = sharedPreferences.getBoolean("swipe_to_refresh", false);
displayWebpageImagesBoolean = sharedPreferences.getBoolean("display_webpage_images", true);
// Set the homepage, search, and proxy options.
waitingForOrbot = false;
}
- // Set swipe to refresh.
- swipeRefreshLayout.setEnabled(swipeToRefreshEnabled);
-
// Set Do Not Track status.
if (doNotTrackEnabled) {
customHeaders.put("DNT", "1");
// Show the `BannerAd` in the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
- // Reload the ad. Because the screen may have rotated, we need to use `reloadAfterRotate`.
- BannerAd.reloadAfterRotate(adView, getApplicationContext(), getString(R.string.ad_id));
-
- // Reinitialize the `adView` variable, as the `View` will have been removed and re-added by `BannerAd.reloadAfterRotate()`.
- adView = findViewById(R.id.adview);
+ // 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.ad_id));
}
// Remove the translucent navigation bar flag if it is set.
}
}
- //
+ // `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled.
// The deprecated `.getDrawable()` must be used until the minimum API >= 21.
@SuppressWarnings("deprecation")
- private void applyDomainSettings(String url, boolean resetFavoriteIcon) {
+ private void applyDomainSettings(String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
// Reset `navigatingHistory`.
navigatingHistory = false;
// Store the general preference information.
String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
- String defaultUserAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0");
+ String defaultUserAgentName = sharedPreferences.getString("user_agent", "Privacy Browser");
String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
+ boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
nightMode = sharedPreferences.getBoolean("night_mode", false);
if (domainSettingsApplied) { // The url we are loading has custom domain settings.
easyPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
fanboysAnnoyanceListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);
fanboysSocialBlockingListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
- String userAgentString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
+ String userAgentName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
- displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
+ int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
+ displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
pinnedDomainSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
pinnedDomainSslIssuedToCNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
pinnedDomainSslIssuedToONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
// Only set the user agent if the webpage is not currently loading. Otherwise, changing the user agent on redirects can cause the original website to reload.
// <https://redmine.stoutner.com/issues/160>
if (!urlIsLoading) {
- switch (userAgentString) {
- case "System default user agent":
- // Set the user agent according to the system default.
- switch (defaultUserAgentString) {
- case "WebView default user agent":
- // Set the user agent to `""`, which uses the default value.
- mainWebView.getSettings().setUserAgentString("");
- break;
-
- case "Custom user agent":
- // Set the custom user agent.
- mainWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
- break;
-
- default:
- // Use the selected user agent.
- mainWebView.getSettings().setUserAgentString(defaultUserAgentString);
- }
+ // Set the user agent.
+ if (userAgentName.equals(getString(R.string.system_default_user_agent))) { // Use the system default user agent.
+ // Get the array position of the default user agent name.
+ int defaultUserAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
+
+ // Set the user agent according to the system default.
+ switch (defaultUserAgentArrayPosition) {
+ case UNRECOGNIZED_USER_AGENT: // The default user agent name is not on the canonical list.
+ // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
+ mainWebView.getSettings().setUserAgentString(defaultUserAgentName);
+ break;
+
+ case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
+ // Set the user agent to `""`, which uses the default value.
+ mainWebView.getSettings().setUserAgentString("");
+ break;
+
+ case SETTINGS_CUSTOM_USER_AGENT:
+ // Set the custom user agent.
+ mainWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
+ break;
+
+ default:
+ // Get the user agent string from the user agent data array
+ mainWebView.getSettings().setUserAgentString(userAgentDataArray[defaultUserAgentArrayPosition]);
+ }
+ } else { // Set the user agent according to the stored name.
+ // Get the array position of the user agent name.
+ int userAgentArrayPosition = userAgentNamesArray.getPosition(userAgentName);
+
+ switch (userAgentArrayPosition) {
+ case UNRECOGNIZED_USER_AGENT: // The user agent name contains a custom user agent.
+ mainWebView.getSettings().setUserAgentString(userAgentName);
+ break;
+
+ case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
+ // Set the user agent to `""`, which uses the default value.
+ mainWebView.getSettings().setUserAgentString("");
+ break;
+
+ default:
+ // Get the user agent string from the user agent data array.
+ mainWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
+ }
+ }
+
+ // Set swipe to refresh.
+ switch (swipeToRefreshInt) {
+ case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
+ // Set swipe to refresh according to the default.
+ swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
break;
- case "WebView default user agent":
- // Set the user agent to `""`, which uses the default value.
- mainWebView.getSettings().setUserAgentString("");
+ case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED:
+ // Enable swipe to refresh.
+ swipeRefreshLayout.setEnabled(true);
break;
- default:
- // Use the selected user agent.
- mainWebView.getSettings().setUserAgentString(userAgentString);
+ case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED:
+ // Disable swipe to refresh.
+ swipeRefreshLayout.setEnabled(false);
}
- // Store the applied user agent string.
+ // Store the applied user agent string, which is used in the View Source activity.
appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
}
mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
mainWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+ swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
// Reset the pinned SSL certificate information.
domainSettingsDatabaseId = -1;
// Only set the user agent if the webpage is not currently loading. Otherwise, changing the user agent on redirects can cause the original website to reload.
// <https://redmine.stoutner.com/issues/160>
if (!urlIsLoading) {
- switch (defaultUserAgentString) {
- case "WebView default user agent":
+ // Get the array position of the user agent name.
+ int userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
+
+ // Set the user agent.
+ switch (userAgentArrayPosition) {
+ case UNRECOGNIZED_USER_AGENT: // The default user agent name is not on the canonical list.
+ // This is probably because it was set in an older version of Privacy Browser before the switch to persistent user agent names.
+ mainWebView.getSettings().setUserAgentString(defaultUserAgentName);
+ break;
+
+ case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
// Set the user agent to `""`, which uses the default value.
mainWebView.getSettings().setUserAgentString("");
break;
- case "Custom user agent":
+ case SETTINGS_CUSTOM_USER_AGENT:
// Set the custom user agent.
mainWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
break;
default:
- // Use the selected user agent.
- mainWebView.getSettings().setUserAgentString(defaultUserAgentString);
+ // Get the user agent string from the user agent data array
+ mainWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
}
- // Store the applied user agent string.
+ // Store the applied user agent string, which is used in the View Source activity.
appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
}
if (mainMenu != null) {
updatePrivacyIcons(true);
}
+
+ // Reload the website if returning from the Domains activity.
+ if (reloadWebsite) {
+ mainWebView.reload();
+ }
}
}
}
private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) {
- // Get handles for the icons.
- MenuItem privacyIconMenuItem = mainMenu.findItem(R.id.toggle_javascript);
- MenuItem firstPartyCookiesIconMenuItem = mainMenu.findItem(R.id.toggle_first_party_cookies);
- MenuItem domStorageIconMenuItem = mainMenu.findItem(R.id.toggle_dom_storage);
- MenuItem formDataIconMenuItem = mainMenu.findItem(R.id.toggle_save_form_data);
+ // Get handles for the menu items.
+ MenuItem privacyMenuItem = mainMenu.findItem(R.id.toggle_javascript);
+ MenuItem firstPartyCookiesMenuItem = mainMenu.findItem(R.id.toggle_first_party_cookies);
+ MenuItem domStorageMenuItem = mainMenu.findItem(R.id.toggle_dom_storage);
+ MenuItem refreshMenuItem = mainMenu.findItem(R.id.refresh);
- // Update `privacyIcon`.
+ // Update the privacy icon.
if (javaScriptEnabled) { // JavaScript is enabled.
- privacyIconMenuItem.setIcon(R.drawable.javascript_enabled);
+ privacyMenuItem.setIcon(R.drawable.javascript_enabled);
} else if (firstPartyCookiesEnabled) { // JavaScript is disabled but cookies are enabled.
- privacyIconMenuItem.setIcon(R.drawable.warning);
+ privacyMenuItem.setIcon(R.drawable.warning);
} else { // All the dangerous features are disabled.
- privacyIconMenuItem.setIcon(R.drawable.privacy_mode);
+ privacyMenuItem.setIcon(R.drawable.privacy_mode);
}
- // Update `firstPartyCookiesIcon`.
+ // Update the first-party cookies icon.
if (firstPartyCookiesEnabled) { // First-party cookies are enabled.
- firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_enabled);
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
} else { // First-party cookies are disabled.
if (darkTheme) {
- firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled_dark);
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_dark);
} else {
- firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled_light);
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_light);
}
}
- // Update `domStorageIcon`.
+ // Update the DOM storage icon.
if (javaScriptEnabled && domStorageEnabled) { // Both JavaScript and DOM storage are enabled.
- domStorageIconMenuItem.setIcon(R.drawable.dom_storage_enabled);
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
} else if (javaScriptEnabled) { // JavaScript is enabled but DOM storage is disabled.
if (darkTheme) {
- domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled_dark);
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_dark);
} else {
- domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled_light);
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_light);
}
} else { // JavaScript is disabled, so DOM storage is ghosted.
if (darkTheme) {
- domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark);
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark);
} else {
- domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted_light);
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_light);
}
}
- // Update `formDataIcon`.
- if (saveFormDataEnabled) { // Form data is enabled.
- formDataIconMenuItem.setIcon(R.drawable.form_data_enabled);
- } else { // Form data is disabled.
- if (darkTheme) {
- formDataIconMenuItem.setIcon(R.drawable.form_data_disabled_dark);
- } else {
- formDataIconMenuItem.setIcon(R.drawable.form_data_disabled_light);
- }
+ // Update the refresh icon.
+ if (darkTheme) {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_dark);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_light);
}
// `invalidateOptionsMenu` calls `onPrepareOptionsMenu()` and redraws the icons in the `AppBar`.
}
private void loadBookmarksFolder() {
- // Update `bookmarksCursor` with the contents of the bookmarks database for the current folder.
+ // Update the bookmarks cursor with the contents of the bookmarks database for the current folder.
bookmarksCursor = bookmarksDatabaseHelper.getAllBookmarksCursorByDisplayOrder(currentBookmarksFolder);
// Populate the bookmarks cursor adapter. `this` specifies the `Context`. `false` disables `autoRequery`.
ImageView bookmarkFavoriteIcon = view.findViewById(R.id.bookmark_favorite_icon);
TextView bookmarkNameTextView = view.findViewById(R.id.bookmark_name);
- // Get the favorite icon byte array from the `Cursor`.
+ // Get the favorite icon byte array from the cursor.
byte[] favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndex(BookmarksDatabaseHelper.FAVORITE_ICON));
// Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.