import android.net.Uri;
import android.net.http.SslCertificate;
import android.net.http.SslError;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
+import androidx.core.content.res.ResourcesCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.DialogFragment;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener,
PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener,
UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener {
+ // The executor service handles background tasks. It is accessed from `ViewSourceActivity`.
+ public static ExecutorService executorService = Executors.newFixedThreadPool(4);
+
// `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxy()`.
public static String orbotStatus = "unknown";
private final int PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE = 2;
private final int PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE = 3;
+ // Define the saved instance state constants.
+ private final String SAVED_STATE_ARRAY_LIST = "saved_state_array_list";
+ private final String SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST = "saved_nested_scroll_webview_state_array_list";
+ private final String SAVED_TAB_POSITION = "saved_tab_position";
+ private final String PROXY_MODE = "proxy_mode";
+
+ // Define the saved instance state variables.
+ private ArrayList<Bundle> savedStateArrayList;
+ private ArrayList<Bundle> savedNestedScrollWebViewStateArrayList;
+ private int savedTabPosition;
+ private String savedProxyMode;
+
+ // Define the class views.
+ private AppBarLayout appBarLayout;
+ private TabLayout tabLayout;
+ private ViewPager webViewPager;
+
+ // Define the class variables.
+ @SuppressWarnings("rawtypes")
+ AsyncTask populateBlocklists;
+
// The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
// `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`.
private NestedScrollWebView currentWebView;
// Run the default commands.
super.onCreate(savedInstanceState);
+ // Check to see if the activity has been restarted.
+ if (savedInstanceState != null) {
+ // Store the saved instance state variables.
+ savedStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_STATE_ARRAY_LIST);
+ savedNestedScrollWebViewStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST);
+ savedTabPosition = savedInstanceState.getInt(SAVED_TAB_POSITION);
+ savedProxyMode = savedInstanceState.getString(PROXY_MODE);
+ }
+
// 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);
// Set the content view.
setContentView(R.layout.main_framelayout);
- // Get handles for the views that need to be modified.
+ // Get handles for the views.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+ appBarLayout = findViewById(R.id.appbar_layout);
Toolbar toolbar = findViewById(R.id.toolbar);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
+ tabLayout = findViewById(R.id.tablayout);
+ webViewPager = findViewById(R.id.webviewpager);
// Get a handle for the app compat delegate.
AppCompatDelegate appCompatDelegate = getDelegate();
// Store up to 100 tabs in memory.
webViewPager.setOffscreenPageLimit(100);
+ // Initialize the app.
+ initializeApp();
+
+ // Apply the app settings from the shared preferences.
+ applyAppSettings();
+
// Populate the blocklists.
- new PopulateBlocklists(this, this).execute();
+ populateBlocklists = new PopulateBlocklists(this, this).execute();
}
@Override
// Replace the intent that started the app with this one.
setIntent(intent);
- // Process the intent here if Privacy Browser is fully initialized. If the process has been killed by the system while sitting in the background, this will be handled in `initializeWebView()`.
- if (ultraPrivacy != null) {
+ // Check to see if the app is being restarted.
+ if (savedStateArrayList == null || savedStateArrayList.size() == 0) { // The activity is running for the first time.
// Get the information from the intent.
String intentAction = intent.getAction();
Uri intentUriData = intent.getData();
String url;
// If the intent action is a web search, perform the search.
- if (isWebSearch) {
+ if (isWebSearch) { // The intent is a web search.
// Create an encoded URL string.
String encodedUrlString;
}
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ // Run the default commands.
+ super.onSaveInstanceState(savedInstanceState);
+
+ // Create the saved state array lists.
+ ArrayList<Bundle> savedStateArrayList = new ArrayList<>();
+ ArrayList<Bundle> savedNestedScrollWebViewStateArrayList = new ArrayList<>();
+
+ // Get the URLs from each tab.
+ 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();
+
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Create saved state bundle.
+ Bundle savedStateBundle = new Bundle();
+
+ // Get the current states.
+ nestedScrollWebView.saveState(savedStateBundle);
+ Bundle savedNestedScrollWebViewStateBundle = nestedScrollWebView.saveNestedScrollWebViewState();
+
+ // Store the saved states in the array lists.
+ savedStateArrayList.add(savedStateBundle);
+ savedNestedScrollWebViewStateArrayList.add(savedNestedScrollWebViewStateBundle);
+ }
+ }
+
+ // Get the current tab position.
+ int currentTabPosition = tabLayout.getSelectedTabPosition();
+
+ // Store the saved states in the bundle.
+ savedInstanceState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList);
+ savedInstanceState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList);
+ savedInstanceState.putInt(SAVED_TAB_POSITION, currentTabPosition);
+ savedInstanceState.putString(PROXY_MODE, proxyMode);
+ }
+
@Override
public void onDestroy() {
// Unregister the orbot status broadcast receiver if it exists.
bookmarksDatabaseHelper.close();
}
+ // Stop populating the blocklists if the AsyncTask is running in the background.
+ if (populateBlocklists != null) {
+ populateBlocklists.cancel(true);
+ }
+
// Run the default commands.
super.onDestroy();
}
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the icon according to the current theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- // Set the dark stop icon.
- refreshMenuItem.setIcon(R.drawable.close_night);
- } else {
- // Set the light stop icon.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.close_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.close_night);
}
}
}
// Display a `Snackbar`.
if (currentWebView.getSettings().getJavaScriptEnabled()) { // JavaScrip is enabled.
- Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
} 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();
+ Snackbar.make(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();
+ Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
// 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();
+ Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
} 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();
+ Snackbar.make(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();
+ Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
// Display a snackbar.
if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(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();
+ Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
// Display a snackbar.
if (currentWebView.getSettings().getDomStorageEnabled()) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(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();
+ Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
}
// Reload the current WebView.
// Display a snackbar.
if (currentWebView.getSettings().getSaveFormData()) {
- Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
+ Snackbar.make(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();
+ Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
}
// Update the privacy icon. `true` runs `invalidateOptionsMenu` as the last step.
return true;
case R.id.clear_cookies:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.cookies_deleted, Snackbar.LENGTH_LONG)
+ Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
.setAction(R.string.undo, v -> {
// Do nothing because everything will be handled by `onDismissed()` below.
})
return true;
case R.id.clear_dom_storage:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
+ Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
.setAction(R.string.undo, v -> {
// Do nothing because everything will be handled by `onDismissed()` below.
})
// Form data can be remove once the minimum API >= 26.
case R.id.clear_form_data:
- Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_deleted, Snackbar.LENGTH_LONG)
+ Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG)
.setAction(R.string.undo, v -> {
// Do nothing because everything will be handled by `onDismissed()` below.
})
// Override `onBackPressed` to handle the navigation drawer and and the WebViews.
@Override
public void onBackPressed() {
- // Get a handle for the drawer layout and the tab layout.
+ // Get a handle for the drawer layout.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
- TabLayout tabLayout = findViewById(R.id.tablayout);
if (drawerLayout.isDrawerVisible(GravityCompat.START)) { // The navigation drawer is open.
// Close the navigation drawer.
}
}
- private void applyAppSettings() {
- // Initialize the app if this is the first run. This is done here instead of in `onCreate()` to shorten the time that an unthemed background is displayed on app startup.
- if (webViewDefaultUserAgent == null) {
- initializeApp();
- }
-
- // Get a handle for the shared preferences.
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
- // Store the values from the shared preferences in variables.
- incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
- boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
- sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
- sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
- sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
- proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
- fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
- hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
- scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
-
- // Get the search string.
- String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
-
- // Set the search string.
- if (searchString.equals("Custom URL")) { // A custom search string is used.
- searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
- } else { // A custom search string is not used.
- searchURL = searchString;
- }
-
- // Get a handle for the app compat delegate.
- AppCompatDelegate appCompatDelegate = getDelegate();
-
- // Get handles for the views that need to be modified.
- FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
- ActionBar actionBar = appCompatDelegate.getSupportActionBar();
- Toolbar toolbar = findViewById(R.id.toolbar);
- LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
- LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
- SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
- // Remove the incorrect lint warning below that the action bar might be null.
- assert actionBar != null;
-
- // Apply the proxy.
- applyProxy(false);
-
- // Set Do Not Track status.
- if (doNotTrackEnabled) {
- customHeaders.put("DNT", "1");
- } else {
- customHeaders.remove("DNT");
- }
-
- // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
- CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
- AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
- AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
- AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
-
- // Add the scrolling behavior to the layout parameters.
- if (scrollAppBar) {
- // Enable scrolling of the app bar.
- swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
- toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
- } else {
- // Disable scrolling of the app bar.
- swipeRefreshLayoutParams.setBehavior(null);
- toolbarLayoutParams.setScrollFlags(0);
- findOnPageLayoutParams.setScrollFlags(0);
- tabsLayoutParams.setScrollFlags(0);
-
- // Expand the app bar if it is currently collapsed.
- appBarLayout.setExpanded(true);
- }
-
- // Apply the modified layout parameters.
- swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
- toolbar.setLayoutParams(toolbarLayoutParams);
- findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
- tabsLinearLayout.setLayoutParams(tabsLayoutParams);
-
- // Set the app bar scrolling for each WebView.
- for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
- // Get the WebView tab fragment.
- WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
- // Get the fragment view.
- View fragmentView = webViewTabFragment.getView();
-
- // Only modify the WebViews if they exist.
- if (fragmentView != null) {
- // Get the nested scroll WebView from the tab fragment.
- NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
- // Set the app bar scrolling.
- nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
- }
- }
-
- // Update the full screen browsing mode settings.
- 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();
- }
-
- // Hide the banner ad in the free flavor.
- if (BuildConfig.FLAVOR.contentEquals("free")) {
- AdHelper.hideAd(findViewById(R.id.adview));
- }
-
- /* Hide the system bars.
- * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
- * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
- * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
- * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
- */
- 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 { // Privacy Browser is not in full screen browsing mode.
- // 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 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(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
- }
-
- // Remove the `SYSTEM_UI` flags from the root frame layout.
- rootFrameLayout.setSystemUiVisibility(0);
- }
- }
-
private void initializeApp() {
// 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;
- // Initialize the foreground color spans for highlighting the URLs. We have to use the deprecated `getColor()` until API >= 23.
- redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ // Initialize the gray foreground color spans for highlighting the URLs. The deprecated `getResources()` must be used until API >= 23.
initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
+ // Get the current theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the red color span according to the theme.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+ } else {
+ redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
+ }
+
// Get handles for the URL views.
EditText urlEditText = findViewById(R.id.url_edittext);
// Get handles for views that need to be modified.
DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
NavigationView navigationView = findViewById(R.id.navigationview);
- TabLayout tabLayout = findViewById(R.id.tablayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
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);
// 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 0 based.
+ // Get handles for the navigation menu and the back and forward menu items.
Menu navigationMenu = navigationView.getMenu();
- MenuItem navigationBackMenuItem = navigationMenu.getItem(2);
- MenuItem navigationForwardMenuItem = navigationMenu.getItem(3);
- MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4);
- MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+ MenuItem navigationBackMenuItem = navigationMenu.findItem(R.id.back);
+ MenuItem navigationForwardMenuItem = navigationMenu.findItem(R.id.forward);
+ MenuItem navigationHistoryMenuItem = navigationMenu.findItem(R.id.history);
+ MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
// Update the web view pager every time a tab is modified.
webViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
// Set the current WebView.
setCurrentWebView(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 or by creating a new tab.
+ // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled by creating a new tab.
if (tabLayout.getSelectedTabPosition() != position) {
// Create a handler to select the tab.
Handler selectTabHandler = new Handler();
tab.select();
};
- // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated.
+ // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated. TODO.
selectTabHandler.postDelayed(selectTabRunnable, 150);
}
}
defaultProgressViewStartOffset = swipeRefreshLayout.getProgressViewStartOffset();
defaultProgressViewEndOffset = swipeRefreshLayout.getProgressViewEndOffset();
- // Get the current theme status.
- int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
// Set the refresh color scheme according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- swipeRefreshLayout.setColorSchemeResources(R.color.blue_500);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+ } else {
+ swipeRefreshLayout.setColorSchemeResources(R.color.violet_500);
}
// Initialize a color background typed value.
bareWebView.destroy();
}
+ private void applyAppSettings() {
+ // Get a handle for the shared preferences.
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // Store the values from the shared preferences in variables.
+ incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
+ boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
+ sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
+ sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
+ sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
+ proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
+ fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
+ hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
+ scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
+
+ // Apply the saved proxy mode if the app has been restarted.
+ if (savedProxyMode != null) {
+ // Apply the saved proxy mode.
+ proxyMode = savedProxyMode;
+
+ // Reset the saved proxy mode.
+ savedProxyMode = null;
+ }
+
+ // Get the search string.
+ String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
+
+ // Set the search string.
+ if (searchString.equals("Custom URL")) { // A custom search string is used.
+ searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
+ } else { // A custom search string is not used.
+ searchURL = searchString;
+ }
+
+ // Get a handle for the app compat delegate.
+ AppCompatDelegate appCompatDelegate = getDelegate();
+
+ // Get handles for the views that need to be modified.
+ FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
+ ActionBar actionBar = appCompatDelegate.getSupportActionBar();
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+ LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
+ SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+
+ // Remove the incorrect lint warning below that the action bar might be null.
+ assert actionBar != null;
+
+ // Apply the proxy.
+ applyProxy(false);
+
+ // Set Do Not Track status.
+ if (doNotTrackEnabled) {
+ customHeaders.put("DNT", "1");
+ } else {
+ customHeaders.remove("DNT");
+ }
+
+ // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
+ CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+ AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
+ AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
+ AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
+
+ // Add the scrolling behavior to the layout parameters.
+ if (scrollAppBar) {
+ // Enable scrolling of the app bar.
+ swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+ toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+ } else {
+ // Disable scrolling of the app bar.
+ swipeRefreshLayoutParams.setBehavior(null);
+ toolbarLayoutParams.setScrollFlags(0);
+ findOnPageLayoutParams.setScrollFlags(0);
+ tabsLayoutParams.setScrollFlags(0);
+
+ // Expand the app bar if it is currently collapsed.
+ appBarLayout.setExpanded(true);
+ }
+
+ // Apply the modified layout parameters.
+ swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
+ toolbar.setLayoutParams(toolbarLayoutParams);
+ findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
+ tabsLinearLayout.setLayoutParams(tabsLayoutParams);
+
+ // Set the app bar scrolling for each WebView.
+ for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+ // Get the WebView tab fragment.
+ WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+ // Get the fragment view.
+ View fragmentView = webViewTabFragment.getView();
+
+ // Only modify the WebViews if they exist.
+ if (fragmentView != null) {
+ // Get the nested scroll WebView from the tab fragment.
+ NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+ // Set the app bar scrolling.
+ nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
+ }
+ }
+
+ // Update the full screen browsing mode settings.
+ 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();
+ }
+
+ // Hide the banner ad in the free flavor.
+ if (BuildConfig.FLAVOR.contentEquals("free")) {
+ AdHelper.hideAd(findViewById(R.id.adview));
+ }
+
+ /* Hide the system bars.
+ * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+ * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+ * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+ * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+ */
+ 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 { // Privacy Browser is not in full screen browsing mode.
+ // 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 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(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+ }
+
+ // Remove the `SYSTEM_UI` flags from the root frame layout.
+ rootFrameLayout.setSystemUiVisibility(0);
+ }
+ }
+
@Override
public void navigateHistory(String url, int steps) {
// Apply the domain settings.
// 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 corresponding tab.
TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
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));
boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
+ String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
boolean wideViewport = sharedPreferences.getBoolean("wide_viewport", true);
boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
+ // Get the WebView theme entry values string array.
+ String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
// Get a handle for the cookie manager.
CookieManager cookieManager = CookieManager.getInstance();
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.
+ // Get the pinned SSL date longs.
+ long pinnedSslStartDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE));
+ long pinnedSslEndDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE));
+
+ // Define 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) {
+ // 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 (pinnedSslStartDateLong == 0) {
pinnedSslStartDate = null;
} else {
- pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
+ pinnedSslStartDate = new Date(pinnedSslStartDateLong);
}
- // 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) {
+ // 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 (pinnedSslEndDateLong == 0) {
pinnedSslEndDate = null;
} else {
- pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+ pinnedSslEndDate = new Date(pinnedSslEndDateLong);
}
// Close the current host domain settings cursor.
// Set the WebView theme.
switch (webViewThemeInt) {
case DomainsDatabaseHelper.SYSTEM_DEFAULT:
- // // Ge the current system theme status.
- int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
- // Set the WebView theme according to the current system theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
- // Turn on the WebView dark mode.
- WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
- } else { // The system is in day mode.
+ // Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+ if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected.
// Turn off the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ } else { // The system default theme is selected.
+ // Get the current system theme status.
+ int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+ // Set the WebView theme according to the current system theme status.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
+ // Turn off the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+ }
}
break;
// Get the current theme status.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- // Set a 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 (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+ // Set a background on the URL relative layout to indicate that custom domain settings are being used.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
}
} else { // The new URL does not have custom domain settings. Load the defaults.
// Store the values from the shared preferences.
nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, sharedPreferences.getBoolean("ultralist", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true));
nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false));
- String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
// Apply the default first-party cookie setting.
cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
}
- // Get the WebView theme entry values string array.
- String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
-
// Apply the WebView theme if supported by the installed WebView.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
// Set the WebView theme. A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the WebView theme according to the current system theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
- // Turn on the WebView dark mode.
- WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
- } else { // The system is in day mode.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
// Turn off the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
}
}
// Set the loading of webpage images.
nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
- // 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));
+ // Set a transparent background on URL edit text.
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
}
// Close the domains database helper.
}
private void applyProxy(boolean reloadWebViews) {
- // Get a handle for the app bar layout.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
// Set the proxy according to the mode. `this` refers to the current activity where an alert dialog might be displayed.
ProxyHelper.setProxy(getApplicationContext(), appBarLayout, proxyMode);
case ProxyHelper.TOR:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
// Check to see if Orbot is installed.
case ProxyHelper.I2P:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
// Check to see if I2P is installed.
case ProxyHelper.CUSTOM:
// Set the app bar background to indicate proxying through Orbot is enabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- appBarLayout.setBackgroundResource(R.color.dark_blue_30);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
appBarLayout.setBackgroundResource(R.color.blue_50);
+ } else {
+ appBarLayout.setBackgroundResource(R.color.dark_blue_30);
}
break;
}
if (currentWebView.getAcceptFirstPartyCookies()) { // First-party cookies are enabled.
firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
} else { // First-party cookies are disabled.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day);
+ } else {
+ firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
}
}
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 (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_day);
+ } else {
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night);
}
} else { // JavaScript is disabled, so DOM storage is ghosted.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_day);
+ } else {
+ domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night);
}
}
// Update the refresh icon.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
}
// `invalidateOptionsMenu()` calls `onPrepareOptionsMenu()` and redraws the icons in the app bar.
ultraList = combinedBlocklists.get(4);
ultraPrivacy = combinedBlocklists.get(5);
- // Add the first tab.
- addNewTab("", true);
+ // Check to see if the activity has been restarted.
+ 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. Once the minimum API >= 24, a `forEach()` command can be used.
+ 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();
+
+ // Get the information from the intent.
+ String intentAction = intent.getAction();
+ Uri intentUriData = intent.getData();
+
+ // 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 || 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 { // The intent should contain a URL.
+ // Set the intent data as the url.
+ url = intentUriData.toString();
+ }
+
+ // Add a new tab if specified in the preferences.
+ if (sharedPreferences.getBoolean("open_intents_in_new_tab", 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) {
}
private void addNewTab(String url, boolean moveToTab) {
- // Sanitize the URL.
- url = sanitizeUrl(url);
-
- // 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 new page number. The page numbers are 0 indexed, so the new page number will match the current count.
int newTabNumber = tabLayout.getTabCount();
}
public void closeTab(View view) {
- // Get a handle for the tab layout.
- TabLayout tabLayout = findViewById(R.id.tablayout);
-
// Run the command according to the number of tabs.
if (tabLayout.getTabCount() > 1) { // There is more than one tab open.
// Close the current tab.
}
private void closeCurrentTab() {
- // Get handles for the views.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
- TabLayout tabLayout = findViewById(R.id.tablayout);
- ViewPager webViewPager = findViewById(R.id.webviewpager);
-
// Get the current tab number.
int currentTabNumber = tabLayout.getSelectedTabPosition();
// Get the current theme status.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
- // 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 (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+ // Set a green background on the URL relative layout to indicate that custom domain settings are being used.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
}
} else {
- urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+ urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
}
} else { // The fragment has not been populated. Try again in 100 milliseconds.
// Create a handler to set the current WebView.
}
@Override
- public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url) {
+ public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState) {
// Get a handle for the shared preferences.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) { // The light theme is selected.
// Turn off the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+ // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
} else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) { // The dark theme is selected.
// Turn on the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the WebView theme according to the current system theme status.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { // The system is in night mode.
- // Turn on the WebView dark mode.
- WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
- } else { // The system is in day mode.
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { // The system is in day mode.
// Turn off the WebView dark mode.
WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+ // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
+ } else { // The system is in night mode.
+ // Turn on the WebView dark mode.
+ WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
}
}
}
ActionBar actionBar = appCompatDelegate.getSupportActionBar();
LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
EditText urlEditText = findViewById(R.id.url_edittext);
- TabLayout tabLayout = findViewById(R.id.tablayout);
SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
// Remove the incorrect lint warning below that the action bar might be null.
//Stop the swipe to refresh indicator if it is running
swipeRefreshLayout.setRefreshing(false);
- }
- // If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode.
- if (progress >= 50) {
- // Make the current WebView visible.
- currentWebView.setVisibility(View.VISIBLE);
+ // Make the current WebView visible. If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+ nestedScrollWebView.setVisibility(View.VISIBLE);
}
}
// 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);
+ // Only populate the title text view if the tab view has been fully populated.
+ if (tabView != null) {
+ // Get the title text view from the tab.
+ TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
- // Set the title according to the URL.
- if (title.equals("about:blank")) {
- // Set the title to indicate a new tab.
- tabTitleTextView.setText(R.string.new_tab);
- } else {
- // Set the title as the tab text.
- tabTitleTextView.setText(title);
+ // Set the title according to the URL.
+ if (title.equals("about:blank")) {
+ // Set the title to indicate a new tab.
+ tabTitleTextView.setText(R.string.new_tab);
+ } else {
+ // Set the title as the tab text.
+ tabTitleTextView.setText(title);
+ }
}
}
}
// 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);
+ // Get a handle for the navigation requests menu item.
+ MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
// 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()));
// Get the preferences.
boolean scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
- // Get a handler for the app bar layout.
- AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
// Set the top padding of the swipe refresh layout according to the app bar scrolling preference. This can't be done in `appAppSettings()` because the app bar is not yet populated there.
if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {
// No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior.
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the stop icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.close_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.close_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.close_night);
}
}
}
int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
// Set the icon according to the theme.
- if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
- refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
- } else {
+ if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
+ } else {
+ refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
}
}
}
}
});
- // Check to see if this is the first page.
- if (pageNumber == 0) {
+ // Check to see if the state is being restored.
+ if (restoringState) { // The state is being restored.
+ // Resume the nested scroll WebView JavaScript timers.
+ nestedScrollWebView.resumeTimers();
+ } else if (pageNumber == 0) { // The first page is being loaded.
// Set this nested scroll WebView as the current WebView.
currentWebView = nestedScrollWebView;
- // Apply the app settings from the shared preferences.
- applyAppSettings();
-
// Initialize the URL to load string.
String urlToLoadString;
} else if (launchingIntentUriData != null){ // The intent contains a URL.
// Store the URL.
urlToLoadString = launchingIntentUriData.toString();
+ } else if (!url.equals("")) { // The activity has been restarted.
+ // Load the saved URL.
+ urlToLoadString = url;
} else { // The is no URL in the intent.
// Store the homepage to be loaded.
urlToLoadString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value));
loadUrl(nestedScrollWebView, urlToLoadString);
}
} else { // This is not the first tab.
- // Apply the domain settings.
- applyDomainSettings(nestedScrollWebView, url, false, false);
-
// Load the URL.
- nestedScrollWebView.loadUrl(url, customHeaders);
+ loadUrl(nestedScrollWebView, url);
// Set the focus and display the keyboard if the URL is blank.
if (url.equals("")) {