import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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.WebSettings;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentPagerAdapter;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager.widget.ViewPager;
import com.stoutner.privacybrowser.BuildConfig;
import com.stoutner.privacybrowser.R;
+import com.stoutner.privacybrowser.adapters.WebViewPagerAdapter;
import com.stoutner.privacybrowser.asynctasks.GetHostIpAddresses;
import com.stoutner.privacybrowser.dialogs.AdConsentDialog;
import com.stoutner.privacybrowser.dialogs.CreateBookmarkDialog;
import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog;
import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog;
import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
-import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
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.CheckPinnedMismatchHelper;
import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
import com.stoutner.privacybrowser.helpers.OrbotProxyHelper;
import com.stoutner.privacybrowser.views.NestedScrollWebView;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
// AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
- EditBookmarkFolderDialog.EditBookmarkFolderListener, HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, WebViewTabFragment.NewTabListener,
- PinnedMismatchDialog.PinnedMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener {
-
- // TODO Consider removing
- // `darkTheme` is public static so it can be accessed from everywhere.
- public static boolean darkTheme;
-
- // TODO Consider removing
- // `allowScreenshots` is public static so it can be accessed from everywhere. It is also used in `onCreate()`.
- public static boolean allowScreenshots;
-
- // TODO Remove
- // `favoriteIconBitmap` is public static so it can be accessed from `BookmarksActivity`, `BookmarksDatabaseViewActivity`, `CreateBookmarkFolderDialog`,
- // `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `EditBookmarkDatabaseViewDialog`, and `ViewSslCertificateDialog`. It is also used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`,
- // `onCreateHomeScreenShortcut()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`.
- public static Bitmap favoriteIconBitmap;
-
- // TODO Remove
- // `favoriteIconDefaultBitmap` public static so it can be accessed from `PinnedMismatchDialog`. It is also used in `onCreate()` and `applyDomainSettings`.
- public static Bitmap favoriteIconDefaultBitmap;
-
- // TODO Consider removing the formatted URL string.
- // `formattedUrlString` is public static so it can be accessed from `AddDomainDialog`, `BookmarksActivity`, `DomainSettingsFragment`, and `PinnedMismatchDialog`.
- // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, `loadUrlFromTextBox()`, and `applyProxyThroughOrbot()`.
- 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()` and `checkPinnedMismatch()`.
- public static SslCertificate sslCertificate;
-
- // `currentHostIpAddresses` is public static so it can be accessed from `DomainSettingsFragment`, `GetHostIpAddresses()`, and `ViewSslCertificateDialog`.
- // It is also used in `onCreate()` and `GetHostIpAddresses()`.
- public static String currentHostIpAddresses;
-
- // The getting IP addresses tracker is used in `onCreate() and `GetHostIpAddresses`.
- public static boolean gettingIpAddresses;
-
- // The URL loading tracker is public static so it can be accessed from `GetHostIpAddresses`.
- // It is also used in `onCreate()`, `onCreateOptionsMenu()`, `loadUrl()`, `applyDomainSettings()`, and `GetHostIpAddresses`.
- public static boolean urlIsLoading;
+ EditBookmarkFolderDialog.EditBookmarkFolderListener, NavigationView.OnNavigationItemSelectedListener, WebViewTabFragment.NewTabListener {
// `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
public static String orbotStatus;
- // `appliedUserAgentString` is public static so it can be accessed from `ViewSourceActivity`. It is also used in `applyDomainSettings()`.
- public static String appliedUserAgentString;
-
- // `reloadOnRestart` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()`
- public static boolean reloadOnRestart;
+ // The WebView pager adapter is accessed from `HttpAuthenticationDialog`, `PinnedMismatchDialog`, and `SslCertificateErrorDialog`. It is also used in `onCreate()`, `onResume()`, and `addTab()`.
+ public static WebViewPagerAdapter webViewPagerAdapter;
- // `reloadUrlOnRestart` is public static so it can be accessed from `SettingsFragment` and `BookmarksActivity`. It is also used in `onRestart()`.
+ // The load URL on restart variables are public static so they can be accessed from `BookmarksActivity`. They are used in `onRestart()`.
public static boolean loadUrlOnRestart;
+ public static String urlToLoadOnRestart;
// `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onRestart()`.
public static boolean restartFromBookmarksActivity;
- // The block list versions are public static so they can be accessed from `AboutTabFragment`. They are also used in `onCreate()`.
- public static String easyListVersion;
- public static String easyPrivacyVersion;
- public static String fanboysAnnoyanceVersion;
- public static String fanboysSocialVersion;
- public static String ultraPrivacyVersion;
-
- // The request items are public static so they can be accessed by `BlockListHelper`, `RequestsArrayAdapter`, and `ViewRequestsDialog`. They are also used in `onCreate()` and `onPrepareOptionsMenu()`.
- public static List<String[]> resourceRequests;
- public static String[] whiteListResultStringArray;
- private int blockedRequests;
- private int easyListBlockedRequests;
- private int easyPrivacyBlockedRequests;
- private int fanboysAnnoyanceListBlockedRequests;
- private int fanboysSocialBlockingListBlockedRequests;
- private int ultraPrivacyBlockedRequests;
- private int thirdPartyBlockedRequests;
-
- public final static int REQUEST_DISPOSITION = 0;
- public final static int REQUEST_URL = 1;
- public final static int REQUEST_BLOCKLIST = 2;
- public final static int REQUEST_SUBLIST = 3;
- public final static int REQUEST_BLOCKLIST_ENTRIES = 4;
- public final static int REQUEST_BLOCKLIST_ORIGINAL_ENTRY = 5;
-
- public final static int REQUEST_DEFAULT = 0;
- public final static int REQUEST_ALLOWED = 1;
- public final static int REQUEST_THIRD_PARTY = 2;
- public final static int REQUEST_BLOCKED = 3;
-
- public final static int MAIN_WHITELIST = 1;
- public final static int FINAL_WHITELIST = 2;
- public final static int DOMAIN_WHITELIST = 3;
- public final static int DOMAIN_INITIAL_WHITELIST = 4;
- public final static int DOMAIN_FINAL_WHITELIST = 5;
- public final static int THIRD_PARTY_WHITELIST = 6;
- public final static int THIRD_PARTY_DOMAIN_WHITELIST = 7;
- public final static int THIRD_PARTY_DOMAIN_INITIAL_WHITELIST = 8;
-
- public final static int MAIN_BLACKLIST = 9;
- public final static int INITIAL_BLACKLIST = 10;
- public final static int FINAL_BLACKLIST = 11;
- public final static int DOMAIN_BLACKLIST = 12;
- public final static int DOMAIN_INITIAL_BLACKLIST = 13;
- public final static int DOMAIN_FINAL_BLACKLIST = 14;
- public final static int DOMAIN_REGULAR_EXPRESSION_BLACKLIST = 15;
- public final static int THIRD_PARTY_BLACKLIST = 16;
- public final static int THIRD_PARTY_INITIAL_BLACKLIST = 17;
- public final static int THIRD_PARTY_DOMAIN_BLACKLIST = 18;
- public final static int THIRD_PARTY_DOMAIN_INITIAL_BLACKLIST = 19;
- public final static int THIRD_PARTY_REGULAR_EXPRESSION_BLACKLIST = 20;
- public final static int THIRD_PARTY_DOMAIN_REGULAR_EXPRESSION_BLACKLIST = 21;
- public final static int REGULAR_EXPRESSION_BLACKLIST = 22;
-
- // `blockAllThirdPartyRequests` is public static so it can be accessed from `RequestsActivity`.
- // It is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`
- public static boolean blockAllThirdPartyRequests;
-
// `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`. It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`,
// `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
public static String currentBookmarksFolder;
- // 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;
- public static String pinnedSslIssuedByCName;
- public static String pinnedSslIssuedByOName;
- public static String pinnedSslIssuedByUName;
- public static Date pinnedSslStartDate;
- public static Date pinnedSslEndDate;
- public static String pinnedHostIpAddresses;
-
// 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;
- // `pinnedDomainSslCertificate` is used in `onCreate()`, `applyDomainSettings()`, and `checkPinnedMismatch()`.
- private static boolean pinnedSslCertificate;
-
- // `pinnedIpAddress` is used in `applyDomainSettings()` and `checkPinnedMismatch()`.
- private static boolean pinnedIpAddresses;
-
- // `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;
-
-
- // A handle for the activity is set in `onCreate()` and accessed in `WebViewPagerAdapter`.
- private Activity activity;
-
- // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`.
- private boolean navigatingHistory;
-
// The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
// `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxyThroughOrbot()`, and `applyDomainSettings()`.
private NestedScrollWebView currentWebView;
- // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`.
- private FrameLayout fullScreenVideoFrameLayout;
-
- // `cookieManager` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`, `loadUrlFromTextBox()`, `onDownloadImage()`, `onDownloadFile()`, and `onRestart()`.
- private CookieManager cookieManager;
-
// `customHeader` is used in `onCreate()`, `onOptionsItemSelected()`, `onCreateContextMenu()`, and `loadUrl()`.
private final Map<String, String> customHeaders = new HashMap<>();
- // `javaScriptEnabled` is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
- private boolean javaScriptEnabled;
-
- // `firstPartyCookiesEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onDownloadImage()`, `onDownloadFile()`, and `applyDomainSettings()`.
- private boolean firstPartyCookiesEnabled;
-
- // `thirdPartyCookiesEnabled` used in `onCreate()`, `onPrepareOptionsMenu()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
- private boolean thirdPartyCookiesEnabled;
-
- // `domStorageEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
- private boolean domStorageEnabled;
-
- // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. It can be removed once the minimum API >= 26.
- private boolean saveFormDataEnabled;
-
- // `nightMode` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
- private boolean nightMode;
-
- // 'homepage' is used in `onCreate()`, `onNavigationItemSelected()`, and `applyProxyThroughOrbot()`.
- private String homepage;
-
- // `searchURL` is used in `loadURLFromTextBox()` and `applyProxyThroughOrbot()`.
+ // The search URL is set in `applyProxyThroughOrbot()` and used in `onCreate()`, `onNewIntent()`, `loadURLFromTextBox()`, and `initializeWebView()`.
private String searchURL;
- // `mainMenu` is used in `onCreateOptionsMenu()` and `updatePrivacyIcons()`.
- private Menu mainMenu;
-
- // `refreshMenuItem` is used in `onCreate()` and `onCreateOptionsMenu()`.
- private MenuItem refreshMenuItem;
-
- // The WebView pager adapter is used in `onCreate()`, `onResume()`, and `addTab()`.
- private WebViewPagerAdapter webViewPagerAdapter;
-
- // The navigation requests menu item is used in `onCreate()` and accessed from `WebViewPagerAdapter`.
- private MenuItem navigationRequestsMenuItem;
-
- // The blocklist helper is used in `onCreate()` and `WebViewPagerAdapter`.
- BlockListHelper blockListHelper;
+ // The options menu is set in `onCreateOptionsMenu()` and used in `onOptionsItemSelected()`, `updatePrivacyIcons()`, and `initializeWebView()`.
+ private Menu optionsMenu;
- // The blocklists are populated in `onCreate()` and accessed from `WebViewPagerAdapter`.
+ // The blocklists are populated in `onCreate()` and accessed from `initializeWebView()`.
private ArrayList<List<String[]>> easyList;
private ArrayList<List<String[]>> easyPrivacy;
private ArrayList<List<String[]>> fanboysAnnoyanceList;
private ArrayList<List<String[]>> fanboysSocialList;
private ArrayList<List<String[]>> ultraPrivacy;
- // The blocklist menu items are used in `onCreate()`, `onCreateOptionsMenu()`, and `onPrepareOptionsMenu()`.
- private MenuItem blocklistsMenuItem;
- private MenuItem easyListMenuItem;
- private MenuItem easyPrivacyMenuItem;
- private MenuItem fanboysAnnoyanceListMenuItem;
- private MenuItem fanboysSocialBlockingListMenuItem;
- private MenuItem ultraPrivacyMenuItem;
- private MenuItem blockAllThirdPartyRequestsMenuItem;
-
- // The blocklist variables are used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
- private boolean easyListEnabled;
- private boolean easyPrivacyEnabled;
- private boolean fanboysAnnoyanceListEnabled;
- private boolean fanboysSocialBlockingListEnabled;
- private boolean ultraPrivacyEnabled;
-
// `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`.
private String webViewDefaultUserAgent;
- // `defaultCustomUserAgentString` is used in `onPrepareOptionsMenu()` and `applyDomainSettings()`.
- private String defaultCustomUserAgentString;
-
- // `privacyBrowserRuntime` is used in `onCreate()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
- private Runtime privacyBrowserRuntime;
-
// `proxyThroughOrbot` is used in `onRestart()`, `onOptionsItemSelected()`, `applyAppSettings()`, and `applyProxyThroughOrbot()`.
private boolean proxyThroughOrbot;
- // `incognitoModeEnabled` is used in `onCreate()` and `applyAppSettings()`.
+ // The incognito mode is set in `applyAppSettings()` and used in `initializeWebView()`.
private boolean incognitoModeEnabled;
- // `fullScreenBrowsingModeEnabled` is used in `onCreate()` and `applyAppSettings()`.
+ // The full screen browsing mode tracker is set it `applyAppSettings()` and used in `initializeWebView()`.
private boolean fullScreenBrowsingModeEnabled;
// `inFullScreenBrowsingMode` is used in `onCreate()`, `onConfigurationChanged()`, and `applyAppSettings()`.
private boolean inFullScreenBrowsingMode;
- // Hide app bar is used in `onCreate()` and `applyAppSettings()`.
+ // The hide app bar tracker is used in `applyAppSettings()` and `initializeWebView()`.
private boolean hideAppBar;
// `reapplyDomainSettingsOnRestart` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, and `onAddDomain()`, .
// `displayingFullScreenVideo` is used in `onCreate()` and `onResume()`.
private boolean displayingFullScreenVideo;
- // `downloadWithExternalApp` is used in `onCreate()`, `onCreateContextMenu()`, and `applyDomainSettings()`.
- private boolean downloadWithExternalApp;
-
- // `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`.
- private String currentDomainName;
-
// `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`.
private BroadcastReceiver orbotStatusBroadcastReceiver;
// `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
private boolean waitingForOrbot;
- // `domainSettingsJavaScriptEnabled` is used in `onOptionsItemSelected()` and `applyDomainSettings()`.
- private Boolean domainSettingsJavaScriptEnabled;
-
- // `waitingForOrbotHtmlString` is used in `onCreate()` and `applyProxyThroughOrbot()`.
- private String waitingForOrbotHtmlString;
-
- // `privateDataDirectoryString` is used in `onCreate()`, `onOptionsItemSelected()`, and `onNavigationItemSelected()`.
- private String privateDataDirectoryString;
-
- // `findOnPageEditText` is used in `onCreate()`, `onOptionsItemSelected()`, and `closeFindOnPage()`.
- private EditText findOnPageEditText;
-
- // `displayAdditionalAppBarIcons` is used in `onCreate()` and `onCreateOptionsMenu()`.
- private boolean displayAdditionalAppBarIcons;
-
// The action bar drawer toggle is initialized in `onCreate()` and used in `onResume()`.
private ActionBarDrawerToggle actionBarDrawerToggle;
private int drawerHeaderPaddingTop;
private int drawerHeaderPaddingBottom;
- // `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`.
- private SslErrorHandler sslErrorHandler;
-
- // `httpAuthHandler` is used in `onCreate()`, `onHttpAuthenticationCancel()`, and `onHttpAuthenticationProceed()`.
- private static HttpAuthHandler httpAuthHandler;
-
- // `inputMethodManager` is used in `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `closeFindOnPage()`.
- private InputMethodManager inputMethodManager;
-
// `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`,
// and `loadBookmarksFolder()`.
private BookmarksDatabaseHelper bookmarksDatabaseHelper;
- // `bookmarksListView` is used in `onCreate()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, and `loadBookmarksFolder()`.
- private ListView bookmarksListView;
-
- // `bookmarksTitleTextView` is used in `onCreate()` and `loadBookmarksFolder()`.
- private TextView bookmarksTitleTextView;
-
// `bookmarksCursor` is used in `onDestroy()`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
private Cursor bookmarksCursor;
// `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()`, `onRequestPermissionResult()`, and `initializeWebView()`.
private final int DOWNLOAD_FILE_REQUEST_CODE = 1;
private final int DOWNLOAD_IMAGE_REQUEST_CODE = 2;
@Override
- // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled. The whole premise of Privacy Browser is built around an understanding of these dangers.
- // Also, remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`.
- @SuppressLint({"SetJavaScriptEnabled", "ClickableViewAccessibility"})
- // Remove Android Studio's warning about deprecations. The deprecated `getColor()` must be used until API >= 23.
- @SuppressWarnings("deprecation")
+ // Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`.
+ @SuppressLint("ClickableViewAccessibility")
protected void onCreate(Bundle savedInstanceState) {
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Get the theme and screenshot preferences.
- darkTheme = sharedPreferences.getBoolean("dark_theme", false);
- allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+ boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+ boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
// Disable screenshots if not allowed.
if (!allowScreenshots) {
// Set the content view.
setContentView(R.layout.main_framelayout);
- // Get handles for views, resources, and managers.
- activity = this;
- Resources resources = getResources();
- fragmentManager = getSupportFragmentManager();
- inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ // Get a handle for the input method.
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ // Remove the lint warning below that the input method manager might be null.
+ assert inputMethodManager != null;
+
+ // Get a handle for the toolbar.
Toolbar toolbar = findViewById(R.id.toolbar);
// Set the action bar. `SupportActionBar` must be used until the minimum API is >= 21.
setSupportActionBar(toolbar);
+
+ // Get a handle for the action bar.
ActionBar actionBar = getSupportActionBar();
// 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.
+ // Add the custom layout, which shows the URL text bar.
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));
- initialGrayColorSpan = new ForegroundColorSpan(resources.getColor(R.color.gray_500));
- finalGrayColorSpan = new ForegroundColorSpan(resources.getColor(R.color.gray_500));
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+ finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
- // Get a handle for `urlTextBox`.
+ // Get handles for the URL views.
EditText urlEditText = findViewById(R.id.url_edittext);
// Remove the formatting from `urlTextBar` when the user is editing the text.
}
});
- // Set `waitingForOrbotHTMLString`.
- waitingForOrbotHtmlString = "<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>";
-
- // Initialize `currentDomainName`, `orbotStatus`, and `waitingForOrbot`.
- currentDomainName = "";
+ // Initialize the Orbot status and the waiting for Orbot trackers.
orbotStatus = "unknown";
waitingForOrbot = false;
// If Privacy Browser is waiting on Orbot, load the website now that Orbot is connected.
if (orbotStatus.equals("ON") && waitingForOrbot) {
- // Reset `waitingForOrbot`.
+ // Reset the waiting for Orbot status.
waitingForOrbot = false;
- // Load `formattedUrlString
- loadUrl(formattedUrlString);
+ // Get the intent that started the app.
+ Intent launchingIntent = getIntent();
+
+ // Get the information from the intent.
+ String launchingIntentAction = launchingIntent.getAction();
+ Uri launchingIntentUriData = launchingIntent.getData();
+
+ // If the intent action is a web search, perform the search.
+ if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) {
+ // Create an encoded URL string.
+ String encodedUrlString;
+
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
+
+ // Load the completed search URL.
+ loadUrl(searchURL + encodedUrlString);
+ } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
+ // Load the URL from the intent.
+ loadUrl(launchingIntentUriData.toString());
+ } else { // The is no URL in the intent.
+ // Select the homepage based on the proxy through Orbot status.
+ if (proxyThroughOrbot) {
+ // Load the Tor homepage.
+ loadUrl(sharedPreferences.getString("tor_homepage", getString(R.string.tor_homepage_default_value)));
+ } else {
+ // Load the normal homepage.
+ loadUrl(sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
+ }
+ }
}
}
};
// Register `orbotStatusBroadcastReceiver` on `this` context.
this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
- // Instantiate the block list helper.
- blockListHelper = new BlockListHelper();
-
- // Initialize the list of resource requests.
- resourceRequests = new ArrayList<>();
+ // Instantiate the blocklist helper.
+ BlockListHelper blockListHelper = new BlockListHelper();
// Parse the block lists.
easyList = blockListHelper.parseBlockList(getAssets(), "blocklists/easylist.txt");
fanboysSocialList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
ultraPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/ultraprivacy.txt");
- // Store the list versions.
- easyListVersion = easyList.get(0).get(0)[0];
- easyPrivacyVersion = easyPrivacy.get(0).get(0)[0];
- fanboysAnnoyanceVersion = fanboysAnnoyanceList.get(0).get(0)[0];
- fanboysSocialVersion = fanboysSocialList.get(0).get(0)[0];
- ultraPrivacyVersion = ultraPrivacy.get(0).get(0)[0];
-
// Get handles for views that need to be modified.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- final NavigationView navigationView = findViewById(R.id.navigationview);
+ NavigationView navigationView = findViewById(R.id.navigationview);
TabLayout tabLayout = findViewById(R.id.tablayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
ViewPager webViewPager = findViewById(R.id.webviewpager);
- bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
- bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview);
+ ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
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);
- findOnPageEditText = findViewById(R.id.find_on_page_edittext);
- fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
+ EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
// Listen for touches on the navigation menu.
navigationView.setNavigationItemSelectedListener(this);
// Get handles for the navigation menu and the back and forward menu items. The menu is zero-based.
- final Menu navigationMenu = navigationView.getMenu();
- final MenuItem navigationCloseTabMenuItem = navigationMenu.getItem(0);
- final MenuItem navigationBackMenuItem = navigationMenu.getItem(3);
- final MenuItem navigationForwardMenuItem = navigationMenu.getItem(4);
- final MenuItem navigationHistoryMenuItem = navigationMenu.getItem(5);
- navigationRequestsMenuItem = navigationMenu.getItem(6);
+ Menu navigationMenu = navigationView.getMenu();
+ MenuItem navigationCloseTabMenuItem = navigationMenu.getItem(0);
+ MenuItem navigationBackMenuItem = navigationMenu.getItem(3);
+ MenuItem navigationForwardMenuItem = navigationMenu.getItem(4);
+ MenuItem navigationHistoryMenuItem = navigationMenu.getItem(5);
+ MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
// Initialize the web view pager adapter.
- webViewPagerAdapter = new WebViewPagerAdapter(fragmentManager);
+ webViewPagerAdapter = new WebViewPagerAdapter(getSupportFragmentManager());
// Set the pager adapter on the web view pager.
webViewPager.setAdapter(webViewPagerAdapter);
@Override
public void onPageSelected(int position) {
- // Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.webViewFragmentsList.get(position);
-
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
-
- // Remove the incorrect lint warning below that the fragment view might be null.
- assert fragmentView != null;
-
- // Store the current WebView.
- currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
- // Store the current formatted URL string.
- formattedUrlString = currentWebView.getUrl();
-
- // Clear the focus from the URL text box.
- urlEditText.clearFocus();
+ // Close the find on page bar if it is open.
+ closeFindOnPage(null);
- // Hide the soft keyboard.
- inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
-
- // Display the current URL in the URL text box.
- urlEditText.setText(formattedUrlString);
+ // Set the current WebView.
+ setCurrentWebView(position);
- // Highlight the URL text.
- highlightUrlText();
+ // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled via swiping in the view pager or by creating a new tab.
+ if (tabLayout.getSelectedTabPosition() != position) {
+ // Create a handler to select the tab.
+ Handler selectTabHandler = new Handler();
- // Set the background to indicate the domain settings status.
- if (currentWebView.getDomainSettingsApplied()) {
- // Set a green background on `urlTextBox` to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
- if (darkTheme) {
- urlEditText.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
- } else {
- urlEditText.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
- }
- } else {
- urlEditText.setBackgroundDrawable(getResources().getDrawable(R.color.transparent));
- }
+ // Create a runnable select the new tab.
+ Runnable selectTabRunnable = () -> {
+ // Get a handle for the tab.
+ TabLayout.Tab tab = tabLayout.getTabAt(position);
- // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled via swiping in the view pager.
- if (tabLayout.getSelectedTabPosition() != position) {
- // Get a handle for the corresponding tab.
- TabLayout.Tab correspondingTab = tabLayout.getTabAt(position);
+ // Assert that the tab is not null.
+ assert tab != null;
- // Assert that the corresponding tab is not null.
- assert correspondingTab != null;
+ // Select the tab.
+ tab.select();
+ };
- // Select the corresponding tab.
- correspondingTab.select();
+ // Select the tab layout after 100 milliseconds, which leaves enough time for a new tab to be created.
+ selectTabHandler.postDelayed(selectTabRunnable, 100);
}
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// Instantiate the View SSL Certificate dialog.
- DialogFragment viewSslCertificateDialogFragment = new ViewSslCertificateDialog();
+ DialogFragment viewSslCertificateDialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView.getWebViewFragmentId());
// Display the View SSL Certificate dialog.
viewSslCertificateDialogFragment.show(getSupportFragmentManager(), getString(R.string.view_ssl_certificate));
});
// Add the first tab.
- webViewPagerAdapter.addPage();
+ addTab(null);
// Set the bookmarks drawer resources according to the theme. This can't be done in the layout due to compatibility issues with the `DrawerLayout` support widget.
+ // The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21 and and `getResources().getColor()` must be used until the minimum API >= 23.
if (darkTheme) {
- launchBookmarksActivityFab.setImageDrawable(resources.getDrawable(R.drawable.bookmarks_dark));
- createBookmarkFolderFab.setImageDrawable(resources.getDrawable(R.drawable.create_folder_dark));
- createBookmarkFab.setImageDrawable(resources.getDrawable(R.drawable.create_bookmark_dark));
- bookmarksListView.setBackgroundColor(resources.getColor(R.color.gray_850));
+ launchBookmarksActivityFab.setImageDrawable(getResources().getDrawable(R.drawable.bookmarks_dark));
+ createBookmarkFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_dark));
+ createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_dark));
+ bookmarksListView.setBackgroundColor(getResources().getColor(R.color.gray_850));
} else {
- launchBookmarksActivityFab.setImageDrawable(resources.getDrawable(R.drawable.bookmarks_light));
- createBookmarkFolderFab.setImageDrawable(resources.getDrawable(R.drawable.create_folder_light));
- createBookmarkFab.setImageDrawable(resources.getDrawable(R.drawable.create_bookmark_light));
- bookmarksListView.setBackgroundColor(resources.getColor(R.color.white));
+ launchBookmarksActivityFab.setImageDrawable(getResources().getDrawable(R.drawable.bookmarks_light));
+ createBookmarkFolderFab.setImageDrawable(getResources().getDrawable(R.drawable.create_folder_light));
+ createBookmarkFab.setImageDrawable(getResources().getDrawable(R.drawable.create_bookmark_light));
+ bookmarksListView.setBackgroundColor(getResources().getColor(R.color.white));
}
// Set the launch bookmarks activity FAB to launch the bookmarks activity.
launchBookmarksActivityFab.setOnClickListener(v -> {
- // Store the current WebView url and title in the bookmarks activity.
- BookmarksActivity.currentWebViewUrl = currentWebView.getUrl();
- BookmarksActivity.currentWebViewTitle = currentWebView.getTitle();
+ // Get a copy of the favorite icon bitmap.
+ Bitmap favoriteIconBitmap = currentWebView.getFavoriteOrDefaultIcon();
+
+ // Create a favorite icon byte array output stream.
+ ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
+
+ // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG).
+ favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream);
+
+ // Convert the favorite icon byte array stream to a byte array.
+ byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray();
// Create an intent to launch the bookmarks activity.
Intent bookmarksIntent = new Intent(getApplicationContext(), BookmarksActivity.class);
- // Include the current folder with the `Intent`.
- bookmarksIntent.putExtra("Current Folder", currentBookmarksFolder);
+ // Add the extra information to the intent.
+ bookmarksIntent.putExtra("current_url", currentWebView.getUrl());
+ bookmarksIntent.putExtra("current_title", currentWebView.getTitle());
+ bookmarksIntent.putExtra("current_folder", currentBookmarksFolder);
+ bookmarksIntent.putExtra("favorite_icon_byte_array", favoriteIconByteArray);
// Make it so.
startActivity(bookmarksIntent);
// Set the create new bookmark folder FAB to display an alert dialog.
createBookmarkFolderFab.setOnClickListener(v -> {
- // 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));
+ // Create a create bookmark folder dialog.
+ DialogFragment createBookmarkFolderDialog = CreateBookmarkFolderDialog.createBookmarkFolder(currentWebView.getFavoriteOrDefaultIcon());
+
+ // Show the create bookmark folder dialog.
+ createBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.create_folder));
});
// Set the create new bookmark FAB to display an alert dialog.
createBookmarkFab.setOnClickListener(view -> {
// Instantiate the create bookmark dialog.
- DialogFragment createBookmarkDialog = CreateBookmarkDialog.createBookmark(currentWebView.getUrl(), currentWebView.getTitle(), favoriteIconBitmap);
+ DialogFragment createBookmarkDialog = CreateBookmarkDialog.createBookmark(currentWebView.getUrl(), currentWebView.getTitle(), currentWebView.getFavoriteOrDefaultIcon());
// Display the create bookmark dialog.
- createBookmarkDialog.show(fragmentManager, resources.getString(R.string.create_bookmark));
+ createBookmarkDialog.show(getSupportFragmentManager(), getString(R.string.create_bookmark));
});
// Search for the string on the page whenever a character changes in the `findOnPageEditText`.
@Override
public void afterTextChanged(Editable s) {
- // Search for the text in `mainWebView`.
- currentWebView.findAllAsync(findOnPageEditText.getText().toString());
+ // Search for the text in the WebView if it is not null. Sometimes on resume after a period of non-use the WebView will be null.
+ if (currentWebView != null) {
+ currentWebView.findAllAsync(findOnPageEditText.getText().toString());
+ }
}
});
oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
// Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`.
- DialogFragment editBookmarkFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId);
- editBookmarkFolderDialog.show(fragmentManager, resources.getString(R.string.edit_folder));
+ DialogFragment editBookmarkFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId, currentWebView.getFavoriteOrDefaultIcon());
+ editBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.edit_folder));
} else {
// Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`.
- DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId);
- editBookmarkDialog.show(fragmentManager, resources.getString(R.string.edit_bookmark));
+ DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId, currentWebView.getFavoriteOrDefaultIcon());
+ editBookmarkDialog.show(getSupportFragmentManager(), getString(R.string.edit_bookmark));
}
// Consume the event.
});
// Get the status bar pixel size.
- int statusBarResourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
- int statusBarPixelSize = resources.getDimensionPixelSize(statusBarResourceId);
+ int statusBarResourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
+ int statusBarPixelSize = getResources().getDimensionPixelSize(statusBarResourceId);
// Get the resource density.
- float screenDensity = resources.getDisplayMetrics().density;
+ float screenDensity = getResources().getDisplayMetrics().density;
// Calculate the drawer header padding. This is used to move the text in the drawer headers below any cutouts.
drawerHeaderPaddingLeftAndRight = (int) (15 * screenDensity);
navigationBackMenuItem.setEnabled(currentWebView.canGoBack());
navigationForwardMenuItem.setEnabled(currentWebView.canGoForward());
navigationHistoryMenuItem.setEnabled((currentWebView.canGoBack() || currentWebView.canGoForward()));
- navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
// Hide the keyboard (if displayed).
inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
// 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);
- // Initialize cookieManager.
- cookieManager = CookieManager.getInstance();
-
// Replace the header that `WebView` creates for `X-Requested-With` with a null value. The default value is the application ID (com.stoutner.privacybrowser.standard).
customHeaders.put("X-Requested-With", "");
// Initialize the default preference values the first time the program is run. `false` keeps this command from resetting any current preferences back to default.
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
- // Get a handle for the `Runtime`.
- privacyBrowserRuntime = Runtime.getRuntime();
-
- // Store the application's private data directory.
- privateDataDirectoryString = getApplicationInfo().dataDir;
- // `dataDir` will vary, but will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`.
-
- // Initialize `inFullScreenBrowsingMode`, which is always false at this point because Privacy Browser never starts in full screen browsing mode.
- inFullScreenBrowsingMode = false;
-
- // Initialize the privacy settings variables.
- javaScriptEnabled = false;
- firstPartyCookiesEnabled = false;
- thirdPartyCookiesEnabled = false;
- domStorageEnabled = false;
- saveFormDataEnabled = false; // Form data can be removed once the minimum API >= 26.
- nightMode = false;
-
// Inflate a bare WebView to get the default user agent. It is not used to render content on the screen.
@SuppressLint("InflateParams") View webViewLayout = getLayoutInflater().inflate(R.layout.bare_webview, null, false);
// Destroy the bare WebView.
bareWebView.destroy();
-
- // 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;
- favoriteIconDefaultBitmap = favoriteIconBitmapDrawable.getBitmap();
-
- // If the favorite icon is null, load the default.
- if (favoriteIconBitmap == null) {
- favoriteIconBitmap = favoriteIconDefaultBitmap;
- }
-
- // Initialize the user agent array adapter and string array.
- userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
- userAgentDataArray = resources.getStringArray(R.array.user_agent_data);
-
- // Get the intent that started the app.
- Intent launchingIntent = getIntent();
-
- // Get the information from the intent.
- String launchingIntentAction = launchingIntent.getAction();
- Uri launchingIntentUriData = launchingIntent.getData();
-
- // If the intent action is a web search, perform the search.
- if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) {
- // Create an encoded URL string.
- String encodedUrlString;
-
- // Sanitize the search input and convert it to a search.
- try {
- encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
- } catch (UnsupportedEncodingException exception) {
- encodedUrlString = "";
- }
-
- // Add the base search URL.
- formattedUrlString = searchURL + encodedUrlString;
- } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
- // Set the formatted URL string.
- formattedUrlString = launchingIntentUriData.toString();
- }
}
@Override
protected void onNewIntent(Intent intent) {
- // Sets the new intent as the activity intent, so that any future `getIntent()`s pick up this one instead of creating a new activity.
- setIntent(intent);
-
// Get the information from the intent.
String intentAction = intent.getAction();
Uri intentUriData = intent.getData();
- // If the intent action is a web search, perform the search.
- if ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH)) {
- // Create an encoded URL string.
- String encodedUrlString;
+ // Only process the URI if it contains data. If the user pressed the desktop icon after the app was already running the URI will be null.
+ if (intentUriData != null) {
+ // Sets the new intent as the activity intent, which replaces the one that originally started the app.
+ setIntent(intent);
- // Sanitize the search input and convert it to a search.
- try {
- encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
- } catch (UnsupportedEncodingException exception) {
- encodedUrlString = "";
+ // Add a new tab.
+ addTab(null);
+
+ // Create a URL string.
+ String url;
+
+ // If the intent action is a web search, perform the search.
+ if ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH)) {
+ // Create an encoded URL string.
+ String encodedUrlString;
+
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
+
+ // Add the base search URL.
+ url = searchURL + encodedUrlString;
+ } else { // The intent should contain a URL.
+ // Set the intent data as the URL.
+ url = intentUriData.toString();
}
- // Add the base search URL.
- formattedUrlString = searchURL + encodedUrlString;
- } else if (intentUriData != null){ // Check to see if the intent contains a new URL.
- // Set the formatted URL string.
- formattedUrlString = intentUriData.toString();
- }
+ // Load the URL.
+ loadUrl(url);
- // Load the URL.
- loadUrl(formattedUrlString);
+ // Get a handle for the drawer layout.
+ DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- // 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);
+ }
- // Close the navigation drawer if it is open.
- if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
- drawerLayout.closeDrawer(GravityCompat.START);
- }
+ // Close the bookmarks drawer if it is open.
+ if (drawerLayout.isDrawerVisible(GravityCompat.END)) {
+ drawerLayout.closeDrawer(GravityCompat.END);
+ }
- // Close the bookmarks drawer if it is open.
- if (drawerLayout.isDrawerVisible(GravityCompat.END)) {
- drawerLayout.closeDrawer(GravityCompat.END);
+ // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it.
+ currentWebView.requestFocus();
}
-
- // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it.
- currentWebView.requestFocus();
}
@Override
sendBroadcast(orbotIntent);
}
- // Apply the app settings if returning from the Settings activity..
+ // Apply the app settings if returning from the Settings activity.
if (reapplyAppSettingsOnRestart) {
+ // Reset the reapply app settings on restart tracker.
+ reapplyAppSettingsOnRestart = false;
+
// Apply the app settings.
applyAppSettings();
+ }
- // Reload the webpage if displaying of images has been disabled in the Settings activity.
- if (reloadOnRestart) {
- // Reload the WebViews.
- for (int i = 0; i < webViewPagerAdapter.webViewFragmentsList.size(); i++) {
- // Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.webViewFragmentsList.get(i);
+ // Apply the domain settings if returning from the settings or domains activity.
+ if (reapplyDomainSettingsOnRestart) {
+ // Reset the reapply domain settings on restart tracker.
+ reapplyDomainSettingsOnRestart = false;
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
+ // Reapply the domain settings for each tab.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
- // Only reload the WebViews if they exist.
- if (fragmentView != null) {
- // Get the nested scroll WebView from the tab fragment.
- NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
- // TODO this doesn't seem to work if for WebViews that aren't visible.
- // Reload the WebView.
- nestedScrollWebView.reload();
+ // Only reload the WebViews if they exist.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Reset the current domain name so the domain settings will be reapplied.
+ nestedScrollWebView.resetCurrentDomainName();
+
+ // Reapply the domain settings if the URL is not null, which can happen if an empty tab is active when returning from settings.
+ if (nestedScrollWebView.getUrl() != null) {
+ applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true);
}
}
-
- // Reset `reloadOnRestartBoolean`.
- reloadOnRestart = false;
}
-
- // Reset the return from settings flag.
- reapplyAppSettingsOnRestart = false;
- }
-
- // Apply the domain settings if returning from the Domains activity.
- if (reapplyDomainSettingsOnRestart) {
- // Reapply the domain settings.
- applyDomainSettings(formattedUrlString, false, true);
-
- // Reset `reapplyDomainSettingsOnRestart`.
- reapplyDomainSettingsOnRestart = false;
}
- // Load the URL on restart to apply changes to night mode.
+ // Load the URL on restart (used when loading a bookmark).
if (loadUrlOnRestart) {
- // Load the current `formattedUrlString`.
- loadUrl(formattedUrlString);
+ // Load the specified URL.
+ loadUrl(urlToLoadOnRestart);
- // Reset `loadUrlOnRestart.
+ // Reset the load on restart tracker.
loadUrlOnRestart = false;
}
// Run the default commands.
super.onResume();
- for (int i = 0; i < webViewPagerAdapter.webViewFragmentsList.size(); i++) {
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
// Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.webViewFragmentsList.get(i);
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
// Get the fragment view.
View fragmentView = webViewTabFragment.getView();
// Get the nested scroll WebView from the tab fragment.
NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
- // Pause the nested scroll WebView JavaScript timers.
+ // Resume the nested scroll WebView JavaScript timers.
nestedScrollWebView.resumeTimers();
- // Pause the nested scroll WebView.
+ // Resume the nested scroll WebView.
nestedScrollWebView.onResume();
}
}
currentWebView.getSettings().setUseWideViewPort(false);
// Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
- currentWebView.loadData(waitingForOrbotHtmlString, "text/html", null);
+ currentWebView.loadData("<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>", "text/html", null);
}
if (displayingFullScreenVideo || inFullScreenBrowsingMode) {
// Run the default commands.
super.onPause();
- for (int i = 0; i < webViewPagerAdapter.webViewFragmentsList.size(); i++) {
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
// Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.webViewFragmentsList.get(i);
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
// Get the fragment view.
View fragmentView = webViewTabFragment.getView();
// 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`.
- mainMenu = menu;
+ // Store a handle for the options menu so it can be used by `onOptionsItemSelected()` and `updatePrivacyIcons()`.
+ optionsMenu = menu;
// Set the initial status of the privacy icons. `false` does not call `invalidateOptionsMenu` as the last step.
updatePrivacyIcons(false);
MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); // Form data can be removed once the minimum API >= 26.
MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data); // Form data can be removed once the minimum API >= 26.
- refreshMenuItem = menu.findItem(R.id.refresh);
- blocklistsMenuItem = menu.findItem(R.id.blocklists);
- easyListMenuItem = menu.findItem(R.id.easylist);
- easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
- fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
- fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
- ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
- blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
+ MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
// Only display third-party cookies if API >= 21
toggleSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
clearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
+ // Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly.
+ clearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26);
+
// Only show Ad Consent if this is the free flavor.
adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
// Get the shared preference values.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Get the status of the additional AppBar icons.
- displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+ // Get the dark theme and app bar preferences..
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+ boolean darkTheme = sharedPreferences.getBoolean("dark_theme", 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 (displayAdditionalAppBarIcons) {
}
// Replace Refresh with Stop if a URL is already loading.
- if (urlIsLoading) {
+ if (currentWebView != null && currentWebView.getProgress() != 100) {
// Set the title.
refreshMenuItem.setTitle(R.string.stop);
@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);
- 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); // Form data can be removed once the minimum API >= 26.
+ MenuItem firstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies);
+ MenuItem thirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
+ MenuItem domStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
+ MenuItem saveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data); // Form data can be removed once the minimum API >= 26.
MenuItem clearDataMenuItem = menu.findItem(R.id.clear_data);
MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies);
MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data); // Form data can be removed once the minimum API >= 26.
+ MenuItem blocklistsMenuItem = menu.findItem(R.id.blocklists);
+ MenuItem easyListMenuItem = menu.findItem(R.id.easylist);
+ MenuItem easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
+ MenuItem fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
+ MenuItem fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
+ MenuItem ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
+ MenuItem blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
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 nightModeMenuItem = menu.findItem(R.id.night_mode);
MenuItem proxyThroughOrbotMenuItem = menu.findItem(R.id.proxy_through_orbot);
- // Set the text for the domain menu item.
- if (currentWebView.getDomainSettingsApplied()) {
- addOrEditDomain.setTitle(R.string.edit_domain_settings);
- } else {
- addOrEditDomain.setTitle(R.string.add_domain_settings);
+ // Get a handle for the cookie manager.
+ CookieManager cookieManager = CookieManager.getInstance();
+
+ // Initialize the current user agent string and the font size.
+ String currentUserAgent = getString(R.string.user_agent_privacy_browser);
+ int fontSize = 100;
+
+ // Set items that require the current web view to be populated. It will be null when the program is first opened, as `onPrepareOptionsMenu()` is called before the first WebView is initialized.
+ if (currentWebView != null) {
+ // Set the add or edit domain text.
+ if (currentWebView.getDomainSettingsApplied()) {
+ addOrEditDomain.setTitle(R.string.edit_domain_settings);
+ } else {
+ addOrEditDomain.setTitle(R.string.add_domain_settings);
+ }
+
+ // Get the current user agent from the WebView.
+ currentUserAgent = currentWebView.getSettings().getUserAgentString();
+
+ // Get the current font size from the
+ fontSize = currentWebView.getSettings().getTextZoom();
+
+ // Set the status of the menu item checkboxes.
+ domStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
+ saveFormDataMenuItem.setChecked(currentWebView.getSettings().getSaveFormData()); // Form data can be removed once the minimum API >= 26.
+ easyListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST));
+ easyPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY));
+ fanboysAnnoyanceListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+ fanboysSocialBlockingListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
+ ultraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY));
+ blockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+ swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
+ displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
+ nightModeMenuItem.setChecked(currentWebView.getNightMode());
+
+ // Initialize the display names for the blocklists with the number of blocked requests.
+ blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+ easyListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASY_LIST) + " - " + getString(R.string.easylist));
+ easyPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASY_PRIVACY) + " - " + getString(R.string.easyprivacy));
+ fanboysAnnoyanceListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " + getString(R.string.fanboys_annoyance_list));
+ fanboysSocialBlockingListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " + getString(R.string.fanboys_social_blocking_list));
+ ultraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRA_PRIVACY) + " - " + getString(R.string.ultraprivacy));
+ blockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests));
+
+ // Only modify third-party cookies if the API >= 21.
+ if (Build.VERSION.SDK_INT >= 21) {
+ // Set the status of the third-party cookies checkbox.
+ thirdPartyCookiesMenuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
+
+ // Enable third-party cookies if first-party cookies are enabled.
+ thirdPartyCookiesMenuItem.setEnabled(cookieManager.acceptCookie());
+ }
+
+ // Enable DOM Storage if JavaScript is enabled.
+ domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
}
// Set the status of the menu item checkboxes.
- toggleFirstPartyCookiesMenuItem.setChecked(firstPartyCookiesEnabled);
- toggleThirdPartyCookiesMenuItem.setChecked(thirdPartyCookiesEnabled);
- toggleDomStorageMenuItem.setChecked(domStorageEnabled);
- toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled); // Form data can be removed once the minimum API >= 26.
- easyListMenuItem.setChecked(easyListEnabled);
- easyPrivacyMenuItem.setChecked(easyPrivacyEnabled);
- fanboysAnnoyanceListMenuItem.setChecked(fanboysAnnoyanceListEnabled);
- fanboysSocialBlockingListMenuItem.setChecked(fanboysSocialBlockingListEnabled);
- ultraPrivacyMenuItem.setChecked(ultraPrivacyEnabled);
- blockAllThirdPartyRequestsMenuItem.setChecked(blockAllThirdPartyRequests);
- swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled());
- displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
- nightModeMenuItem.setChecked(nightMode);
+ firstPartyCookiesMenuItem.setChecked(cookieManager.acceptCookie());
proxyThroughOrbotMenuItem.setChecked(proxyThroughOrbot);
- // Enable third-party cookies if first-party cookies are enabled.
- toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled);
-
- // Enable DOM Storage if JavaScript is enabled.
- toggleDomStorageMenuItem.setEnabled(javaScriptEnabled);
-
// Enable Clear Cookies if there are any.
clearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
+ // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`, which links to `/data/data/com.stoutner.privacybrowser.standard`.
+ String privateDataDirectoryString = getApplicationInfo().dataDir;
+
// Get a count of the number of files in the Local Storage directory.
File localStorageDirectory = new File (privateDataDirectoryString + "/app_webview/Local Storage/");
int localStorageDirectoryNumberOfFiles = 0;
// Enable Clear Form Data is there is any. This can be removed once the minimum API >= 26.
if (Build.VERSION.SDK_INT < 26) {
- WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
- clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
- } else {
- // Disable clear form data because it is not supported on current version of Android.
- clearFormDataMenuItem.setEnabled(false);
+ // Get the WebView database.
+ WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
+
+ // Enable the clear form data menu item if there is anything to clear.
+ clearFormDataMenuItem.setEnabled(webViewDatabase.hasFormData());
}
// Enable Clear Data if any of the submenu items are enabled.
clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled());
- // Disable Fanboy's Social Blocking List if Fanboy's Annoyance List is checked.
- fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled);
-
- // Initialize the display names for the blocklists with the number of blocked requests.
- blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + blockedRequests);
- easyListMenuItem.setTitle(easyListBlockedRequests + " - " + getString(R.string.easylist));
- easyPrivacyMenuItem.setTitle(easyPrivacyBlockedRequests + " - " + getString(R.string.easyprivacy));
- fanboysAnnoyanceListMenuItem.setTitle(fanboysAnnoyanceListBlockedRequests + " - " + getString(R.string.fanboys_annoyance_list));
- fanboysSocialBlockingListMenuItem.setTitle(fanboysSocialBlockingListBlockedRequests + " - " + getString(R.string.fanboys_social_blocking_list));
- ultraPrivacyMenuItem.setTitle(ultraPrivacyBlockedRequests + " - " + getString(R.string.ultraprivacy));
- blockAllThirdPartyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests));
-
- // Get the current user agent.
- String currentUserAgent = currentWebView.getSettings().getUserAgentString();
+ // Disable Fanboy's Social Blocking List menu item if Fanboy's Annoyance List is checked.
+ fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListMenuItem.isChecked());
// Select the current user agent menu item. A switch statement cannot be used because the user agents are not compile time constants.
if (currentUserAgent.equals(getResources().getStringArray(R.array.user_agent_data)[0])) { // Privacy Browser.
menu.findItem(R.id.user_agent_custom).setChecked(true);
}
- // Initialize font size variables.
- int fontSize = currentWebView.getSettings().getTextZoom();
+ // Instantiate the font size title and the selected font size menu item.
String fontSizeTitle;
MenuItem selectedFontSizeMenuItem;
@Override
// Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.
@SuppressLint("SetJavaScriptEnabled")
- // removeAllCookies is deprecated, but it is required for API < 21.
- @SuppressWarnings("deprecation")
public boolean onOptionsItemSelected(MenuItem menuItem) {
// Reenter full screen browsing mode if it was interrupted by the options menu. <https://redmine.stoutner.com/issues/389>
if (inFullScreenBrowsingMode) {
// Get the selected menu item ID.
int menuItemId = menuItem.getItemId();
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Get a handle for the cookie manager.
+ CookieManager cookieManager = CookieManager.getInstance();
+
// Run the commands that correlate to the selected menu item.
switch (menuItemId) {
case R.id.toggle_javascript:
- // Switch the status of javaScriptEnabled.
- javaScriptEnabled = !javaScriptEnabled;
-
- // Apply the new JavaScript status.
- currentWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
+ // Toggle the JavaScript status.
+ currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled());
// Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step.
updatePrivacyIcons(true);
// Display a `Snackbar`.
- if (javaScriptEnabled) { // JavaScrip is enabled.
+ if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled.
Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
- } else if (firstPartyCookiesEnabled) { // JavaScript is disabled, but first-party cookies are enabled.
+ } else if (cookieManager.acceptCookie()) { // JavaScript is disabled, but first-party cookies are enabled.
Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
} else { // Privacy mode.
Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
if (currentWebView.getDomainSettingsApplied()) { // 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", currentWebView.getDomainSettingsDatabaseId());
- domainsIntent.putExtra("closeOnBack", true);
+ // Add the extra information to the intent.
+ domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId());
+ domainsIntent.putExtra("close_on_back", true);
+ domainsIntent.putExtra("current_url", currentWebView.getUrl());
+
+ // Get the current certificate.
+ SslCertificate sslCertificate = currentWebView.getCertificate();
+
+ // Check to see if the SSL certificate is populated.
+ if (sslCertificate != null) {
+ // Extract the certificate to strings.
+ String issuedToCName = sslCertificate.getIssuedTo().getCName();
+ String issuedToOName = sslCertificate.getIssuedTo().getOName();
+ String issuedToUName = sslCertificate.getIssuedTo().getUName();
+ String issuedByCName = sslCertificate.getIssuedBy().getCName();
+ String issuedByOName = sslCertificate.getIssuedBy().getOName();
+ String issuedByUName = sslCertificate.getIssuedBy().getUName();
+ long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+ long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+
+ // Add the certificate to the intent.
+ domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+ domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+ domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+ domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+ domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+ domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+ domainsIntent.putExtra("ssl_start_date", startDateLong);
+ domainsIntent.putExtra("ssl_end_date", endDateLong);
+ }
+
+ // Check to see if the current IP addresses have been received.
+ if (currentWebView.hasCurrentIpAddresses()) {
+ // Add the current IP addresses to the intent.
+ domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+ }
// 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);
+ Uri currentUri = Uri.parse(currentWebView.getUrl());
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`.
// 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);
+ // Add the extra information to the intent.
+ domainsIntent.putExtra("load_domain", newDomainDatabaseId);
+ domainsIntent.putExtra("close_on_back", true);
+ domainsIntent.putExtra("current_url", currentWebView.getUrl());
+
+ // Get the current certificate.
+ SslCertificate sslCertificate = currentWebView.getCertificate();
+
+ // Check to see if the SSL certificate is populated.
+ if (sslCertificate != null) {
+ // Extract the certificate to strings.
+ String issuedToCName = sslCertificate.getIssuedTo().getCName();
+ String issuedToOName = sslCertificate.getIssuedTo().getOName();
+ String issuedToUName = sslCertificate.getIssuedTo().getUName();
+ String issuedByCName = sslCertificate.getIssuedBy().getCName();
+ String issuedByOName = sslCertificate.getIssuedBy().getOName();
+ String issuedByUName = sslCertificate.getIssuedBy().getUName();
+ long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+ long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+
+ // Add the certificate to the intent.
+ domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+ domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+ domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+ domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+ domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+ domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+ domainsIntent.putExtra("ssl_start_date", startDateLong);
+ domainsIntent.putExtra("ssl_end_date", endDateLong);
+ }
+
+ // Check to see if the current IP addresses have been received.
+ if (currentWebView.hasCurrentIpAddresses()) {
+ // Add the current IP addresses to the intent.
+ domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+ }
// Make it so.
startActivity(domainsIntent);
return true;
case R.id.toggle_first_party_cookies:
- // Switch the status of firstPartyCookiesEnabled.
- firstPartyCookiesEnabled = !firstPartyCookiesEnabled;
+ // Switch the first-party cookie status.
+ cookieManager.setAcceptCookie(!cookieManager.acceptCookie());
- // Update the menu checkbox.
- menuItem.setChecked(firstPartyCookiesEnabled);
+ // Store the first-party cookie status.
+ currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie());
- // Apply the new cookie status.
- cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
+ // Update the menu checkbox.
+ menuItem.setChecked(cookieManager.acceptCookie());
// Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step.
updatePrivacyIcons(true);
- // Display a `Snackbar`.
- if (firstPartyCookiesEnabled) { // First-party cookies are enabled.
+ // Display a snackbar.
+ if (cookieManager.acceptCookie()) { // First-party cookies are enabled.
Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
- } else if (javaScriptEnabled) { // JavaScript is still enabled.
+ } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is still enabled.
Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
} else { // Privacy mode.
Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
case R.id.toggle_third_party_cookies:
if (Build.VERSION.SDK_INT >= 21) {
// Switch the status of thirdPartyCookiesEnabled.
- thirdPartyCookiesEnabled = !thirdPartyCookiesEnabled;
+ cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView));
// Update the menu checkbox.
- menuItem.setChecked(thirdPartyCookiesEnabled);
-
- // Apply the new cookie status.
- cookieManager.setAcceptThirdPartyCookies(currentWebView, thirdPartyCookiesEnabled);
+ menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
- // Display a `Snackbar`.
- if (thirdPartyCookiesEnabled) {
+ // Display a snackbar.
+ if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
} else {
Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
return true;
case R.id.toggle_dom_storage:
- // Switch the status of domStorageEnabled.
- domStorageEnabled = !domStorageEnabled;
+ // Toggle the status of domStorageEnabled.
+ currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled());
// Update the menu checkbox.
- menuItem.setChecked(domStorageEnabled);
+ menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
- // Apply the new DOM Storage status.
- currentWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
-
- // Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step.
+ // Update the privacy icon. `true` refreshes the app bar icons.
updatePrivacyIcons(true);
- // Display a `Snackbar`.
- if (domStorageEnabled) {
+ // Display a snackbar.
+ if (currentWebView.getSettings().getDomStorageEnabled()) {
Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
} else {
Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
// Form data can be removed once the minimum API >= 26.
case R.id.toggle_save_form_data:
// Switch the status of saveFormDataEnabled.
- saveFormDataEnabled = !saveFormDataEnabled;
+ currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
// Update the menu checkbox.
- menuItem.setChecked(saveFormDataEnabled);
-
- // Apply the new form data status.
- currentWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+ menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
- // Display a `Snackbar`.
- if (saveFormDataEnabled) {
+ // Display a snackbar.
+ if (currentWebView.getSettings().getSaveFormData()) {
Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
} else {
Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
@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.
- case Snackbar.Callback.DISMISS_EVENT_ACTION:
- // Do nothing.
- break;
-
- // 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 {
- cookieManager.removeAllCookies(null);
- }
+ if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
+ // Delete the cookies, which command varies by SDK.
+ if (Build.VERSION.SDK_INT < 21) {
+ cookieManager.removeAllCookie();
+ } else {
+ cookieManager.removeAllCookies(null);
+ }
}
}
})
@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.
- case Snackbar.Callback.DISMISS_EVENT_ACTION:
- // Do nothing.
- break;
-
- // The snackbar was dismissed without the undo button being pushed.
- default:
- // Delete the DOM Storage.
- WebStorage webStorage = WebStorage.getInstance();
- webStorage.deleteAllData();
-
- // Initialize a handler to manually delete the DOM storage files and directories.
- Handler deleteDomStorageHandler = new Handler();
-
- // Setup a runnable to manually delete the DOM storage files and directories.
- Runnable deleteDomStorageRunnable = () -> {
- try {
- // 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 `*`.
- 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.
- }
- };
-
- // Manually delete the DOM storage files after 200 milliseconds.
- deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
+ if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
+ // Delete the DOM Storage.
+ WebStorage webStorage = WebStorage.getInstance();
+ webStorage.deleteAllData();
+
+ // Initialize a handler to manually delete the DOM storage files and directories.
+ Handler deleteDomStorageHandler = new Handler();
+
+ // Setup a runnable to manually delete the DOM storage files and directories.
+ Runnable deleteDomStorageRunnable = () -> {
+ try {
+ // Get a handle for the runtime.
+ Runtime runtime = Runtime.getRuntime();
+
+ // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+ // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+ String privateDataDirectoryString = getApplicationInfo().dataDir;
+
+ // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+ Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+ // Multiple commands must be used because `Runtime.exec()` does not like `*`.
+ Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+ Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+ Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+ Process deleteDatabasesProcess = runtime.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.
+ }
+ };
+
+ // Manually delete the DOM storage files after 200 milliseconds.
+ deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
}
}
})
@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.
- case Snackbar.Callback.DISMISS_EVENT_ACTION:
- // Do nothing.
- break;
-
- // The snackbar was dismissed without the `Undo` button being pushed.
- default:
- // Delete the form data.
- WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
- mainWebViewDatabase.clearFormData();
+ if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) { // The snackbar was dismissed without the undo button being pushed.
+ // Delete the form data.
+ WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
+ mainWebViewDatabase.clearFormData();
}
}
})
case R.id.easylist:
// Toggle the EasyList status.
- easyListEnabled = !easyListEnabled;
+ currentWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST));
// Update the menu checkbox.
- menuItem.setChecked(easyListEnabled);
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST));
// Reload the current WebView.
currentWebView.reload();
case R.id.easyprivacy:
// Toggle the EasyPrivacy status.
- easyPrivacyEnabled = !easyPrivacyEnabled;
+ currentWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY));
// Update the menu checkbox.
- menuItem.setChecked(easyPrivacyEnabled);
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY));
// Reload the current WebView.
currentWebView.reload();
case R.id.fanboys_annoyance_list:
// Toggle Fanboy's Annoyance List status.
- fanboysAnnoyanceListEnabled = !fanboysAnnoyanceListEnabled;
+ currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
// Update the menu checkbox.
- menuItem.setChecked(fanboysAnnoyanceListEnabled);
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
// Update the staus of Fanboy's Social Blocking List.
- MenuItem fanboysSocialBlockingListMenuItem = mainMenu.findItem(R.id.fanboys_social_blocking_list);
- fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled);
+ MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list);
+ fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
// Reload the current WebView.
currentWebView.reload();
case R.id.fanboys_social_blocking_list:
// Toggle Fanboy's Social Blocking List status.
- fanboysSocialBlockingListEnabled = !fanboysSocialBlockingListEnabled;
+ currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
// Update the menu checkbox.
- menuItem.setChecked(fanboysSocialBlockingListEnabled);
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
// Reload the current WebView.
currentWebView.reload();
case R.id.ultraprivacy:
// Toggle the UltraPrivacy status.
- ultraPrivacyEnabled = !ultraPrivacyEnabled;
+ currentWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY));
// Update the menu checkbox.
- menuItem.setChecked(ultraPrivacyEnabled);
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY));
// Reload the current WebView.
currentWebView.reload();
case R.id.block_all_third_party_requests:
//Toggle the third-party requests blocker status.
- blockAllThirdPartyRequests = !blockAllThirdPartyRequests;
+ currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
// Update the menu checkbox.
- menuItem.setChecked(blockAllThirdPartyRequests);
+ menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
// Reload the current WebView.
currentWebView.reload();
case R.id.user_agent_custom:
// Update the user agent.
- currentWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
+ currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
// Reload the current WebView.
currentWebView.reload();
return true;
case R.id.swipe_to_refresh:
+ // Toggle the stored status of swipe to refresh.
+ currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh());
+
// Get a handle for the swipe refresh layout.
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- // Toggle swipe to refresh.
- swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled());
+ // Update the swipe refresh layout.
+ if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
+ if (Build.VERSION.SDK_INT >= 23) { // For API >= 23, the status of the scroll refresh listener is continuously updated by the on scroll change listener.
+ // Only enable the swipe refresh layout if the WebView is scrolled to the top.
+ swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+ } else { // For API < 23, the swipe refresh layout is always enabled.
+ // Enable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(true);
+ }
+ } else { // Swipe to refresh is disabled.
+ // Disable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(false);
+ }
return true;
case R.id.display_images:
case R.id.night_mode:
// Toggle night mode.
- nightMode = !nightMode;
+ currentWebView.setNightMode(!currentWebView.getNightMode());
// Enable or disable JavaScript according to night mode, the global preference, and any domain settings.
- if (nightMode) { // Night mode is enabled. Enable JavaScript.
- // Update the global variable.
- javaScriptEnabled = true;
+ if (currentWebView.getNightMode()) { // Night mode is enabled, which requires JavaScript.
+ // Enable JavaScript.
+ currentWebView.getSettings().setJavaScriptEnabled(true);
} else if (currentWebView.getDomainSettingsApplied()) { // Night mode is disabled and domain settings are applied. Set JavaScript according to the domain settings.
- // Get the JavaScript preference that was stored the last time domain settings were loaded.
- javaScriptEnabled = domainSettingsJavaScriptEnabled;
+ // Apply the JavaScript preference that was stored the last time domain settings were loaded.
+ currentWebView.getSettings().setJavaScriptEnabled(currentWebView.getDomainSettingsJavaScriptEnabled());
} else { // Night mode is disabled and domain settings are not applied. Set JavaScript according to the global preference.
- // Get a handle for the shared preference.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
- // Get the JavaScript preference.
- javaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
+ // Apply the JavaScript preference.
+ currentWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
}
- // Apply the JavaScript setting to the WebView.
- currentWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
-
// Update the privacy icons.
updatePrivacyIcons(false);
// Get a handle for the views.
Toolbar toolbar = findViewById(R.id.toolbar);
LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+ EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
+
+ // Set the minimum height of the find on page linear layout to match the toolbar.
+ findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight());
// Hide the toolbar.
toolbar.setVisibility(View.GONE);
// Set the focus on `findOnPageEditText`.
findOnPageEditText.requestFocus();
+ // Get a handle for the input method manager.
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ // Remove the lint warning below that the input method manager might be null.
+ assert inputMethodManager != null;
+
// Display the keyboard. `0` sets no input flags.
inputMethodManager.showSoftInput(findOnPageEditText, 0);
}, 200);
return true;
case R.id.view_source:
- // Launch the View Source activity.
+ // Create an intent to launch the view source activity.
Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+
+ // Add the variables to the intent.
+ viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString());
+ viewSourceIntent.putExtra("current_url", currentWebView.getUrl());
+
+ // Make it so.
startActivity(viewSourceIntent);
return true;
case R.id.share_url:
// Setup the share string.
- String shareString = currentWebView.getTitle() + " – " + formattedUrlString;
+ String shareString = currentWebView.getTitle() + " – " + currentWebView.getUrl();
// Create the share intent.
Intent shareIntent = new Intent(Intent.ACTION_SEND);
return true;
case R.id.print:
- // Get a `PrintManager` instance.
+ // Get a print manager instance.
PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
- // Create a print document adapter form the current WebView.
- PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
-
- // Remove the lint error below that `printManager` might be `null`.
+ // Remove the lint error below that print manager might be null.
assert printManager != null;
- // Print the document. The print attributes are `null`.
+ // Create a print document adapter from the current WebView.
+ PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
+
+ // Print the document.
printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
return true;
case R.id.open_with_app:
- openWithApp(formattedUrlString);
+ openWithApp(currentWebView.getUrl());
return true;
case R.id.open_with_browser:
- openWithBrowser(formattedUrlString);
+ openWithBrowser(currentWebView.getUrl());
return true;
case R.id.add_to_homescreen:
// Instantiate the create home screen shortcut dialog.
- DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), formattedUrlString, favoriteIconBitmap);
+ DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
+ currentWebView.getFavoriteOrDefaultIcon());
// Show the create home screen shortcut dialog.
createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
}
// removeAllCookies is deprecated, but it is required for API < 21.
- @SuppressWarnings("deprecation")
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
// Get the menu item ID.
int menuItemId = menuItem.getItemId();
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
// Run the commands that correspond to the selected menu item.
switch (menuItemId) {
case R.id.close_tab:
- // Get a handle for the tab layout.
+ // Get a handle for the tab layout and the view pager.
TabLayout tabLayout = findViewById(R.id.tablayout);
+ ViewPager webViewPager = findViewById(R.id.webviewpager);
// Get the current tab number.
int currentTabNumber = tabLayout.getSelectedTabPosition();
- // Delete the tab and page.
- webViewPagerAdapter.deletePage(currentTabNumber);
+ // Delete the current tab.
+ tabLayout.removeTabAt(currentTabNumber);
+
+ // Delete the current page. If the selected page number did not change during the delete, it will return true, meaning that the current WebView must be reset.
+ if (webViewPagerAdapter.deletePage(currentTabNumber, webViewPager)) {
+ setCurrentWebView(currentTabNumber);
+ }
break;
case R.id.clear_and_exit:
bookmarksCursor.close();
bookmarksDatabaseHelper.close();
- // 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);
+ // Get a handle for the runtime.
+ Runtime runtime = Runtime.getRuntime();
+
+ // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+ // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+ String privateDataDirectoryString = getApplicationInfo().dataDir;
+
// Clear cookies.
if (clearEverything || sharedPreferences.getBoolean("clear_cookies", true)) {
// The command to remove cookies changed slightly in API 21.
if (Build.VERSION.SDK_INT >= 21) {
- cookieManager.removeAllCookies(null);
+ CookieManager.getInstance().removeAllCookies(null);
} else {
- cookieManager.removeAllCookie();
+ CookieManager.getInstance().removeAllCookie();
}
// Manually delete the cookies database, as `CookieManager` sometimes will not flush its changes to disk before `System.exit(0)` is run.
try {
// 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");
+ Process deleteCookiesProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies");
+ Process deleteCookiesJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/Cookies-journal");
// Wait until the processes have finished.
deleteCookiesProcess.waitFor();
// 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.
- Process deleteLocalStorageProcess = privacyBrowserRuntime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+ Process deleteLocalStorageProcess = runtime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
// Multiple commands must be used because `Runtime.exec()` does not like `*`.
- 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");
+ Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+ Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+ Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+ Process deleteDatabaseProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
// Wait until the processes have finished.
deleteLocalStorageProcess.waitFor();
// Manually delete the form data database, as `WebViewDatabase` sometimes will not flush its changes to disk before `System.exit(0)` is run.
try {
// 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"});
+ Process deleteWebDataProcess = runtime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data"});
+ Process deleteWebDataJournalProcess = runtime.exec(new String[] {"rm", "-f", privateDataDirectoryString + "/app_webview/Web Data-journal"});
// Wait until the processes have finished.
deleteWebDataProcess.waitFor();
// Clear the cache.
if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
- // Clear the cache.
- // TODO
- currentWebView.clearCache(true);
+ // Clear the cache from each WebView.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
+
+ // Only clear the cache if the WebView exists.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Clear the cache for this WebView.
+ nestedScrollWebView.clearCache(true);
+ }
+ }
// Manually delete the cache directories.
try {
// Delete the main cache directory.
- Process deleteCacheProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
+ Process deleteCacheProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/cache");
// Delete the secondary `Service Worker` cache directory.
// 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/"});
+ Process deleteServiceWorkerProcess = runtime.exec(new String[] {"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
// Wait until the processes have finished.
deleteCacheProcess.waitFor();
}
}
- // Clear SSL certificate preferences.
- // TODO
- currentWebView.clearSslPreferences();
+ // Wipe out each WebView.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
- // Clear the back/forward history.
- // TODO
- currentWebView.clearHistory();
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
- // Clear `formattedUrlString`.
- formattedUrlString = null;
+ // Only wipe out the WebView if it exists.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
- // Clear `customHeaders`.
- customHeaders.clear();
+ // Clear SSL certificate preferences for this WebView.
+ nestedScrollWebView.clearSslPreferences();
- // Destroy the internal state of `mainWebView`.
- // TODO
- currentWebView.destroy();
+ // Clear the back/forward history for this WebView.
+ nestedScrollWebView.clearHistory();
- // Manually delete the `app_webview` folder, which contains the cookies, DOM storage, form data, and `Service Worker` cache.
+ // Destroy the internal state of `mainWebView`.
+ nestedScrollWebView.destroy();
+ }
+ }
+
+ // Clear the custom headers.
+ customHeaders.clear();
+
+ // Manually delete the `app_webview` folder, which contains the cookies, DOM storage, form data, and `Service Worker` cache.
// See `https://code.google.com/p/android/issues/detail?id=233826&thanks=233826&ts=1486670530`.
if (clearEverything) {
try {
// Delete the folder.
- Process deleteAppWebviewProcess = privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
+ Process deleteAppWebviewProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview");
// Wait until the process has finished.
deleteAppWebviewProcess.waitFor();
break;
case R.id.home:
- loadUrl(homepage);
+ // Select the homepage based on the proxy through Orbot status.
+ if (proxyThroughOrbot) {
+ // Load the Tor homepage.
+ loadUrl(sharedPreferences.getString("tor_homepage", getString(R.string.tor_homepage_default_value)));
+ } else {
+ // Load the normal homepage.
+ loadUrl(sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
+ }
break;
case R.id.back:
if (currentWebView.canGoBack()) {
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
+ // Reset the current domain name so that navigation works if third-party requests are blocked.
+ currentWebView.resetCurrentDomainName();
- // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
- navigatingHistory = true;
+ // Set navigating history so that the domain settings are applied when the new URL is loaded.
+ currentWebView.setNavigatingHistory(true);
// Load the previous website in the history.
currentWebView.goBack();
case R.id.forward:
if (currentWebView.canGoForward()) {
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
+ // Reset the current domain name so that navigation works if third-party requests are blocked.
+ currentWebView.resetCurrentDomainName();
- // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
- navigatingHistory = true;
+ // Set navigating history so that the domain settings are applied when the new URL is loaded.
+ currentWebView.setNavigatingHistory(true);
// Load the next website in the history.
currentWebView.goForward();
break;
case R.id.history:
- // Get the `WebBackForwardList`.
- WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+ // Instantiate the URL history dialog.
+ DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId());
- // Show the URL history dialog and name this instance `R.string.history`.
- DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(this, webBackForwardList);
+ // Show the URL history dialog.
urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
break;
case R.id.requests:
- // Launch the requests activity.
+ // Populate the resource requests.
+ RequestsActivity.resourceRequests = currentWebView.getResourceRequests();
+
+ // Create an intent to launch the Requests activity.
Intent requestsIntent = new Intent(this, RequestsActivity.class);
+
+ // Add the block third-party requests status to the intent.
+ requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+
+ // Make it so.
startActivity(requestsIntent);
break;
case R.id.domains:
// Set the flag to reapply the domain settings on restart when returning from Domain Settings.
reapplyDomainSettingsOnRestart = true;
- currentDomainName = "";
// Launch the domains activity.
Intent domainsIntent = new Intent(this, DomainsActivity.class);
+
+ // Add the extra information to the intent.
+ domainsIntent.putExtra("current_url", currentWebView.getUrl());
+
+ // Get the current certificate.
+ SslCertificate sslCertificate = currentWebView.getCertificate();
+
+ // Check to see if the SSL certificate is populated.
+ if (sslCertificate != null) {
+ // Extract the certificate to strings.
+ String issuedToCName = sslCertificate.getIssuedTo().getCName();
+ String issuedToOName = sslCertificate.getIssuedTo().getOName();
+ String issuedToUName = sslCertificate.getIssuedTo().getUName();
+ String issuedByCName = sslCertificate.getIssuedBy().getCName();
+ String issuedByOName = sslCertificate.getIssuedBy().getOName();
+ String issuedByUName = sslCertificate.getIssuedBy().getUName();
+ long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+ long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+
+ // Add the certificate to the intent.
+ domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+ domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+ domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+ domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+ domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+ domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+ domainsIntent.putExtra("ssl_start_date", startDateLong);
+ domainsIntent.putExtra("ssl_end_date", endDateLong);
+ }
+
+ // Check to see if the current IP addresses have been received.
+ if (currentWebView.hasCurrentIpAddresses()) {
+ // Add the current IP addresses to the intent.
+ domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+ }
+
+ // Make it so.
startActivity(domainsIntent);
break;
// Set the flag to reapply the domain settings on restart when returning from Settings.
reapplyDomainSettingsOnRestart = true;
- currentDomainName = "";
// Launch the settings activity.
Intent settingsIntent = new Intent(this, SettingsActivity.class);
break;
case R.id.about:
- // Launch `AboutActivity`.
+ // Create an intent to launch the about activity.
Intent aboutIntent = new Intent(this, AboutActivity.class);
+
+ // Create a string array for the blocklist versions.
+ String[] blocklistVersions = new String[] {easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0],
+ ultraPrivacy.get(0).get(0)[0]};
+
+ // Add the blocklist versions to the intent.
+ aboutIntent.putExtra("blocklist_versions", blocklistVersions);
+
+ // Make it so.
startActivity(aboutIntent);
break;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
- // Store the `HitTestResult`.
+ // Store the hit test result.
final WebView.HitTestResult hitTestResult = currentWebView.getHitTestResult();
- // Create strings.
+ // Create the URL strings.
final String imageUrl;
final String linkUrl;
- // Get a handle for the the clipboard and fragment managers.
+ // Get handles for the system managers.
final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
FragmentManager fragmentManager = getSupportFragmentManager();
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Remove the lint errors below that `clipboardManager` might be `null`.
+ // Remove the lint errors below that the clipboard manager might be null.
assert clipboardManager != null;
+ // Process the link according to the type.
switch (hitTestResult.getType()) {
// `SRC_ANCHOR_TYPE` is a link.
case WebView.HitTestResult.SRC_ANCHOR_TYPE:
menu.setHeaderTitle(linkUrl);
// Add a Load URL entry.
- menu.add(R.string.load_url).setOnMenuItemClickListener((MenuItem item) -> {
+ menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
+ // Add a new tab.
+ addTab(null);
+
+ // Load the URL.
loadUrl(linkUrl);
return false;
});
+ // 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 Copy URL entry.
menu.add(R.string.copy_url).setOnMenuItemClickListener((MenuItem item) -> {
// Save the link URL in a `ClipData`.
// Add a Download URL entry.
menu.add(R.string.download_url).setOnMenuItemClickListener((MenuItem item) -> {
// Check if the download should be processed by an external app.
- if (downloadWithExternalApp) { // Download with an external app.
+ if (sharedPreferences.getBoolean("download_with_external_app", false)) { // Download with an external app.
openUrlWithExternalApp(linkUrl);
} else { // Download with Android's download manager.
// Check to see if the storage permission has already been granted.
return false;
});
- // 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;
// 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.
+ if (sharedPreferences.getBoolean("download_with_external_app", false)) { // Download with an external app.
openUrlWithExternalApp(imageUrl);
} else { // Download with Android's download manager.
// Check to see if the storage permission has already been granted.
// 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.
+ if (sharedPreferences.getBoolean("download_with_external_app", false)) { // Download with an external app.
openUrlWithExternalApp(imageUrl);
} else { // Download with Android's download manager.
// Check to see if the storage permission has already been granted.
}
@Override
- public void onCreateBookmark(DialogFragment dialogFragment) {
+ public void onCreateBookmark(DialogFragment dialogFragment, Bitmap favoriteIconBitmap) {
+ // Get a handle for the bookmarks list view.
+ ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+
// Get the views from the dialog fragment.
EditText createBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext);
EditText createBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext);
String bookmarkNameString = createBookmarkNameEditText.getText().toString();
String bookmarkUrlString = createBookmarkUrlEditText.getText().toString();
- // Get a copy of the favorite icon bitmap.
- Bitmap favoriteIcon = favoriteIconBitmap;
-
- // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
- if ((favoriteIcon.getHeight() > 256) || (favoriteIcon.getWidth() > 256)) {
- favoriteIcon = Bitmap.createScaledBitmap(favoriteIcon, 256, 256, true);
- }
-
// Create a favorite icon byte array output stream.
ByteArrayOutputStream favoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
// Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG).
- favoriteIcon.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream);
+ favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, favoriteIconByteArrayOutputStream);
// Convert the favorite icon byte array stream to a byte array.
byte[] favoriteIconByteArray = favoriteIconByteArrayOutputStream.toByteArray();
}
@Override
- public void onCreateBookmarkFolder(DialogFragment dialogFragment) {
+ public void onCreateBookmarkFolder(DialogFragment dialogFragment, Bitmap favoriteIconBitmap) {
+ // Get a handle for the bookmarks list view.
+ ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+
// Get handles for the views in the dialog fragment.
EditText createFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext);
RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton);
// Convert the folder icon bitmap drawable to a bitmap.
folderIconBitmap = folderIconBitmapDrawable.getBitmap();
} else { // Use the WebView favorite icon.
- // Get a copy of the favorite icon bitmap.
+ // Copy the favorite icon bitmap to the folder icon bitmap.
folderIconBitmap = favoriteIconBitmap;
-
- // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
- if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) {
- folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true);
- }
}
// Create a folder icon byte array output stream.
}
@Override
- public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId) {
+ public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, Bitmap favoriteIconBitmap) {
// 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);
if (currentBookmarkIconRadioButton.isChecked()) { // Update the bookmark without changing the favorite icon.
bookmarksDatabaseHelper.updateBookmark(selectedBookmarkDatabaseId, bookmarkNameString, bookmarkUrlString);
} else { // Update the bookmark using the `WebView` favorite icon.
- // Get a copy of the favorite icon bitmap.
- Bitmap favoriteIcon = favoriteIconBitmap;
-
- // Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
- if ((favoriteIcon.getHeight() > 256) || (favoriteIcon.getWidth() > 256)) {
- favoriteIcon = Bitmap.createScaledBitmap(favoriteIcon, 256, 256, true);
- }
-
// Create a favorite icon byte array output stream.
ByteArrayOutputStream newFavoriteIconByteArrayOutputStream = new ByteArrayOutputStream();
// Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG).
- favoriteIcon.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream);
+ favoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, newFavoriteIconByteArrayOutputStream);
// Convert the favorite icon byte array stream to a byte array.
byte[] newFavoriteIconByteArray = newFavoriteIconByteArrayOutputStream.toByteArray();
}
@Override
- public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId) {
+ public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) {
// 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);
// Convert the folder icon bitmap drawable to a bitmap.
folderIconBitmap = folderIconBitmapDrawable.getBitmap();
} else { // Use the `WebView` favorite icon.
- // Get a copy of the favorite icon bitmap.
- folderIconBitmap = MainWebViewActivity.favoriteIconBitmap;
-
- // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
- if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) {
- folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true);
- }
+ // Copy the favorite icon bitmap to the folder icon bitmap.
+ folderIconBitmap = favoriteIconBitmap;
}
// Create a folder icon byte array output stream.
// Convert the folder icon bitmap drawable to a bitmap.
folderIconBitmap = folderIconBitmapDrawable.getBitmap();
} else { // Use the `WebView` favorite icon.
- // Get a copy of the favorite icon bitmap.
- folderIconBitmap = MainWebViewActivity.favoriteIconBitmap;
-
- // Scale the folder icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
- if ((folderIconBitmap.getHeight() > 256) || (folderIconBitmap.getWidth() > 256)) {
- folderIconBitmap = Bitmap.createScaledBitmap(folderIconBitmap, 256, 256, true);
- }
+ // Copy the favorite icon bitmap to the folder icon bitmap.
+ folderIconBitmap = favoriteIconBitmap;
}
// Create a folder icon byte array output stream.
// Parse `imageUrl`.
DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(imageUrl));
+ // Get a handle for the cookie manager.
+ CookieManager cookieManager = CookieManager.getInstance();
+
// Pass cookies to download manager if cookies are enabled. This is required to download images from websites that require a login.
// Code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- if (firstPartyCookiesEnabled) {
+ if (cookieManager.acceptCookie()) {
// Get the cookies for `imageUrl`.
String cookies = cookieManager.getCookie(imageUrl);
// Parse `downloadUrl`.
DownloadManager.Request downloadRequest = new DownloadManager.Request(Uri.parse(downloadUrl));
+ // Get a handle for the cookie manager.
+ CookieManager cookieManager = CookieManager.getInstance();
+
// Pass cookies to download manager if cookies are enabled. This is required to download files from websites that require a login.
// Code contributed 2017 Hendrik Knackstedt. Copyright assigned to Soren Stoutner <soren@stoutner.com>.
- if (firstPartyCookiesEnabled) {
+ if (cookieManager.acceptCookie()) {
// Get the cookies for `downloadUrl`.
String cookies = cookieManager.getCookie(downloadUrl);
}
}
- @Override
- public void onHttpAuthenticationCancel() {
- // Cancel the `HttpAuthHandler`.
- httpAuthHandler.cancel();
- }
-
- @Override
- 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);
-
- // Proceed with the HTTP authentication.
- httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString());
- }
-
- @Override
- public void onSslErrorCancel() {
- sslErrorHandler.cancel();
- }
-
- @Override
- public void onSslErrorProceed() {
- sslErrorHandler.proceed();
- }
-
- @Override
- public void onPinnedMismatchBack() {
- if (currentWebView.canGoBack()) { // There is a back page in the history.
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
-
- // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
- navigatingHistory = true;
-
- // Go back.
- currentWebView.goBack();
- } else { // There are no pages to go back to.
- // Load a blank page
- loadUrl("");
- }
- }
-
- @Override
- public void onPinnedMismatchProceed() {
- // Do not check the pinned information for this domain again until the domain changes.
- ignorePinnedDomainInformation = true;
- }
-
- @Override
- public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) {
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
-
- // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
- navigatingHistory = true;
-
- // Load the history entry.
- currentWebView.goBackOrForward(moveBackOrForwardSteps);
- }
-
- @Override
- public void onClearHistory() {
- // Clear the history.
- currentWebView.clearHistory();
- }
-
- // Override `onBackPressed` to handle the navigation drawer and `mainWebView`.
+ // Override `onBackPressed` to handle the navigation drawer and and the WebView.
@Override
public void onBackPressed() {
// Get a handle for the drawer layout.
// Load the new folder.
loadBookmarksFolder();
}
-
} else if (currentWebView.canGoBack()) { // There is at least one item in the current WebView history.
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
+ // Reset the current domain name so that navigation works if third-party requests are blocked.
+ currentWebView.resetCurrentDomainName();
- // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
- navigatingHistory = true;
+ // Set navigating history so that the domain settings are applied when the new URL is loaded.
+ currentWebView.setNavigatingHistory(true);
// Go back.
currentWebView.goBack();
- } else { // There isn't anything to do in Privacy Browser.
- // Pass `onBackPressed()` to the system.
- super.onBackPressed();
+ } else { // There is nothing else to do.
+ // Load a blank website.
+ loadUrl("");
}
}
// 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 = urlEditText.getText().toString().trim();
+ // Initialize the formatted URL string.
+ String url = "";
+
// Check to see if `unformattedUrlString` is a valid URL. Otherwise, convert it into a search.
- if (unformattedUrlString.startsWith("content://")) {
+ if (unformattedUrlString.startsWith("content://")) { // This is a Content URL.
// Load the entire content URL.
- formattedUrlString = unformattedUrlString;
- } else if (Patterns.WEB_URL.matcher(unformattedUrlString).matches() || unformattedUrlString.startsWith("http://") || unformattedUrlString.startsWith("https://")
- || unformattedUrlString.startsWith("file://")) {
+ url = unformattedUrlString;
+ } else if (Patterns.WEB_URL.matcher(unformattedUrlString).matches() || unformattedUrlString.startsWith("http://") || unformattedUrlString.startsWith("https://") ||
+ unformattedUrlString.startsWith("file://")) { // This is a standard URL.
// Add `https://` at the beginning if there is no protocol. Otherwise the app will segfault.
if (!unformattedUrlString.startsWith("http") && !unformattedUrlString.startsWith("file://") && !unformattedUrlString.startsWith("content://")) {
unformattedUrlString = "https://" + unformattedUrlString;
String fragment = unformattedUrl != null ? unformattedUrl.getRef() : null;
// Build the URI.
- Uri.Builder formattedUri = new Uri.Builder();
- formattedUri.scheme(scheme).authority(authority).path(path).query(query).fragment(fragment);
+ Uri.Builder uri = new Uri.Builder();
+ uri.scheme(scheme).authority(authority).path(path).query(query).fragment(fragment);
- // Decode `formattedUri` as a `String` in `UTF-8`.
+ // Decode the URI as a UTF-8 string in.
try {
- formattedUrlString = URLDecoder.decode(formattedUri.build().toString(), "UTF-8");
+ url = URLDecoder.decode(uri.build().toString(), "UTF-8");
} catch (UnsupportedEncodingException exception) {
- // Load a blank string.
- formattedUrlString = "";
+ // Do nothing. The formatted URL string will remain blank.
}
- } else if (unformattedUrlString.isEmpty()){ // Load a blank web site.
- // Load a blank string.
- formattedUrlString = "";
- } else { // Search for the contents of the URL box.
+ } else if (!unformattedUrlString.isEmpty()){ // This is not a URL, but rather a search string.
// Create an encoded URL String.
String encodedUrlString;
}
// Add the base search URL.
- formattedUrlString = searchURL + encodedUrlString;
+ url = searchURL + encodedUrlString;
}
// Clear the focus from the URL edit text. Otherwise, proximate typing in the box will retain the colorized formatting instead of being reset during refocus.
urlEditText.clearFocus();
// Make it so.
- loadUrl(formattedUrlString);
+ loadUrl(url);
}
- private void loadUrl(String url) {// Apply any custom domain settings.
- // Set the URL as the formatted URL string so that checking third-party requests works correctly.
- formattedUrlString = url;
-
+ private void loadUrl(String url) {
// Apply the domain settings.
- applyDomainSettings(url, true, false);
-
- // If loading a website, set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website.
- urlIsLoading = !url.equals("");
+ applyDomainSettings(currentWebView, url, true, false);
// Load the URL.
currentWebView.loadUrl(url, customHeaders);
// Get a handle for the views.
Toolbar toolbar = findViewById(R.id.toolbar);
LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+ EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
// Delete the contents of `find_on_page_edittext`.
findOnPageEditText.setText(null);
// Show the toolbar.
toolbar.setVisibility(View.VISIBLE);
+ // Get a handle for the input method manager.
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ // Remove the lint warning below that the input method manager might be null.
+ assert inputMethodManager != null;
+
// Hide the keyboard.
inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
}
proxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
- downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false);
- // Get handles for the views that need to be modified. `getSupportActionBar()` must be used until the minimum API >= 21.
+ // Get handles for the views that need to be modified.
FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
ActionBar actionBar = getSupportActionBar();
+ LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
- // Remove the incorrect lint warnings below that the action bar might be null.
+ // Remove the incorrect lint warning below that the action bar might be null.
assert actionBar != null;
// Apply the proxy through Orbot settings.
customHeaders.remove("DNT");
}
- // TODO this also needs to be set when creating a new tab.
// Set the app bar scrolling for each WebView.
- for (int i = 0; i < webViewPagerAdapter.webViewFragmentsList.size(); i++) {
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
// Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.webViewFragmentsList.get(i);
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
// Get the fragment view.
View fragmentView = webViewTabFragment.getView();
if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
// Update the visibility of the app bar, which might have changed in the settings.
if (hideAppBar) {
+ // Hide the tab linear layout.
+ tabsLinearLayout.setVisibility(View.GONE);
+
+ // Hide the action bar.
actionBar.hide();
} else {
+ // Show the tab linear layout.
+ tabsLinearLayout.setVisibility(View.VISIBLE);
+
+ // Show the action bar.
actionBar.show();
}
// 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 app bar.
+ // Show the tab linear layout.
+ tabsLinearLayout.setVisibility(View.VISIBLE);
+
+ // Show the action bar.
actionBar.show();
// Show the banner ad in the free flavor.
if (BuildConfig.FLAVOR.contentEquals("free")) {
// Initialize the ads. If this isn't the first run, `loadAd()` will be automatically called instead.
- AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), fragmentManager, getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+ AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
}
// Remove the `SYSTEM_UI` flags from the root frame layout.
// `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 boolean applyDomainSettings(String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
- // Get a handle for the URL edit text.
- EditText urlEditText = findViewById(R.id.url_edittext);
-
- // Get the current user agent.
- String initialUserAgent = currentWebView.getSettings().getUserAgentString();
-
- // Initialize a variable to track if the user agent changes.
- boolean userAgentChanged = false;
+ @SuppressLint("SetJavaScriptEnabled")
+ private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) {
+ // Store a copy of the current user agent to track changes for the return boolean.
+ String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString();
// Parse the URL into a URI.
Uri uri = Uri.parse(url);
// Extract the domain from `uri`.
- String hostName = uri.getHost();
-
- // Initialize `loadingNewDomainName`.
- boolean loadingNewDomainName;
-
- // If either `hostName` or `currentDomainName` are `null`, run the options for loading a new domain name.
- // The lint suggestion to simplify the `if` statement is incorrect, because `hostName.equals(currentDomainName)` can produce a `null object reference.`
- //noinspection SimplifiableIfStatement
- if ((hostName == null) || (currentDomainName == null)) {
- loadingNewDomainName = true;
- } else { // Determine if `hostName` equals `currentDomainName`.
- loadingNewDomainName = !hostName.equals(currentDomainName);
- }
+ String newHostName = uri.getHost();
// Strings don't like to be null.
- if (hostName == null) {
- hostName = "";
+ if (newHostName == null) {
+ newHostName = "";
}
// Only apply the domain settings if a new domain is being loaded. This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc.
- if (loadingNewDomainName) {
- // Set the new `hostname` as the `currentDomainName`.
- currentDomainName = hostName;
+ if (!nestedScrollWebView.getCurrentDomainName().equals(newHostName)) {
+ // Set the new host name as the current domain name.
+ nestedScrollWebView.setCurrentDomainName(newHostName);
// Reset the ignoring of pinned domain information.
- ignorePinnedDomainInformation = false;
+ nestedScrollWebView.setIgnorePinnedDomainInformation(false);
+
+ // Clear any pinned SSL certificate or IP addresses.
+ nestedScrollWebView.clearPinnedSslCertificate();
+ nestedScrollWebView.clearPinnedIpAddresses();
// Reset the favorite icon if specified.
- if (resetFavoriteIcon) {
- // Store the favorite icon bitmap.
- favoriteIconBitmap = favoriteIconDefaultBitmap;
+ if (resetTab) {
+ // Initialize the favorite icon.
+ nestedScrollWebView.initializeFavoriteIcon();
+
+ // Get the current page position.
+ int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
// Get a handle for the tab layout.
TabLayout tabLayout = findViewById(R.id.tablayout);
- // Get the current tab.
- TabLayout.Tab currentTab = tabLayout.getTabAt(tabLayout.getSelectedTabPosition());
+ // Get the corresponding tab.
+ TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
- // Remove the warning below that the current tab might be null.
- assert currentTab != null;
+ // Remove the warning below that the tab might be null.
+ assert tab != null;
- // Get the current tab custom view.
- View currentTabCustomView = currentTab.getCustomView();
+ // Get the tab custom view.
+ View tabCustomView = tab.getCustomView();
- // Remove the warning below that the current tab custom view might be null.
- assert currentTabCustomView != null;
+ // Remove the warning below that the tab custom view might be null.
+ assert tabCustomView != null;
- // Get the current tab favorite icon image view.
- ImageView currentTabFavoriteIconImageView = currentTabCustomView.findViewById(R.id.favorite_icon_imageview);
+ // Get the tab views.
+ ImageView tabFavoriteIconImageView = tabCustomView.findViewById(R.id.favorite_icon_imageview);
+ TextView tabTitleTextView = tabCustomView.findViewById(R.id.title_textview);
// Set the default favorite icon as the favorite icon for this tab.
- currentTabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(favoriteIconBitmap, 64, 64, true));
- }
+ tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteOrDefaultIcon(), 64, 64, true));
- // Get a handle for the swipe refresh layout.
- SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+ // Set the loading title text.
+ tabTitleTextView.setText(R.string.loading);
+ }
// 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);
String domainNameInDatabase = null;
// Check the hostname against the domain settings set.
- if (domainSettingsSet.contains(hostName)) { // The hostname is contained in the domain settings set.
+ if (domainSettingsSet.contains(newHostName)) { // The hostname is contained in the domain settings set.
// Record the domain name in the database.
- domainNameInDatabase = hostName;
+ domainNameInDatabase = newHostName;
// Set the domain settings applied tracker to true.
- currentWebView.setDomainSettingsApplied(true);
+ nestedScrollWebView.setDomainSettingsApplied(true);
} else { // The hostname is not contained in the domain settings set.
// Set the domain settings applied tracker to false.
- currentWebView.setDomainSettingsApplied(false);
+ nestedScrollWebView.setDomainSettingsApplied(false);
}
// Check all the subdomains of the host name against wildcard domains in the domain cursor.
- while (!currentWebView.getDomainSettingsApplied() && hostName.contains(".")) { // Stop checking if domain settings are already applied or there are no more `.` in the host name.
- if (domainSettingsSet.contains("*." + hostName)) { // Check the host name prepended by `*.`.
+ while (!nestedScrollWebView.getDomainSettingsApplied() && newHostName.contains(".")) { // Stop checking if domain settings are already applied or there are no more `.` in the host name.
+ if (domainSettingsSet.contains("*." + newHostName)) { // Check the host name prepended by `*.`.
// Set the domain settings applied tracker to true.
- currentWebView.setDomainSettingsApplied(true);
+ nestedScrollWebView.setDomainSettingsApplied(true);
// Store the applied domain names as it appears in the database.
- domainNameInDatabase = "*." + hostName;
+ domainNameInDatabase = "*." + newHostName;
}
// Strip out the lowest subdomain of of the host name.
- hostName = hostName.substring(hostName.indexOf(".") + 1);
+ newHostName = newHostName.substring(newHostName.indexOf(".") + 1);
}
- // Get a handle for the shared preference.
+ // Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Store the general preference information.
String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
- defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value));
boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
- nightMode = sharedPreferences.getBoolean("night_mode", false);
boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
+ boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+
+ // Get a handle for the cookie manager.
+ CookieManager cookieManager = CookieManager.getInstance();
+
+ // Get handles for the views.
+ RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- if (currentWebView.getDomainSettingsApplied()) { // The url has custom domain settings.
+ // Initialize the user agent array adapter and string array.
+ ArrayAdapter<CharSequence> userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item);
+ String[] userAgentDataArray = getResources().getStringArray(R.array.user_agent_data);
+
+ if (nestedScrollWebView.getDomainSettingsApplied()) { // The url has custom domain settings.
// Get a cursor for the current host and move it to the first position.
- Cursor currentHostDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase);
- currentHostDomainSettingsCursor.moveToFirst();
+ Cursor currentDomainSettingsCursor = domainsDatabaseHelper.getCursorForDomainName(domainNameInDatabase);
+ currentDomainSettingsCursor.moveToFirst();
// Get the settings from the cursor.
- currentWebView.setDomainSettingsDatabaseId(currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
- javaScriptEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
- firstPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
- thirdPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
- domStorageEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
+ nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
+ nestedScrollWebView.setDomainSettingsJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
+ nestedScrollWebView.setAcceptFirstPartyCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
+ boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
+ nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
// Form data can be removed once the minimum API >= 26.
- saveFormDataEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);
- easyListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);
- 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);
- ultraPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1);
- blockAllThirdPartyRequests = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
- String userAgentName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
- int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
- int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
- int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
- int displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
- pinnedSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
- pinnedSslIssuedToCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
- pinnedSslIssuedToOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
- pinnedSslIssuedToUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
- pinnedSslIssuedByCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
- pinnedSslIssuedByOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
- pinnedSslIssuedByUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
- pinnedIpAddresses = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
- pinnedHostIpAddresses = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
-
- // Set `nightMode` according to `nightModeInt`. If `nightModeInt` is `DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT` the current setting from `sharedPreferences` will be used.
+ boolean saveFormData = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST,
+ currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY,
+ currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST,
+ currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST,
+ currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY,
+ currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1);
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS,
+ currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
+ String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
+ int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
+ int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
+ int nightModeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
+ int displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
+ boolean pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
+ String pinnedSslIssuedToCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
+ String pinnedSslIssuedToOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
+ String pinnedSslIssuedToUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT));
+ String pinnedSslIssuedByCName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME));
+ String pinnedSslIssuedByOName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION));
+ String pinnedSslIssuedByUName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT));
+ boolean pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
+ String pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
+
+ // Create the pinned SSL date variables.
+ Date pinnedSslStartDate;
+ Date pinnedSslEndDate;
+
+ // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
+ if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
+ pinnedSslStartDate = null;
+ } else {
+ pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
+ }
+
+ // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
+ if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) {
+ pinnedSslEndDate = null;
+ } else {
+ pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+ }
+
+ // If there is a pinned SSL certificate, store it in the WebView.
+ if (pinnedSslCertificate) {
+ nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName,
+ pinnedSslStartDate, pinnedSslEndDate);
+ }
+
+ // If there is a pinned IP address, store it in the WebView.
+ if (pinnedIpAddresses) {
+ nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses);
+ }
+
+ // Set night mode according to the night mode int.
switch (nightModeInt) {
+ case DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT:
+ // Set night mode according to the current default.
+ nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
+ break;
+
case DomainsDatabaseHelper.NIGHT_MODE_ENABLED:
- nightMode = true;
+ // Enable night mode.
+ nestedScrollWebView.setNightMode(true);
break;
case DomainsDatabaseHelper.NIGHT_MODE_DISABLED:
- nightMode = false;
+ // Disable night mode.
+ nestedScrollWebView.setNightMode(false);
break;
}
- // Store the domain JavaScript status. This is used by the options menu night mode toggle.
- domainSettingsJavaScriptEnabled = javaScriptEnabled;
-
// Enable JavaScript if night mode is enabled.
- if (nightMode) {
- javaScriptEnabled = true;
- }
-
- // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0.
- if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
- pinnedSslStartDate = null;
- } else {
- pinnedSslStartDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
- }
-
- // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0.
- if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) {
- pinnedSslEndDate = null;
+ if (nestedScrollWebView.getNightMode()) {
+ // Enable JavaScript.
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
} else {
- pinnedSslEndDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+ // Set JavaScript according to the domain settings.
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(nestedScrollWebView.getDomainSettingsJavaScriptEnabled());
}
- // Close `currentHostDomainSettingsCursor`.
- currentHostDomainSettingsCursor.close();
+ // Close the current host domain settings cursor.
+ currentDomainSettingsCursor.close();
// Apply the domain settings.
- currentWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
- cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
- currentWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
+ cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
+
+ // Set third-party cookies status if API >= 21.
+ if (Build.VERSION.SDK_INT >= 21) {
+ cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, domainThirdPartyCookiesEnabled);
+ }
// Apply the form data setting if the API < 26.
if (Build.VERSION.SDK_INT < 26) {
- currentWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+ nestedScrollWebView.getSettings().setSaveFormData(saveFormData);
}
// Apply the font size.
if (fontSize == 0) { // Apply the default font size.
- currentWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+ nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
} else { // Apply the specified font size.
- currentWebView.getSettings().setTextZoom(fontSize);
- }
-
- // Set third-party cookies status if API >= 21.
- if (Build.VERSION.SDK_INT >= 21) {
- cookieManager.setAcceptThirdPartyCookies(currentWebView, thirdPartyCookiesEnabled);
+ nestedScrollWebView.getSettings().setTextZoom(fontSize);
}
// 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) {
+ if (nestedScrollWebView.getProgress() == 100) { // A URL is not loading.
// 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.
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.
- currentWebView.getSettings().setUserAgentString(defaultUserAgentName);
+ nestedScrollWebView.getSettings().setUserAgentString(defaultUserAgentName);
break;
case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
// Set the user agent to `""`, which uses the default value.
- currentWebView.getSettings().setUserAgentString("");
+ nestedScrollWebView.getSettings().setUserAgentString("");
break;
case SETTINGS_CUSTOM_USER_AGENT:
- // Set the custom user agent.
- currentWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
+ // Set the default custom user agent.
+ nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
break;
default:
// Get the user agent string from the user agent data array
- currentWebView.getSettings().setUserAgentString(userAgentDataArray[defaultUserAgentArrayPosition]);
+ nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[defaultUserAgentArrayPosition]);
}
} else { // Set the user agent according to the stored name.
// Get the array position of the user agent name.
switch (userAgentArrayPosition) {
case UNRECOGNIZED_USER_AGENT: // The user agent name contains a custom user agent.
- currentWebView.getSettings().setUserAgentString(userAgentName);
+ nestedScrollWebView.getSettings().setUserAgentString(userAgentName);
break;
case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
// Set the user agent to `""`, which uses the default value.
- currentWebView.getSettings().setUserAgentString("");
+ nestedScrollWebView.getSettings().setUserAgentString("");
break;
default:
// Get the user agent string from the user agent data array.
- currentWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
+ nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
}
}
-
- // Store the applied user agent string, which is used in the View Source activity.
- appliedUserAgentString = currentWebView.getSettings().getUserAgentString();
-
- // Update the user agent change tracker.
- userAgentChanged = !appliedUserAgentString.equals(initialUserAgent);
}
// Set swipe to refresh.
switch (swipeToRefreshInt) {
case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
- // Set swipe to refresh according to the default.
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
+
+ // Apply swipe to refresh according to the default. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
break;
case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED:
- // Enable swipe to refresh.
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(true);
+
+ // Enable swipe to refresh. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
swipeRefreshLayout.setEnabled(true);
break;
case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED:
- // Disable swipe to refresh.
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(false);
+
+ // Disable swipe to refresh. This can be removed once the minimum API >= 23 because it is continuously set by an on scroll change listener.
swipeRefreshLayout.setEnabled(false);
}
// Set the loading of webpage images.
switch (displayWebpageImagesInt) {
case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_SYSTEM_DEFAULT:
- currentWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
+ nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
break;
case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_ENABLED:
- currentWebView.getSettings().setLoadsImagesAutomatically(true);
+ nestedScrollWebView.getSettings().setLoadsImagesAutomatically(true);
break;
case DomainsDatabaseHelper.DISPLAY_WEBPAGE_IMAGES_DISABLED:
- currentWebView.getSettings().setLoadsImagesAutomatically(false);
+ nestedScrollWebView.getSettings().setLoadsImagesAutomatically(false);
break;
}
- // Set a green background on URL edit text to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
+ // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
if (darkTheme) {
- urlEditText.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+ urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
} else {
- urlEditText.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+ urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
}
} else { // The new URL does not have custom domain settings. Load the defaults.
- // Store the values from `sharedPreferences` in variables.
- javaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
- firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies", false);
- thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
- domStorageEnabled = sharedPreferences.getBoolean("dom_storage", false);
- saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26.
- easyListEnabled = sharedPreferences.getBoolean("easylist", true);
- easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);
- fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboys_annoyance_list", true);
- fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboys_social_blocking_list", true);
- ultraPrivacyEnabled = sharedPreferences.getBoolean("ultraprivacy", true);
- blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false);
-
- // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
- if (nightMode) {
- javaScriptEnabled = true;
+ // Store the values from the shared preferences.
+ boolean defaultJavaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
+ nestedScrollWebView.setAcceptFirstPartyCookies(sharedPreferences.getBoolean("first_party_cookies", false));
+ boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
+ nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false));
+ boolean saveFormData = sharedPreferences.getBoolean("save_form_data", false); // Form data can be removed once the minimum API >= 26.
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, sharedPreferences.getBoolean("easylist", true));
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, sharedPreferences.getBoolean("easyprivacy", true));
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean("fanboys_annoyance_list", true));
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean("fanboys_social_blocking_list", true));
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY, sharedPreferences.getBoolean("ultraprivacy", true));
+ nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false));
+ nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
+
+ // Enable JavaScript if night mode is enabled.
+ if (nestedScrollWebView.getNightMode()) {
+ // Enable JavaScript.
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
+ } else {
+ // Set JavaScript according to the domain settings.
+ nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled);
}
// Apply the default settings.
- currentWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
- cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
- currentWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
- currentWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
- swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+ cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
+ nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
// Apply the form data setting if the API < 26.
if (Build.VERSION.SDK_INT < 26) {
- currentWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+ nestedScrollWebView.getSettings().setSaveFormData(saveFormData);
}
+ // Store the swipe to refresh status in the nested scroll WebView.
+ nestedScrollWebView.setSwipeToRefresh(defaultSwipeToRefresh);
+
+ // Apply swipe to refresh according to the default.
+ swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+
// Reset the pinned variables.
- currentWebView.setDomainSettingsDatabaseId(-1);
- pinnedSslCertificate = false;
- pinnedSslIssuedToCName = "";
- pinnedSslIssuedToOName = "";
- pinnedSslIssuedToUName = "";
- pinnedSslIssuedByCName = "";
- pinnedSslIssuedByOName = "";
- pinnedSslIssuedByUName = "";
- pinnedSslStartDate = null;
- pinnedSslEndDate = null;
- pinnedIpAddresses = false;
- pinnedHostIpAddresses = "";
+ nestedScrollWebView.setDomainSettingsDatabaseId(-1);
// Set third-party cookies status if API >= 21.
if (Build.VERSION.SDK_INT >= 21) {
- cookieManager.setAcceptThirdPartyCookies(currentWebView, thirdPartyCookiesEnabled);
+ cookieManager.setAcceptThirdPartyCookies(nestedScrollWebView, defaultThirdPartyCookiesEnabled);
}
// 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) {
+ if (nestedScrollWebView.getProgress() == 100) { // A URL is not loading.
// Get the array position of the user agent name.
int userAgentArrayPosition = userAgentNamesArray.getPosition(defaultUserAgentName);
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.
- currentWebView.getSettings().setUserAgentString(defaultUserAgentName);
+ nestedScrollWebView.getSettings().setUserAgentString(defaultUserAgentName);
break;
case SETTINGS_WEBVIEW_DEFAULT_USER_AGENT:
// Set the user agent to `""`, which uses the default value.
- currentWebView.getSettings().setUserAgentString("");
+ nestedScrollWebView.getSettings().setUserAgentString("");
break;
case SETTINGS_CUSTOM_USER_AGENT:
- // Set the custom user agent.
- currentWebView.getSettings().setUserAgentString(defaultCustomUserAgentString);
+ // Set the default custom user agent.
+ nestedScrollWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
break;
default:
// Get the user agent string from the user agent data array
- currentWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
+ nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
}
-
- // Store the applied user agent string, which is used in the View Source activity.
- appliedUserAgentString = currentWebView.getSettings().getUserAgentString();
-
- // Update the user agent change tracker.
- userAgentChanged = !appliedUserAgentString.equals(initialUserAgent);
}
// Set the loading of webpage images.
- currentWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
+ nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
- // Set a transparent background on URL edit text. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
- urlEditText.setBackgroundDrawable(getResources().getDrawable(R.color.transparent));
+ // Set a transparent background on URL edit text. The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21.
+ urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
}
// Close the domains database helper.
domainsDatabaseHelper.close();
- // Update the privacy icons, but only if `mainMenu` has already been populated.
- if (mainMenu != null) {
- updatePrivacyIcons(true);
- }
+ // Update the privacy icons.
+ updatePrivacyIcons(true);
}
// Reload the website if returning from the Domains activity.
if (reloadWebsite) {
- currentWebView.reload();
+ nestedScrollWebView.reload();
}
// Return the user agent changed status.
- return userAgentChanged;
+ return !nestedScrollWebView.getSettings().getUserAgentString().equals(initialUserAgent);
}
private void applyProxyThroughOrbot(boolean reloadWebsite) {
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Get the search preferences.
- String homepageString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value));
- String torHomepageString = sharedPreferences.getString("tor_homepage", getString(R.string.tor_homepage_default_value));
+ // Get the search and theme preferences.
String torSearchString = sharedPreferences.getString("tor_search", getString(R.string.tor_search_default_value));
String torSearchCustomUrlString = sharedPreferences.getString("tor_search_custom_url", getString(R.string.tor_search_custom_url_default_value));
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));
+ boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
// Get a handle for the action bar. `getSupportActionBar()` must be used until the minimum API >= 21.
ActionBar actionBar = getSupportActionBar();
// Set the homepage, search, and proxy options.
if (proxyThroughOrbot) { // Set the Tor options.
- // Set `torHomepageString` as `homepage`.
- homepage = torHomepageString;
-
- // If formattedUrlString is null assign the homepage to it.
- if (formattedUrlString == null) {
- formattedUrlString = homepage;
- }
-
// Set the search URL.
if (torSearchString.equals("Custom URL")) { // Get the custom URL string.
searchURL = torSearchCustomUrlString;
// Set the proxy. `this` refers to the current activity where an `AlertDialog` might be displayed.
OrbotProxyHelper.setProxy(getApplicationContext(), this, "localhost", "8118");
- // Set the `appBar` background to indicate proxying through Orbot is enabled. `this` refers to the context.
+ // Set the `appBar` background to indicate proxying through Orbot is enabled.
if (darkTheme) {
actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.dark_blue_30));
} else {
currentWebView.getSettings().setUseWideViewPort(false);
// Load a waiting page. `null` specifies no encoding, which defaults to ASCII.
- currentWebView.loadData(waitingForOrbotHtmlString, "text/html", null);
+ currentWebView.loadData("<html><body><br/><center><h1>" + getString(R.string.waiting_for_orbot) + "</h1></center></body></html>", "text/html", null);
} else if (reloadWebsite) { // Orbot is ready and the website should be reloaded.
// Reload the website.
currentWebView.reload();
}
} else { // Set the non-Tor options.
- // Set `homepageString` as `homepage`.
- homepage = homepageString;
-
- // If formattedUrlString is null assign the homepage to it.
- if (formattedUrlString == null) {
- formattedUrlString = homepage;
- }
-
// Set the search URL.
if (searchString.equals("Custom URL")) { // Get the custom URL string.
searchURL = searchCustomUrlString;
// Reset the proxy to default. The host is `""` and the port is `"0"`.
OrbotProxyHelper.setProxy(getApplicationContext(), this, "", "0");
- // Set the default `appBar` background. `this` refers to the context.
+ // Set the default `appBar` background.
if (darkTheme) {
actionBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.gray_900));
} else {
// Reload the WebViews if requested.
if (reloadWebsite) {
// Reload the WebViews.
- for (int i = 0; i < webViewPagerAdapter.webViewFragmentsList.size(); i++) {
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
// Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.webViewFragmentsList.get(i);
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
// Get the fragment view.
View fragmentView = webViewTabFragment.getView();
}
private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) {
- // 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 the privacy icon.
- if (javaScriptEnabled) { // JavaScript is enabled.
- privacyMenuItem.setIcon(R.drawable.javascript_enabled);
- } else if (firstPartyCookiesEnabled) { // JavaScript is disabled but cookies are enabled.
- privacyMenuItem.setIcon(R.drawable.warning);
- } else { // All the dangerous features are disabled.
- privacyMenuItem.setIcon(R.drawable.privacy_mode);
- }
+ // Only update the privacy icons if the options menu and the current WebView have already been populated.
+ if ((optionsMenu != null) && (currentWebView != null)) {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Update the first-party cookies icon.
- if (firstPartyCookiesEnabled) { // First-party cookies are enabled.
- firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
- } else { // First-party cookies are disabled.
- if (darkTheme) {
- firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_dark);
- } else {
- firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_light);
+ // Get the theme and screenshot preferences.
+ boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+
+ // Get handles for the menu items.
+ MenuItem privacyMenuItem = optionsMenu.findItem(R.id.toggle_javascript);
+ MenuItem firstPartyCookiesMenuItem = optionsMenu.findItem(R.id.toggle_first_party_cookies);
+ MenuItem domStorageMenuItem = optionsMenu.findItem(R.id.toggle_dom_storage);
+ MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
+
+ // Update the privacy icon.
+ if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled.
+ privacyMenuItem.setIcon(R.drawable.javascript_enabled);
+ } else if (currentWebView.getAcceptFirstPartyCookies()) { // JavaScript is disabled but cookies are enabled.
+ privacyMenuItem.setIcon(R.drawable.warning);
+ } else { // All the dangerous features are disabled.
+ privacyMenuItem.setIcon(R.drawable.privacy_mode);
}
- }
- // Update the DOM storage icon.
- if (javaScriptEnabled && domStorageEnabled) { // Both JavaScript and DOM storage are enabled.
- domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
- } else if (javaScriptEnabled) { // JavaScript is enabled but DOM storage is disabled.
- if (darkTheme) {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_dark);
- } else {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_light);
+ // Update the first-party cookies icon.
+ if (currentWebView.getAcceptFirstPartyCookies()) { // First-party cookies are enabled.
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
+ } else { // First-party cookies are disabled.
+ if (darkTheme) {
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_dark);
+ } else {
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_light);
+ }
+ }
+
+ // Update the DOM storage icon.
+ if (currentWebView.getSettings().getJavaScriptEnabled() && currentWebView.getSettings().getDomStorageEnabled()) { // Both JavaScript and DOM storage are enabled.
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
+ } else if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled but DOM storage is disabled.
+ if (darkTheme) {
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_dark);
+ } else {
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_light);
+ }
+ } else { // JavaScript is disabled, so DOM storage is ghosted.
+ if (darkTheme) {
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark);
+ } else {
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_light);
+ }
}
- } else { // JavaScript is disabled, so DOM storage is ghosted.
+
+ // Update the refresh icon.
if (darkTheme) {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark);
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_dark);
} else {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_light);
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_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`.
- if (runInvalidateOptionsMenu) {
- invalidateOptionsMenu();
+ // `invalidateOptionsMenu` calls `onPrepareOptionsMenu()` and redraws the icons in the `AppBar`.
+ if (runInvalidateOptionsMenu) {
+ invalidateOptionsMenu();
+ }
}
}
}
};
- // Populate the `ListView` with the adapter.
+ // Get a handle for the bookmarks list view.
+ ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
+
+ // Populate the list view with the adapter.
bookmarksListView.setAdapter(bookmarksCursorAdapter);
+ // Get a handle for the bookmarks title text view.
+ TextView bookmarksTitleTextView = findViewById(R.id.bookmarks_title_textview);
+
// Set the bookmarks drawer title.
if (currentBookmarksFolder.isEmpty()) {
bookmarksTitleTextView.setText(R.string.bookmarks);
startActivity(openWithBrowserIntent);
}
- public static void checkPinnedMismatch(int domainSettingsDatabaseId) {
- 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();
- }
+ public void addTab(View view) {
+ // Get a handle for the tab layout and the view pager.
+ TabLayout tabLayout = findViewById(R.id.tablayout);
+ ViewPager webViewPager = findViewById(R.id.webviewpager);
- if (currentWebsiteSslEndDate != null) {
- currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString();
- }
+ // Get the new page number. The page numbers are 0 indexed, so the new page number will match the current count.
+ int newTabNumber = tabLayout.getTabCount();
- if (pinnedSslStartDate != null) {
- pinnedSslStartDateString = pinnedSslStartDate.toString();
- }
+ // Add a new tab.
+ tabLayout.addTab(tabLayout.newTab());
- if (pinnedSslEndDate != null) {
- pinnedSslEndDateString = pinnedSslEndDate.toString();
- }
+ // Get the new tab.
+ TabLayout.Tab newTab = tabLayout.getTabAt(newTabNumber);
- // 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)))) {
+ // Remove the lint warning below that the current tab might be null.
+ assert newTab != null;
- // Get a handle for the pinned mismatch alert dialog.
- DialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(domainSettingsDatabaseId, pinnedSslCertificate, pinnedIpAddresses);
+ // Set a custom view on the new tab.
+ newTab.setCustomView(R.layout.tab_custom_view);
- // Show the pinned mismatch alert dialog.
- pinnedMismatchDialogFragment.show(fragmentManager, "Pinned Mismatch");
- }
- }
+ // Add the new WebView page.
+ webViewPagerAdapter.addPage(newTabNumber, webViewPager);
}
- private class WebViewPagerAdapter extends FragmentPagerAdapter {
- // The WebView fragments list contains all the WebViews.
- private LinkedList<WebViewTabFragment> webViewFragmentsList = new LinkedList<>();
+ private void setCurrentWebView(int pageNumber) {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- // Define the constructor.
- private WebViewPagerAdapter(FragmentManager fragmentManager){
- // Run the default commands.
- super(fragmentManager);
- }
+ // Get the theme preference.
+ boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
- @Override
- public int getCount() {
- // Return the number of pages.
- return webViewFragmentsList.size();
- }
+ // Get handles for the URL views.
+ RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
+ EditText urlEditText = findViewById(R.id.url_edittext);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- @Override
- public int getItemPosition(@NonNull Object object) {
- //noinspection SuspiciousMethodCalls
- if (webViewFragmentsList.contains(object)) {
- // The tab has not been deleted.
- return POSITION_UNCHANGED;
- } else {
- // The tab has been deleted.
- return POSITION_NONE;
- }
- }
+ //Stop the swipe to refresh indicator if it is running
+ swipeRefreshLayout.setRefreshing(false);
- @Override
- public Fragment getItem(int pageNumber) {
- // Get a WebView for a particular page. Page numbers are 0 indexed.
- return webViewFragmentsList.get(pageNumber);
- }
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(pageNumber);
- private void addPage() {
- // Add a new page. The pages and tabs are 0 indexed, so the size of the current list equals the number of the next page.
- webViewFragmentsList.add(WebViewTabFragment.createTab(webViewFragmentsList.size()));
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
- // Update the view pager.
- notifyDataSetChanged();
- }
+ // Set the current WebView if the fragment view is not null.
+ if (fragmentView != null) {
+ // Store the current WebView.
+ currentWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
- private void deletePage(int pageNumber) {
- // Get a handle for the tab layout.
- TabLayout tabLayout = findViewById(R.id.tablayout);
+ // Update the status of swipe to refresh.
+ if (currentWebView.getSwipeToRefresh()) { // Swipe to refresh is enabled.
+ if (Build.VERSION.SDK_INT >= 23) { // For API >= 23, swipe refresh layout is continuously updated with an on scroll change listener and only enabled if the WebView is scrolled to the top.
+ // Enable the swipe refresh layout if the WebView is scrolled all the way to the top.
+ swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+ } else {
+ // Enable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(true);
+ }
+ } else { // Swipe to refresh is disabled.
+ // Disable the swipe refresh layout.
+ swipeRefreshLayout.setEnabled(false);
+ }
- // TODO always move to the next tab if possible.
- // Select a tab that is not being deleted.
- if (pageNumber == 0) { // The first tab is being deleted.
- // Get a handle for the second tab. The tabs are 0 indexed.
- TabLayout.Tab secondTab = tabLayout.getTabAt(1);
+ // Get a handle for the cookie manager.
+ CookieManager cookieManager = CookieManager.getInstance();
- // Remove the incorrect lint warning below that the second tab might be null.
- assert secondTab != null;
+ // Set the first-party cookie status.
+ cookieManager.setAcceptCookie(currentWebView.getAcceptFirstPartyCookies());
- // Select the second tab.
- secondTab.select();
- } else { // The first tab is not being deleted.
- // Get a handle for the previous tab.
- TabLayout.Tab previousTab = tabLayout.getTabAt(pageNumber - 1);
+ // Update the privacy icons. `true` redraws the icons in the app bar.
+ updatePrivacyIcons(true);
- // Remove the incorrect lint warning below tha the previous tab might be null.
- assert previousTab != null;
+ // Get a handle for the input method manager.
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- // Select the previous tab.
- previousTab.select();
- }
+ // Remove the lint warning below that the input method manager might be null.
+ assert inputMethodManager != null;
- // Delete the page.
- webViewFragmentsList.remove(pageNumber);
+ // Get the current URL.
+ String url = currentWebView.getUrl();
- // Delete the tab.
- tabLayout.removeTabAt(pageNumber);
+ if ((url == null) || url.equals("about:blank")) { // The WebView is blank.
+ // Display the hint in the URL edit text.
+ urlEditText.setText("");
- // Update the view pager.
- notifyDataSetChanged();
- }
- }
+ // Request focus for the URL text box.
+ urlEditText.requestFocus();
- public void addTab(View view) {
- // Add the new WebView page.
- webViewPagerAdapter.addPage();
+ // Display the keyboard.
+ inputMethodManager.showSoftInput(urlEditText, 0);
+ } else { // The WebView has a loaded URL.
+ // Clear the focus from the URL text box.
+ urlEditText.clearFocus();
- // Get a handle for the tab layout.
- TabLayout tabLayout = findViewById(R.id.tablayout);
+ // Hide the soft keyboard.
+ inputMethodManager.hideSoftInputFromWindow(currentWebView.getWindowToken(), 0);
- // Get a handle for the new tab. The tabs are 0 indexed.
- TabLayout.Tab newTab = tabLayout.getTabAt(tabLayout.getTabCount() - 1);
+ // Display the current URL in the URL text box.
+ urlEditText.setText(url);
- // Remove the incorrect warning below that the new tab might be null.
- assert newTab != null;
+ // Highlight the URL text.
+ highlightUrlText();
+ }
- // Move the tab layout to the new tab.
- newTab.select();
+ // Set the background to indicate the domain settings status.
+ if (currentWebView.getDomainSettingsApplied()) {
+ // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
+ if (darkTheme) {
+ urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+ } else {
+ urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+ }
+ } else {
+ urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+ }
+ }
}
@Override
- public void initializeWebView(int tabNumber, ProgressBar progressBar, NestedScrollWebView nestedScrollWebView) {
+ public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar) {
// Get handles for the activity views.
- final FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
- final DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- final RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
- final ActionBar actionBar = getSupportActionBar();
+ FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
+ DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+ RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
+ ActionBar actionBar = getSupportActionBar();
+ LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
EditText urlEditText = findViewById(R.id.url_edittext);
- final TabLayout tabLayout = findViewById(R.id.tablayout);
- final SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+ TabLayout tabLayout = findViewById(R.id.tablayout);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- // Remove the incorrect lint warnings below that the some of the views might be null.
+ // Remove the incorrect lint warning below that the action bar might be null.
assert actionBar != null;
- // TODO. Still doesn't work right.
- // Create the tab if it doesn't already exist.
- try {
- TabLayout.Tab tab = tabLayout.getTabAt(tabNumber);
-
- assert tab != null;
-
- tab.getCustomView();
- } catch (Exception exception) {
- tabLayout.addTab(tabLayout.newTab());
- }
-
- // Get the current tab.
- TabLayout.Tab currentTab = tabLayout.getTabAt(tabNumber);
+ // Get a handle for the activity
+ Activity activity = this;
- // Remove the lint warning below that the current tab might be null.
- assert currentTab != null;
-
- // Set a custom view on the current tab.
- currentTab.setCustomView(R.layout.custom_tab_view);
-
- // Get the custom view from the tab.
- View currentTabView = currentTab.getCustomView();
+ // Get a handle for the input method manager.
+ InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- // Remove the incorrect warning below that the current tab view might be null.
- assert currentTabView != null;
+ // Instantiate the blocklist helper.
+ BlockListHelper blockListHelper = new BlockListHelper();
- // Get the current views from the tab.
- ImageView tabFavoriteIconImageView = currentTabView.findViewById(R.id.favorite_icon_imageview);
- TextView tabTitleTextView = currentTabView.findViewById(R.id.title_textview);
+ // Remove the lint warning below that the input method manager might be null.
+ assert inputMethodManager != null;
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
// Get the relevant preferences.
boolean downloadWithExternalApp = sharedPreferences.getBoolean("download_with_external_app", false);
+ // Initialize the favorite icon.
+ nestedScrollWebView.initializeFavoriteIcon();
+
+ // Set the app bar scrolling.
+ nestedScrollWebView.setNestedScrollingEnabled(sharedPreferences.getBoolean("scroll_app_bar", true));
+
// Allow pinch to zoom.
nestedScrollWebView.getSettings().setBuiltInZoomControls(true);
if (inFullScreenBrowsingMode) { // Switch to full screen mode.
// Hide the app bar if specified.
if (hideAppBar) {
+ // Close the find on page bar if it is visible.
+ closeFindOnPage(null);
+
+ // Hide the tab linear layout.
+ tabsLinearLayout.setVisibility(View.GONE);
+
+ // Hide the action bar.
actionBar.hide();
}
rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
} else { // Switch to normal viewing mode.
- // Show the app bar.
+ // Show the tab linear layout.
+ tabsLinearLayout.setVisibility(View.VISIBLE);
+
+ // Show the action bar.
actionBar.show();
// Show the banner ad in the free flavor.
downloadContentLength = contentLength;
// Show a dialog if the user has previously denied the permission.
- if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show a dialog explaining the request first.
// Instantiate 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(fragmentManager, getString(R.string.download_location));
+ downloadLocationPermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.download_location));
} else { // Show the permission request directly.
// Request the permission. The download dialog will be launched by `onRequestPermissionResult()`.
- ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, DOWNLOAD_FILE_REQUEST_CODE);
+ 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.
DialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength);
// Show the download file alert dialog.
- downloadFileDialogFragment.show(fragmentManager, getString(R.string.download));
+ downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download));
}
}
});
}
});
+ if (Build.VERSION.SDK_INT >= 23) {
+ nestedScrollWebView.setOnScrollChangeListener((View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) -> {
+ // Update the status of swipe to refresh if it is enabled.
+ if (nestedScrollWebView.getSwipeToRefresh()) {
+ // Only enable swipe to refresh if the WebView is scrolled to the top.
+ swipeRefreshLayout.setEnabled(scrollY == 0);
+ }
+ });
+ }
+
// Set the web chrome client.
nestedScrollWebView.setWebChromeClient(new WebChromeClient() {
// Update the progress bar when a page is loading.
@Override
public void onProgressChanged(WebView view, int progress) {
// Inject the night mode CSS if night mode is enabled.
- if (nightMode) {
+ if (nestedScrollWebView.getNightMode()) { // Night mode is enabled.
// `background-color: #212121` sets the background to be dark gray. `color: #BDBDBD` sets the text color to be light gray. `box-shadow: none` removes a lower underline on links
// used by WordPress. `text-decoration: none` removes all text underlines. `text-shadow: none` removes text shadows, which usually have a hard coded color.
// `border: none` removes all borders, which can also be used to underline text. `a {color: #1565C0}` sets links to be a dark blue.
}
};
- // Displaying of `mainWebView` after 500 milliseconds.
+ // Display the WebView after 500 milliseconds.
displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
});
+ } else { // Night mode is disabled.
+ // Display the nested scroll WebView if night mode is disabled.
+ // Because of a race condition between `applyDomainSettings` and `onPageStarted`,
+ // when night mode is set by domain settings the WebView may be hidden even if night mode is not currently enabled.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
}
// Update the progress bar.
// Hide the progress bar.
progressBar.setVisibility(View.GONE);
- // Display `mainWebView` if night mode is disabled.
- // Because of a race condition between `applyDomainSettings` and `onPageStarted`, when night mode is set by domain settings the `WebView` may be hidden even if night mode is not
- // currently enabled.
- if (!nightMode) {
- nestedScrollWebView.setVisibility(View.VISIBLE);
- }
-
//Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.setRefreshing(false);
}
public void onReceivedIcon(WebView view, Bitmap icon) {
// Only update the favorite icon if the website has finished loading.
if (progressBar.getVisibility() == View.GONE) {
- // Save a copy of the favorite icon.
- // TODO. We need to save and access the icons for each tab.
- favoriteIconBitmap = icon;
+ // Store the new favorite icon.
+ nestedScrollWebView.setFavoriteOrDefaultIcon(icon);
+
+ // Get the current page position.
+ int currentPosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+
+ // Get the current tab.
+ TabLayout.Tab tab = tabLayout.getTabAt(currentPosition);
+
+ // Check to see if the tab has been populated.
+ if (tab != null) {
+ // Get the custom view from the tab.
+ View tabView = tab.getCustomView();
- tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
+ // Check to see if the custom tab view has been populated.
+ if (tabView != null) {
+ // Get the favorite icon image view from the tab.
+ ImageView tabFavoriteIconImageView = tabView.findViewById(R.id.favorite_icon_imageview);
+
+ // Display the favorite icon in the tab.
+ tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
+ }
+ }
}
}
// Save a copy of the title when it changes.
@Override
public void onReceivedTitle(WebView view, String title) {
- // Set the title as the tab text.
- tabTitleTextView.setText(title);
+ // Get the current page position.
+ int currentPosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+
+ // Get the current tab.
+ TabLayout.Tab tab = tabLayout.getTabAt(currentPosition);
+
+ // Only populate the title text view if the tab has been fully created.
+ if (tab != null) {
+ // Get the custom view from the tab.
+ View tabView = tab.getCustomView();
+
+ // Remove the incorrect warning below that the current tab view might be null.
+ assert tabView != null;
+
+ // Get the title text view from the tab.
+ TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
+
+ // Set the title as the tab text.
+ tabTitleTextView.setText(title);
+ }
}
// Enter full screen video.
@Override
public void onShowCustomView(View video, CustomViewCallback callback) {
+ // Get a handle for the full screen video frame layout.
+ FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
+
// Set the full screen video flag.
displayingFullScreenVideo = true;
// Exit full screen video.
@Override
public void onHideCustomView() {
+ // Get a handle for the full screen video frame layout.
+ FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
+
// Unset the full screen video flag.
displayingFullScreenVideo = false;
// Show the main content relative layout.
mainContentRelativeLayout.setVisibility(View.VISIBLE);
- // Apply the appropriate full screen mode the `SYSTEM_UI` flags.
+ // Apply the appropriate full screen mode flags.
if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
// Hide the app bar if specified.
if (hideAppBar) {
+ // Hide the tab linear layout.
+ tabsLinearLayout.setVisibility(View.GONE);
+
+ // Hide the action bar.
actionBar.hide();
}
nestedScrollWebView.setWebViewClient(new WebViewClient() {
// `shouldOverrideUrlLoading` makes this `WebView` the default handler for URLs inside the app, so that links are not kicked out to other apps.
// The deprecated `shouldOverrideUrlLoading` must be used until API >= 24.
- @SuppressWarnings("deprecation")
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("http")) { // Load the URL in Privacy Browser.
- // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
- formattedUrlString = "";
-
- // Apply the domain settings for the new URL. `applyDomainSettings` doesn't do anything if the domain has not changed.
- boolean userAgentChanged = applyDomainSettings(url, true, false);
+ // Apply the domain settings for the new URL. This doesn't do anything if the domain has not changed.
+ boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
// Check if the user agent has changed.
if (userAgentChanged) {
}
// Check requests against the block lists. The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21.
- @SuppressWarnings("deprecation")
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+ // Get a handle for the navigation view.
+ NavigationView navigationView = findViewById(R.id.navigationview);
+
+ // Get a handle for the navigation menu.
+ Menu navigationMenu = navigationView.getMenu();
+
+ // Get a handle for the navigation requests menu item. The menu is 0 based.
+ MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+
// Create an empty web resource response to be used if the resource request is blocked.
WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
// Reset the whitelist results tracker.
- whiteListResultStringArray = null;
+ String[] whitelistResultStringArray = null;
// Initialize the third party request tracker.
boolean isThirdPartyRequest = false;
- // Initialize the current domain string.
- String currentDomain = "";
+ // Get the current URL. `.getUrl()` throws an error because operations on the WebView cannot be made from this thread.
+ String currentBaseDomain = nestedScrollWebView.getCurrentDomainName();
- // Nobody is happy when comparing null strings.
- if (!(formattedUrlString == null) && !(url == null)) {
- // Get the domain strings to URIs.
- Uri currentDomainUri = Uri.parse(formattedUrlString);
- Uri requestDomainUri = Uri.parse(url);
+ // Store a copy of the current domain for use in later requests.
+ String currentDomain = currentBaseDomain;
- // Get the domain host names.
- String currentBaseDomain = currentDomainUri.getHost();
- String requestBaseDomain = requestDomainUri.getHost();
+ // Nobody is happy when comparing null strings.
+ if ((currentBaseDomain != null) && (url != null)) {
+ // Convert the request URL to a URI.
+ Uri requestUri = Uri.parse(url);
- // Update the current domain variable.
- currentDomain = currentBaseDomain;
+ // Get the request host name.
+ String requestBaseDomain = requestUri.getHost();
- // Only compare the current base domain and the request base domain if neither is null.
- if (!(currentBaseDomain == null) && !(requestBaseDomain == null)) {
+ // Only check for third-party requests if the current base domain is not empty and the request domain is not null.
+ if (!currentBaseDomain.isEmpty() && (requestBaseDomain != null)) {
// Determine the current base domain.
while (currentBaseDomain.indexOf(".", currentBaseDomain.indexOf(".") + 1) > 0) { // There is at least one subdomain.
// Remove the first subdomain.
}
}
+ // Get the current WebView page position.
+ int webViewPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+
+ // Determine if the WebView is currently displayed.
+ boolean webViewDisplayed = (webViewPagePosition == tabLayout.getSelectedTabPosition());
+
// Block third-party requests if enabled.
- if (isThirdPartyRequest && blockAllThirdPartyRequests) {
+ if (isThirdPartyRequest && nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS)) {
+ // Add the result to the resource requests.
+ nestedScrollWebView.addResourceRequest(new String[]{BlockListHelper.REQUEST_THIRD_PARTY, url});
+
// Increment the blocked requests counters.
- blockedRequests++;
- thirdPartyBlockedRequests++;
-
- // Update the titles of the blocklist menu items. This must be run from the UI thread.
- activity.runOnUiThread(() -> {
- navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- blockAllThirdPartyRequestsMenuItem.setTitle(thirdPartyBlockedRequests + " - " + getString(R.string.block_all_third_party_requests));
- });
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS);
- // Add the request to the log.
- resourceRequests.add(new String[]{String.valueOf(REQUEST_THIRD_PARTY), url});
+ // Update the titles of the blocklist menu items if the WebView is currently displayed.
+ if (webViewDisplayed) {
+ // Updating the UI must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ // Update the menu item titles.
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+
+ // Update the options menu if it has been populated.
+ if (optionsMenu != null) {
+ optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+ optionsMenu.findItem(R.id.block_all_third_party_requests).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " +
+ getString(R.string.block_all_third_party_requests));
+ }
+ });
+ }
// Return an empty web resource response.
return emptyWebResourceResponse;
}
// Check UltraPrivacy if it is enabled.
- if (ultraPrivacyEnabled) {
- if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, ultraPrivacy)) {
- // Increment the blocked requests counters.
- blockedRequests++;
- ultraPrivacyBlockedRequests++;
+ if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY)) {
+ // Check the URL against UltraPrivacy.
+ String[] ultraPrivacyResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraPrivacy);
- // Update the titles of the blocklist menu items. This must be run from the UI thread.
- activity.runOnUiThread(() -> {
- navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- ultraPrivacyMenuItem.setTitle(ultraPrivacyBlockedRequests + " - " + getString(R.string.ultraprivacy));
- });
+ // Process the UltraPrivacy results.
+ if (ultraPrivacyResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched UltraPrivacy's blacklist.
+ // Add the result to the resource requests.
+ nestedScrollWebView.addResourceRequest(new String[] {ultraPrivacyResults[0], ultraPrivacyResults[1], ultraPrivacyResults[2], ultraPrivacyResults[3], ultraPrivacyResults[4],
+ ultraPrivacyResults[5]});
+
+ // Increment the blocked requests counters.
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.ULTRA_PRIVACY);
+
+ // Update the titles of the blocklist menu items if the WebView is currently displayed.
+ if (webViewDisplayed) {
+ // Updating the UI must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ // Update the menu item titles.
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+
+ // Update the options menu if it has been populated.
+ if (optionsMenu != null) {
+ optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+ optionsMenu.findItem(R.id.ultraprivacy).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRA_PRIVACY) + " - " + getString(R.string.ultraprivacy));
+ }
+ });
+ }
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
- }
-
- // If the whitelist result is not null, the request has been allowed by UltraPrivacy.
- if (whiteListResultStringArray != null) {
+ } else if (ultraPrivacyResults[0].equals(BlockListHelper.REQUEST_ALLOWED)) { // The resource request matched UltraPrivacy's whitelist.
// Add a whitelist entry to the resource requests array.
- resourceRequests.add(whiteListResultStringArray);
+ nestedScrollWebView.addResourceRequest(new String[] {ultraPrivacyResults[0], ultraPrivacyResults[1], ultraPrivacyResults[2], ultraPrivacyResults[3], ultraPrivacyResults[4],
+ ultraPrivacyResults[5]});
// The resource request has been allowed by UltraPrivacy. `return null` loads the requested resource.
return null;
}
// Check EasyList if it is enabled.
- if (easyListEnabled) {
- if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, easyList)) {
- // Increment the blocked requests counters.
- blockedRequests++;
- easyListBlockedRequests++;
+ if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST)) {
+ // Check the URL against EasyList.
+ String[] easyListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyList);
- // Update the titles of the blocklist menu items. This must be run from the UI thread.
- activity.runOnUiThread(() -> {
- navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- easyListMenuItem.setTitle(easyListBlockedRequests + " - " + getString(R.string.easylist));
- });
+ // Process the EasyList results.
+ if (easyListResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched EasyList's blacklist.
+ // Add the result to the resource requests.
+ nestedScrollWebView.addResourceRequest(new String[] {easyListResults[0], easyListResults[1], easyListResults[2], easyListResults[3], easyListResults[4], easyListResults[5]});
- // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
- whiteListResultStringArray = null;
+ // Increment the blocked requests counters.
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASY_LIST);
+
+ // Update the titles of the blocklist menu items if the WebView is currently displayed.
+ if (webViewDisplayed) {
+ // Updating the UI must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ // Update the menu item titles.
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+
+ // Update the options menu if it has been populated.
+ if (optionsMenu != null) {
+ optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+ optionsMenu.findItem(R.id.easylist).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASY_LIST) + " - " + getString(R.string.easylist));
+ }
+ });
+ }
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
+ } else if (easyListResults[0].equals(BlockListHelper.REQUEST_ALLOWED)) { // The resource request matched EasyList's whitelist.
+ // Update the whitelist result string array tracker.
+ whitelistResultStringArray = new String[] {easyListResults[0], easyListResults[1], easyListResults[2], easyListResults[3], easyListResults[4], easyListResults[5]};
}
}
// Check EasyPrivacy if it is enabled.
- if (easyPrivacyEnabled) {
- if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, easyPrivacy)) {
- // Increment the blocked requests counters.
- blockedRequests++;
- easyPrivacyBlockedRequests++;
+ if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY)) {
+ // Check the URL against EasyPrivacy.
+ String[] easyPrivacyResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyPrivacy);
- // Update the titles of the blocklist menu items. This must be run from the UI thread.
- activity.runOnUiThread(() -> {
- navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- easyPrivacyMenuItem.setTitle(easyPrivacyBlockedRequests + " - " + getString(R.string.easyprivacy));
- });
+ // Process the EasyPrivacy results.
+ if (easyPrivacyResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched EasyPrivacy's blacklist.
+ // Add the result to the resource requests.
+ nestedScrollWebView.addResourceRequest(new String[] {easyPrivacyResults[0], easyPrivacyResults[1], easyPrivacyResults[2], easyPrivacyResults[3], easyPrivacyResults[4],
+ easyPrivacyResults[5]});
- // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
- whiteListResultStringArray = null;
+ // Increment the blocked requests counters.
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASY_PRIVACY);
+
+ // Update the titles of the blocklist menu items if the WebView is currently displayed.
+ if (webViewDisplayed) {
+ // Updating the UI must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ // Update the menu item titles.
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+
+ // Update the options menu if it has been populated.
+ if (optionsMenu != null) {
+ optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+ optionsMenu.findItem(R.id.easyprivacy).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASY_PRIVACY) + " - " + getString(R.string.easyprivacy));
+ }
+ });
+ }
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
+ } else if (easyPrivacyResults[0].equals(BlockListHelper.REQUEST_ALLOWED)) { // The resource request matched EasyPrivacy's whitelist.
+ // Update the whitelist result string array tracker.
+ whitelistResultStringArray = new String[] {easyPrivacyResults[0], easyPrivacyResults[1], easyPrivacyResults[2], easyPrivacyResults[3], easyPrivacyResults[4], easyPrivacyResults[5]};
}
}
// Check Fanboy’s Annoyance List if it is enabled.
- if (fanboysAnnoyanceListEnabled) {
- if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList)) {
- // Increment the blocked requests counters.
- blockedRequests++;
- fanboysAnnoyanceListBlockedRequests++;
+ if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST)) {
+ // Check the URL against Fanboy's Annoyance List.
+ String[] fanboysAnnoyanceListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList);
- // Update the titles of the blocklist menu items. This must be run from the UI thread.
- activity.runOnUiThread(() -> {
- navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- fanboysAnnoyanceListMenuItem.setTitle(fanboysAnnoyanceListBlockedRequests + " - " + getString(R.string.fanboys_annoyance_list));
- });
+ // Process the Fanboy's Annoyance List results.
+ if (fanboysAnnoyanceListResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched Fanboy's Annoyance List's blacklist.
+ // Add the result to the resource requests.
+ nestedScrollWebView.addResourceRequest(new String[] {fanboysAnnoyanceListResults[0], fanboysAnnoyanceListResults[1], fanboysAnnoyanceListResults[2], fanboysAnnoyanceListResults[3],
+ fanboysAnnoyanceListResults[4], fanboysAnnoyanceListResults[5]});
- // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
- whiteListResultStringArray = null;
+ // Increment the blocked requests counters.
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST);
+
+ // Update the titles of the blocklist menu items if the WebView is currently displayed.
+ if (webViewDisplayed) {
+ // Updating the UI must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ // Update the menu item titles.
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+
+ // Update the options menu if it has been populated.
+ if (optionsMenu != null) {
+ optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+ optionsMenu.findItem(R.id.fanboys_annoyance_list).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " +
+ getString(R.string.fanboys_annoyance_list));
+ }
+ });
+ }
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
+ } else if (fanboysAnnoyanceListResults[0].equals(BlockListHelper.REQUEST_ALLOWED)){ // The resource request matched Fanboy's Annoyance List's whitelist.
+ // Update the whitelist result string array tracker.
+ whitelistResultStringArray = new String[] {fanboysAnnoyanceListResults[0], fanboysAnnoyanceListResults[1], fanboysAnnoyanceListResults[2], fanboysAnnoyanceListResults[3],
+ fanboysAnnoyanceListResults[4], fanboysAnnoyanceListResults[5]};
}
- } else if (fanboysSocialBlockingListEnabled) { // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled.
- if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, fanboysSocialList)) {
- // Increment the blocked requests counters.
- blockedRequests++;
- fanboysSocialBlockingListBlockedRequests++;
+ } else if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST)) { // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled.
+ // Check the URL against Fanboy's Annoyance List.
+ String[] fanboysSocialListResults = blockListHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, fanboysSocialList);
- // Update the titles of the blocklist menu items. This must be run from the UI thread.
- activity.runOnUiThread(() -> {
- navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- blocklistsMenuItem.setTitle(getString(R.string.requests) + " - " + blockedRequests);
- fanboysSocialBlockingListMenuItem.setTitle(fanboysSocialBlockingListBlockedRequests + " - " + getString(R.string.fanboys_social_blocking_list));
- });
+ // Process the Fanboy's Social Blocking List results.
+ if (fanboysSocialListResults[0].equals(BlockListHelper.REQUEST_BLOCKED)) { // The resource request matched Fanboy's Social Blocking List's blacklist.
+ // Add the result to the resource requests.
+ nestedScrollWebView.addResourceRequest(new String[] {fanboysSocialListResults[0], fanboysSocialListResults[1], fanboysSocialListResults[2], fanboysSocialListResults[3],
+ fanboysSocialListResults[4], fanboysSocialListResults[5]});
- // Reset the whitelist results tracker (because otherwise it will sometimes add results to the list due to a race condition).
- whiteListResultStringArray = null;
+ // Increment the blocked requests counters.
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
+ nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST);
+
+ // Update the titles of the blocklist menu items if the WebView is currently displayed.
+ if (webViewDisplayed) {
+ // Updating the UI must be run from the UI thread.
+ activity.runOnUiThread(() -> {
+ // Update the menu item titles.
+ navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+
+ // Update the options menu if it has been populated.
+ if (optionsMenu != null) {
+ optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+ optionsMenu.findItem(R.id.fanboys_social_blocking_list).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " +
+ getString(R.string.fanboys_social_blocking_list));
+ }
+ });
+ }
// The resource request was blocked. Return an empty web resource response.
return emptyWebResourceResponse;
+ } else if (fanboysSocialListResults[0].equals(BlockListHelper.REQUEST_ALLOWED)) { // The resource request matched Fanboy's Social Blocking List's whitelist.
+ // Update the whitelist result string array tracker.
+ whitelistResultStringArray = new String[] {fanboysSocialListResults[0], fanboysSocialListResults[1], fanboysSocialListResults[2], fanboysSocialListResults[3],
+ fanboysSocialListResults[4], fanboysSocialListResults[5]};
}
}
// Add the request to the log because it hasn't been processed by any of the previous checks.
- if (whiteListResultStringArray != null) { // The request was processed by a whitelist.
- resourceRequests.add(whiteListResultStringArray);
+ if (whitelistResultStringArray != null) { // The request was processed by a whitelist.
+ nestedScrollWebView.addResourceRequest(whitelistResultStringArray);
} else { // The request didn't match any blocklist entry. Log it as a default request.
- resourceRequests.add(new String[]{String.valueOf(REQUEST_DEFAULT), url});
+ nestedScrollWebView.addResourceRequest(new String[]{BlockListHelper.REQUEST_DEFAULT, url});
}
// The resource request has not been blocked. `return null` loads the requested resource.
// Handle HTTP authentication requests.
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
- // Store `handler` so it can be accessed from `onHttpAuthenticationCancel()` and `onHttpAuthenticationProceed()`.
- httpAuthHandler = handler;
+ // Store the handler.
+ nestedScrollWebView.setHttpAuthHandler(handler);
+
+ // Instantiate an HTTP authentication dialog.
+ DialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm, nestedScrollWebView.getWebViewFragmentId());
- // Display the HTTP authentication dialog.
- DialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm);
- httpAuthenticationDialogFragment.show(fragmentManager, getString(R.string.http_authentication));
+ // Show the HTTP authentication dialog.
+ httpAuthenticationDialogFragment.show(getSupportFragmentManager(), 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 = "";
+ // Get the theme preference.
+ boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
// Reset the list of resource requests.
- resourceRequests.clear();
+ nestedScrollWebView.clearResourceRequests();
- // Initialize the counters for requests blocked by each blocklist.
- blockedRequests = 0;
- easyListBlockedRequests = 0;
- easyPrivacyBlockedRequests = 0;
- fanboysAnnoyanceListBlockedRequests = 0;
- fanboysSocialBlockingListBlockedRequests = 0;
- ultraPrivacyBlockedRequests = 0;
- thirdPartyBlockedRequests = 0;
+ // Reset the requests counters.
+ nestedScrollWebView.resetRequestsCounters();
// If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
- if (nightMode) {
+ if (nestedScrollWebView.getNightMode()) {
nestedScrollWebView.setVisibility(View.INVISIBLE);
+ } else {
+ nestedScrollWebView.setVisibility(View.VISIBLE);
}
// Hide the keyboard.
// Check to see if Privacy Browser is waiting on Orbot.
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;
+ // Get the current page position.
+ int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+
+ // Update the URL text bar if the page is currently selected.
+ if (tabLayout.getSelectedTabPosition() == currentPagePosition) {
+ // Display the formatted URL text.
+ urlEditText.setText(url);
- // Display the formatted URL text.
- urlEditText.setText(formattedUrlString);
+ // Apply text highlighting to `urlTextBox`.
+ highlightUrlText();
+ }
- // Apply text highlighting to `urlTextBox`.
- highlightUrlText();
+ // Reset the list of host IP addresses.
+ nestedScrollWebView.clearCurrentIpAddresses();
// Get a URI for the current URL.
- Uri currentUri = Uri.parse(formattedUrlString);
+ Uri currentUri = Uri.parse(url);
// Get the IP addresses for the host.
- new GetHostIpAddresses(activity, currentWebView.getDomainSettingsDatabaseId()).execute(currentUri.getHost());
+ new GetHostIpAddresses(activity, getSupportFragmentManager(), nestedScrollWebView).execute(currentUri.getHost());
// Apply any custom domain settings if the URL was loaded by navigating history.
- if (navigatingHistory) {
- // Apply the domain settings.
- boolean userAgentChanged = applyDomainSettings(url, true, false);
+ if (nestedScrollWebView.getNavigatingHistory()) {
+ // Reset navigating history.
+ nestedScrollWebView.setNavigatingHistory(false);
- // Reset `navigatingHistory`.
- navigatingHistory = false;
+ // Apply the domain settings.
+ boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
// Manually load the URL if the user agent has changed, which will have caused the previous URL to be reloaded.
if (userAgentChanged) {
- loadUrl(formattedUrlString);
+ loadUrl(url);
}
}
- // 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) {
+ // Replace Refresh with Stop if the options menu has been created. (The WebView typically begins loading before the menu items are instantiated.)
+ if (optionsMenu != null) {
+ // Get a handle for the refresh menu item.
+ MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
+
// Set the title.
refreshMenuItem.setTitle(R.string.stop);
+ // Get the app bar and theme preferences.
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+
// If the icon is displayed in the AppBar, set it according to the theme.
if (displayAdditionalAppBarIcons) {
if (darkTheme) {
}
}
- // It is necessary to update `formattedUrlString` and `urlTextBox` after the page finishes loading because the final URL can change during load.
@Override
public void onPageFinished(WebView view, String url) {
// Reset the wide view port if it has been turned off by the waiting for Orbot message.
nestedScrollWebView.getSettings().setUseWideViewPort(url.startsWith("http"));
}
- // Flush any cookies to persistent storage. `CookieManager` has become very lazy about flushing cookies in recent versions.
- if (firstPartyCookiesEnabled && Build.VERSION.SDK_INT >= 21) {
- cookieManager.flush();
+ // Flush any cookies to persistent storage. The cookie manager has become very lazy about flushing cookies in recent versions.
+ if (nestedScrollWebView.getAcceptFirstPartyCookies() && Build.VERSION.SDK_INT >= 21) {
+ CookieManager.getInstance().flush();
}
- // Update the Refresh menu item if it has been created.
- if (refreshMenuItem != null) {
+ // Update the Refresh menu item if the options menu has been created.
+ if (optionsMenu != null) {
+ // Get a handle for the refresh menu item.
+ MenuItem refreshMenuItem = optionsMenu.findItem(R.id.refresh);
+
// Reset the Refresh title.
refreshMenuItem.setTitle(R.string.refresh);
+ // Get the app bar and theme preferences.
+ boolean displayAdditionalAppBarIcons = sharedPreferences.getBoolean("display_additional_app_bar_icons", false);
+ boolean darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+
// If the icon is displayed in the AppBar, reset it according to the theme.
if (displayAdditionalAppBarIcons) {
if (darkTheme) {
}
}
-
// Clear the cache and history if Incognito Mode is enabled.
if (incognitoModeEnabled) {
// Clear the cache. `true` includes disk files.
// Manually delete cache folders.
try {
+ // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+ // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+ String privateDataDirectoryString = getApplicationInfo().dataDir;
+
// Delete the main cache directory.
- privacyBrowserRuntime.exec("rm -rf " + privateDataDirectoryString + "/cache");
+ Runtime.getRuntime().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/"});
+ Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
} catch (IOException e) {
// Do nothing if an error is thrown.
}
// Update the URL text box and apply domain settings if not waiting on Orbot.
if (!waitingForOrbot) {
- // Check to see if `WebView` has set `url` to be `about:blank`.
- if (url.equals("about:blank")) { // `WebView` is blank, so `formattedUrlString` should be `""` and `urlTextBox` should display a hint.
- // Set `formattedUrlString` to `""`.
- formattedUrlString = "";
-
- urlEditText.setText(formattedUrlString);
-
- // Request focus for `urlTextBox`.
- urlEditText.requestFocus();
-
- // Display the keyboard.
- inputMethodManager.showSoftInput(urlEditText, 0);
-
- // Apply the domain settings. This clears any settings from the previous domain.
- applyDomainSettings(formattedUrlString, true, false);
- } else { // `WebView` has loaded a webpage.
- // Set the formatted URL string. Getting the URL from the WebView instead of using the one provided by `onPageFinished` makes websites like YouTube function correctly.
- formattedUrlString = nestedScrollWebView.getUrl();
-
- // Only update the URL text box if the user is not typing in it.
- if (!urlEditText.hasFocus()) {
- // Display the formatted URL text.
- urlEditText.setText(formattedUrlString);
-
- // Apply text highlighting to `urlTextBox`.
- highlightUrlText();
+ // Get the current page position.
+ int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
+
+ // Update the URL text bar if the page is currently selected.
+ if (tabLayout.getSelectedTabPosition() == currentPagePosition) {
+ // Check to see if the URL is `about:blank`.
+ if (url.equals("about:blank")) { // The WebView is blank.
+ // Display the hint in the URL edit text.
+ urlEditText.setText("");
+
+ // Request focus for the URL text box.
+ urlEditText.requestFocus();
+
+ // Display the keyboard.
+ inputMethodManager.showSoftInput(urlEditText, 0);
+
+ // Hide the WebView, which causes the default background color to be displayed according to the theme.
+ nestedScrollWebView.setVisibility(View.INVISIBLE);
+
+ // Apply the domain settings. This clears any settings from the previous domain.
+ applyDomainSettings(nestedScrollWebView, "", true, false);
+ } else { // The WebView has loaded a webpage.
+ // Only update the URL text box if the user is not typing in it.
+ if (!urlEditText.hasFocus()) {
+ // Display the final URL. Getting the URL from the WebView instead of using the one provided by `onPageFinished()` makes websites like YouTube function correctly.
+ urlEditText.setText(nestedScrollWebView.getUrl());
+
+ // Apply text highlighting to the URL.
+ highlightUrlText();
+ }
}
}
- // Store the SSL certificate so it can be accessed from `ViewSslCertificateDialog` and `PinnedMismatchDialog`.
- sslCertificate = nestedScrollWebView.getCertificate();
-
// Check the current website information against any pinned domain information if the current IP addresses have been loaded.
- if (!gettingIpAddresses) {
- checkPinnedMismatch(currentWebView.getDomainSettingsDatabaseId());
+ if ((nestedScrollWebView.hasPinnedSslCertificate() || nestedScrollWebView.hasPinnedIpAddresses()) && nestedScrollWebView.hasCurrentIpAddresses() &&
+ !nestedScrollWebView.ignorePinnedDomainInformation()) {
+ CheckPinnedMismatchHelper.checkPinnedMismatch(getSupportFragmentManager(), nestedScrollWebView);
}
}
-
- // 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.
Date currentWebsiteSslEndDate = currentWebsiteSslCertificate.getValidNotAfterDate();
// Proceed to the website if the current SSL website certificate matches the pinned domain certificate.
- if (pinnedSslCertificate &&
- currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) && currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) &&
- currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) && currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) &&
- currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) && currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) &&
- currentWebsiteSslStartDate.equals(pinnedSslStartDate) && currentWebsiteSslEndDate.equals(pinnedSslEndDate)) {
-
- // An SSL certificate is pinned and matches the current domain certificate. Proceed to the website without displaying an error.
- handler.proceed();
+ if (nestedScrollWebView.hasPinnedSslCertificate()) {
+ // Get the pinned SSL certificate.
+ ArrayList<Object> pinnedSslCertificateArrayList = nestedScrollWebView.getPinnedSslCertificate();
+
+ // Extract the arrays from the array list.
+ String[] pinnedSslCertificateStringArray = (String[]) pinnedSslCertificateArrayList.get(0);
+ Date[] pinnedSslCertificateDateArray = (Date[]) pinnedSslCertificateArrayList.get(1);
+
+ // Check if the current SSL certificate matches the pinned certificate.
+ if (currentWebsiteIssuedToCName.equals(pinnedSslCertificateStringArray[0]) && currentWebsiteIssuedToOName.equals(pinnedSslCertificateStringArray[1]) &&
+ currentWebsiteIssuedToUName.equals(pinnedSslCertificateStringArray[2]) && currentWebsiteIssuedByCName.equals(pinnedSslCertificateStringArray[3]) &&
+ currentWebsiteIssuedByOName.equals(pinnedSslCertificateStringArray[4]) && currentWebsiteIssuedByUName.equals(pinnedSslCertificateStringArray[5]) &&
+ currentWebsiteSslStartDate.equals(pinnedSslCertificateDateArray[0]) && currentWebsiteSslEndDate.equals(pinnedSslCertificateDateArray[1])) {
+
+ // An SSL certificate is pinned and matches the current domain certificate. Proceed to the website without displaying an error.
+ handler.proceed();
+ }
} else { // Either there isn't a pinned SSL certificate or it doesn't match the current website certificate.
- // Store `handler` so it can be accesses from `onSslErrorCancel()` and `onSslErrorProceed()`.
- sslErrorHandler = handler;
+ // Store the SSL error handler.
+ nestedScrollWebView.setSslErrorHandler(handler);
+
+ // Instantiate an SSL certificate error alert dialog.
+ DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error, nestedScrollWebView.getWebViewFragmentId());
- // Display the SSL error `AlertDialog`.
- DialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error);
- sslCertificateErrorDialogFragment.show(fragmentManager, getString(R.string.ssl_certificate_error));
+ // Show the SSL certificate error dialog.
+ sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error));
}
}
});
- // Check to see if this is the first tab.
- if (tabNumber == 0) {
+ // Check to see if this is the first page.
+ if (pageNumber == 0) {
// Set this nested scroll WebView as the current WebView.
currentWebView = nestedScrollWebView;
// Load the website if not waiting for Orbot to connect.
if (!waitingForOrbot) {
- loadUrl(formattedUrlString);
+ // Get the intent that started the app.
+ Intent launchingIntent = getIntent();
+
+ // Get the information from the intent.
+ String launchingIntentAction = launchingIntent.getAction();
+ Uri launchingIntentUriData = launchingIntent.getData();
+
+ // If the intent action is a web search, perform the search.
+ if ((launchingIntentAction != null) && launchingIntentAction.equals(Intent.ACTION_WEB_SEARCH)) {
+ // Create an encoded URL string.
+ String encodedUrlString;
+
+ // Sanitize the search input and convert it to a search.
+ try {
+ encodedUrlString = URLEncoder.encode(launchingIntent.getStringExtra(SearchManager.QUERY), "UTF-8");
+ } catch (UnsupportedEncodingException exception) {
+ encodedUrlString = "";
+ }
+
+ // Load the completed search URL.
+ loadUrl(searchURL + encodedUrlString);
+ } else if (launchingIntentUriData != null){ // Check to see if the intent contains a new URL.
+ // Load the URL from the intent.
+ loadUrl(launchingIntentUriData.toString());
+ } else { // The is no URL in the intent.
+ // Select the homepage based on the proxy through Orbot status.
+ if (proxyThroughOrbot) {
+ // Load the Tor homepage.
+ loadUrl(sharedPreferences.getString("tor_homepage", getString(R.string.tor_homepage_default_value)));
+ } else {
+ // Load the normal homepage.
+ loadUrl(sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
+ }
+ }
}
}
}