+ case ProxyHelper.I2P:
+ // Set the app bar background to indicate proxying through Orbot is enabled.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
+ }
+ // Get the package manager.
+ PackageManager packageManager = getPackageManager();
+
+ // Check to see if I2P is installed.
+ try {
+ // Check to see if the F-Droid flavor is installed. This will throw an error and drop to the catch section if it isn't installed.
+ packageManager.getPackageInfo("net.i2p.android.router", 0);
+ } catch (PackageManager.NameNotFoundException fdroidException) { // The F-Droid flavor is not installed.
+ try {
+ // Check to see if the Google Play flavor is installed. This will throw an error and drop to the catch section if it isn't installed.
+ packageManager.getPackageInfo("net.i2p.android", 0);
+ } catch (PackageManager.NameNotFoundException googlePlayException) { // The Google Play flavor is not installed.
+ // Sow the I2P not installed dialog if it is not already displayed.
+ if (getSupportFragmentManager().findFragmentByTag(getString(R.string.proxy_not_installed_dialog)) == null) {
+ // Get a handle for the waiting for proxy alert dialog.
+ DialogFragment i2pNotInstalledDialogFragment = ProxyNotInstalledDialog.displayDialog(proxyMode);
+
+ // Try to show the dialog. Sometimes the window is not yet active if returning from Settings.
+ try {
+ // Display the I2P not installed alert dialog.
+ i2pNotInstalledDialogFragment.show(getSupportFragmentManager(), getString(R.string.proxy_not_installed_dialog));
+ } catch (Exception i2pNotInstalledException) {
+ // Add the dialog to the pending dialog array list. It will be displayed in `onStart()`.
+ pendingDialogsArrayList.add(new PendingDialogDataClass(i2pNotInstalledDialogFragment, getString(R.string.proxy_not_installed_dialog)));
+ }
+ }
+ }
+ }
+ break;
+
+ case ProxyHelper.CUSTOM:
+ // Set the app bar background to indicate proxying through Orbot is enabled.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
+ }
+ break;
+ }
+
+ // Reload the WebViews if requested and not waiting for the proxy.
+ if (reloadWebViews && !waitingForProxy) {
+ // Reload the WebViews.
+ 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 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);
+
+ // Reload the WebView.
+ nestedScrollWebView.reload();
+ }
+ }
+ }
+ }
+
+ private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) {
+ // Only update the privacy icons if the options menu and the current WebView have already been populated.
+ if ((optionsMenu != null) && (currentWebView != null)) {
+ // Update the privacy icon.
+ if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScript is enabled.
+ optionsPrivacyMenuItem.setIcon(R.drawable.javascript_enabled);
+ } else if (currentWebView.getAcceptCookies()) { // JavaScript is disabled but cookies are enabled.
+ optionsPrivacyMenuItem.setIcon(R.drawable.warning);
+ } else { // All the dangerous features are disabled.
+ optionsPrivacyMenuItem.setIcon(R.drawable.privacy_mode);
+ }
+
+ // Update the cookies icon.
+ if (currentWebView.getAcceptCookies()) {
+ optionsCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
+ } else {
+ optionsCookiesMenuItem.setIcon(R.drawable.cookies_disabled);
+ }
+
+ // Update the refresh icon.
+ if (optionsRefreshMenuItem.getTitle() == getString(R.string.refresh)) { // The refresh icon is displayed.
+ // Set the icon. Once the minimum API is >= 26, the blue and black icons can be combined with a tint list.
+ optionsRefreshMenuItem.setIcon(R.drawable.refresh_enabled);
+ } else { // The stop icon is displayed.
+ // Set the icon. Once the minimum API is >= 26, the blue and black icons can be combined with a tint list.
+ optionsRefreshMenuItem.setIcon(R.drawable.close_blue);
+ }
+
+ // `invalidateOptionsMenu()` calls `onPrepareOptionsMenu()` and redraws the icons in the app bar.
+ if (runInvalidateOptionsMenu) {
+ invalidateOptionsMenu();
+ }
+ }
+ }
+
+ private void loadBookmarksFolder() {
+ // Update the bookmarks cursor with the contents of the bookmarks database for the current folder.
+ bookmarksCursor = bookmarksDatabaseHelper.getBookmarksByDisplayOrder(currentBookmarksFolder);
+
+ // Populate the bookmarks cursor adapter.
+ bookmarksCursorAdapter = new CursorAdapter(this, bookmarksCursor, false) {
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ // Inflate the individual item layout.
+ return getLayoutInflater().inflate(R.layout.bookmarks_drawer_item_linearlayout, parent, false);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ // Get handles for the views.
+ ImageView bookmarkFavoriteIcon = view.findViewById(R.id.bookmark_favorite_icon);
+ TextView bookmarkNameTextView = view.findViewById(R.id.bookmark_name);
+
+ // Get the favorite icon byte array from the cursor.
+ byte[] favoriteIconByteArray = cursor.getBlob(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.FAVORITE_ICON));
+
+ // Convert the byte array to a `Bitmap` beginning at the first byte and ending at the last.
+ Bitmap favoriteIconBitmap = BitmapFactory.decodeByteArray(favoriteIconByteArray, 0, favoriteIconByteArray.length);
+
+ // Display the bitmap in `bookmarkFavoriteIcon`.
+ bookmarkFavoriteIcon.setImageBitmap(favoriteIconBitmap);
+
+ // Get the bookmark name from the cursor and display it in `bookmarkNameTextView`.
+ String bookmarkNameString = cursor.getString(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME));
+ bookmarkNameTextView.setText(bookmarkNameString);
+
+ // Make the font bold for folders.
+ if (cursor.getInt(cursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1) {
+ bookmarkNameTextView.setTypeface(Typeface.DEFAULT_BOLD);
+ } else { // Reset the font to default for normal bookmarks.
+ bookmarkNameTextView.setTypeface(Typeface.DEFAULT);
+ }
+ }
+ };
+
+ // 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);
+ } else {
+ bookmarksTitleTextView.setText(currentBookmarksFolder);
+ }
+ }
+
+ private void openWithApp(String url) {
+ // Create an open with app intent with `ACTION_VIEW`.
+ Intent openWithAppIntent = new Intent(Intent.ACTION_VIEW);
+
+ // Set the URI but not the MIME type. This should open all available apps.
+ openWithAppIntent.setData(Uri.parse(url));
+
+ // Flag the intent to open in a new task.
+ openWithAppIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // Try the intent.
+ try {
+ // Show the chooser.
+ startActivity(openWithAppIntent);
+ } catch (ActivityNotFoundException exception) { // There are no apps available to open the URL.
+ // Show a snackbar with the error.
+ Snackbar.make(currentWebView, getString(R.string.error) + exception, Snackbar.LENGTH_INDEFINITE).show();
+ }
+ }
+
+ private void openWithBrowser(String url) {
+ // Create an open with browser intent with `ACTION_VIEW`.
+ Intent openWithBrowserIntent = new Intent(Intent.ACTION_VIEW);
+
+ // Set the URI and the MIME type. `"text/html"` should load browser options.
+ openWithBrowserIntent.setDataAndType(Uri.parse(url), "text/html");
+
+ // Flag the intent to open in a new task.
+ openWithBrowserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // Try the intent.
+ try {
+ // Show the chooser.
+ startActivity(openWithBrowserIntent);
+ } catch (ActivityNotFoundException exception) { // There are no browsers available to open the URL.
+ // Show a snackbar with the error.
+ Snackbar.make(currentWebView, getString(R.string.error) + exception, Snackbar.LENGTH_INDEFINITE).show();
+ }
+ }
+
+ private String sanitizeUrl(String url) {
+ // Sanitize tracking queries.
+ if (sanitizeTrackingQueries)
+ url = SanitizeUrlHelper.sanitizeTrackingQueries(url);
+
+ // Sanitize AMP redirects.
+ if (sanitizeAmpRedirects)
+ url = SanitizeUrlHelper.sanitizeAmpRedirects(url);
+
+ // Return the sanitized URL.
+ return url;
+ }
+
+ public void finishedPopulatingBlocklists(ArrayList<ArrayList<List<String[]>>> combinedBlocklists) {
+ // Store the blocklists.
+ easyList = combinedBlocklists.get(0);
+ easyPrivacy = combinedBlocklists.get(1);
+ fanboysAnnoyanceList = combinedBlocklists.get(2);
+ fanboysSocialList = combinedBlocklists.get(3);
+ ultraList = combinedBlocklists.get(4);
+ ultraPrivacy = combinedBlocklists.get(5);
+
+ // Check to see if the activity has been restarted with a saved state.
+ if ((savedStateArrayList == null) || (savedStateArrayList.size() == 0)) { // The activity has not been restarted or it was restarted on start to force the night theme.
+ // Add the first tab.
+ addNewTab("", true);
+ } else { // The activity has been restarted.
+ // Restore each tab.
+ for (int i = 0; i < savedStateArrayList.size(); i++) {
+ // Add a new tab.
+ tabLayout.addTab(tabLayout.newTab());
+
+ // Get the new tab.
+ TabLayout.Tab newTab = tabLayout.getTabAt(i);
+
+ // Remove the lint warning below that the current tab might be null.
+ assert newTab != null;
+
+ // Set a custom view on the new tab.
+ newTab.setCustomView(R.layout.tab_custom_view);
+
+ // Add the new page.
+ webViewPagerAdapter.restorePage(savedStateArrayList.get(i), savedNestedScrollWebViewStateArrayList.get(i));
+ }
+
+ // Reset the saved state variables.
+ savedStateArrayList = null;
+ savedNestedScrollWebViewStateArrayList = null;
+
+ // Restore the selected tab position.
+ if (savedTabPosition == 0) { // The first tab is selected.
+ // Set the first page as the current WebView.
+ setCurrentWebView(0);
+ } else { // The first tab is not selected.
+ // Move to the selected tab.
+ webViewPager.setCurrentItem(savedTabPosition);
+ }
+
+ // Get the intent that started the app.
+ Intent intent = getIntent();
+
+ // Reset the intent. This prevents a duplicate tab from being created on restart.
+ setIntent(new Intent());
+
+ // Get the information from the intent.
+ String intentAction = intent.getAction();
+ Uri intentUriData = intent.getData();
+ String intentStringExtra = intent.getStringExtra(Intent.EXTRA_TEXT);
+
+ // Determine if this is a web search.
+ boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
+
+ // Only process the URI if it contains data or it is a web search. If the user pressed the desktop icon after the app was already running the URI will be null.
+ if (intentUriData != null || intentStringExtra != null || isWebSearch) {
+ // Get the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Create a URL string.
+ String url;
+
+ // If the intent action is a web search, perform the search.
+ if (isWebSearch) { // The intent is a 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 if (intentUriData != null) { // The intent contains a URL formatted as a URI.
+ // Set the intent data as the URL.
+ url = intentUriData.toString();
+ } else { // The intent contains a string, which might be a URL.
+ // Set the intent string as the URL.
+ url = intentStringExtra;
+ }
+
+ // Add a new tab if specified in the preferences.
+ if (sharedPreferences.getBoolean(getString(R.string.open_intents_in_new_tab_key), true)) { // Load the URL in a new tab.
+ // Set the loading new intent flag.
+ loadingNewIntent = true;
+
+ // Add a new tab.
+ addNewTab(url, true);
+ } else { // Load the URL in the current tab.
+ // Make it so.
+ loadUrl(currentWebView, url);
+ }
+ }
+ }
+ }
+
+ public void addTab(View view) {
+ // Add a new tab with a blank URL.
+ addNewTab("", true);
+ }
+
+ private void addNewTab(String url, boolean moveToTab) {
+ // Clear the focus from the URL edit text, so that it will be populated with the information from the new tab.
+ urlEditText.clearFocus();
+
+ // 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();
+
+ // Add a new tab.
+ tabLayout.addTab(tabLayout.newTab());