X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FDomainsActivity.java;h=2ef534f1d4286fb54557e8926efcf3b0bf648c82;hp=6baf5f6e534df8c4cf7ac969753abeb500c25b0e;hb=1b27ac6f2b7c046945fc97e2aff9adbde8a152ce;hpb=0488649384ddea89d768c1fc1cc5fb71f8af6528 diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java index 6baf5f6e..2ef534f1 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java @@ -1,20 +1,20 @@ /* - * Copyright © 2017-2020 Soren Stoutner . + * Copyright © 2017-2022 Soren Stoutner . * - * This file is part of Privacy Browser . + * This file is part of Privacy Browser Android . * - * Privacy Browser is free software: you can redistribute it and/or modify + * Privacy Browser Android is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * Privacy Browser is distributed in the hope that it will be useful, + * Privacy Browser Android is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with Privacy Browser. If not, see . + * along with Privacy Browser Android. If not, see . */ package com.stoutner.privacybrowser.activities; @@ -23,7 +23,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.os.Bundle; @@ -38,17 +37,18 @@ import android.widget.CursorAdapter; import android.widget.EditText; import android.widget.ListView; import android.widget.RadioButton; +import android.widget.ScrollView; import android.widget.Spinner; -import android.widget.Switch; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; // The AndroidX toolbar must be used until the minimum API is >= 21. +import androidx.appcompat.widget.SwitchCompat; +import androidx.appcompat.widget.Toolbar; import androidx.core.app.NavUtils; import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.FragmentManager; // The AndroidX dialog fragment must be used or an error is produced on API <=22. +import androidx.fragment.app.FragmentManager; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; @@ -62,16 +62,11 @@ import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; import java.util.Objects; public class DomainsActivity extends AppCompatActivity implements AddDomainDialog.AddDomainListener, DomainsListFragment.DismissSnackbarInterface { - // `twoPanedMode` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onCreate()`, `onCreateOptionsMenu()`, and `populateDomainsListView()`. + // Define the public static variables. + public static int domainsListViewPosition; public static boolean twoPanedMode; - - // `databaseId` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onCreateOptionsMenu()`, `saveDomainSettings()` and `populateDomainsListView()`. public static int currentDomainDatabaseId; - - // `deleteMenuItem` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `onBackPressed()`. public static MenuItem deleteMenuItem; - - // `dismissingSnackbar` is public static so it can be accessed from `DomainsListFragment`. It is also used in `onOptionsItemSelected()`. public static boolean dismissingSnackbar; // The SSL certificate and IP address information are accessed from `DomainSettingsFragment` and `saveDomainSettings()`. @@ -86,6 +81,21 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo public static String currentIpAddresses; + // Initialize the class constants. + private final String LISTVIEW_POSITION = "listview_position"; + private final String DOMAIN_SETTINGS_DISPLAYED = "domain_settings_displayed"; + private final String DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_is"; + private final String DOMAIN_SETTINGS_SCROLL_Y = "domain_settings_scroll_y"; + + // Initialize the class variables. + private boolean restartAfterRotate; + private boolean domainSettingsDisplayedBeforeRotate; + private int domainSettingsDatabaseIdBeforeRotate; + private int domainSettingsScrollY = 0; + + // Defile the class views. + private ListView domainsListView; + // `closeActivityAfterDismissingSnackbar` is used in `onOptionsItemSelected()`, and `onBackPressed()`. private boolean closeActivityAfterDismissingSnackbar; @@ -95,24 +105,12 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // `domainsDatabaseHelper` is used in `onCreate()`, `saveDomainSettings()`, and `onDestroy()`. private static DomainsDatabaseHelper domainsDatabaseHelper; - // `domainsListView` is used in `onCreate()` and `populateDomainsList()`. - private ListView domainsListView; - // `addDomainFAB` is used in `onCreate()`, `onCreateOptionsMenu()`, `onOptionsItemSelected()`, and `onBackPressed()`. private FloatingActionButton addDomainFAB; // `deletedDomainPosition` is used in an inner and outer class in `onOptionsItemSelected()`. private int deletedDomainPosition; - // `restartAfterRotate` is used in `onCreate()` and `onCreateOptionsMenu()`. - private boolean restartAfterRotate; - - // `domainSettingsDisplayedBeforeRotate` is used in `onCreate()` and `onCreateOptionsMenu()`. - private boolean domainSettingsDisplayedBeforeRotate; - - // `domainSettingsDatabaseIdBeforeRotate` is used in `onCreate()` and `onCreateOptionsMenu()`. - private int domainSettingsDatabaseIdBeforeRotate; - // `goDirectlyToDatabaseId` is used in `onCreate()` and `onCreateOptionsMenu()`. private int goDirectlyToDatabaseId; @@ -130,8 +128,9 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Get a handle for the shared preferences. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - // Get the screenshot preference. - boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false); + // Get the preferences. + boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false); + boolean bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false); // Disable screenshots if not allowed. if (!allowScreenshots) { @@ -144,11 +143,16 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Run the default commands. super.onCreate(savedInstanceState); - // Extract the values from `savedInstanceState` if it is not `null`. + // Initialize the domains listview position. + domainsListViewPosition = 0; + + // Extract the values from the saved instance state if it is not null. if (savedInstanceState != null) { + domainsListViewPosition = savedInstanceState.getInt(LISTVIEW_POSITION); restartAfterRotate = true; - domainSettingsDisplayedBeforeRotate = savedInstanceState.getBoolean("domain_settings_displayed"); - domainSettingsDatabaseIdBeforeRotate = savedInstanceState.getInt("domain_settings_database_id"); + domainSettingsDisplayedBeforeRotate = savedInstanceState.getBoolean(DOMAIN_SETTINGS_DISPLAYED); + domainSettingsDatabaseIdBeforeRotate = savedInstanceState.getInt(DOMAIN_SETTINGS_DATABASE_ID); + domainSettingsScrollY = savedInstanceState.getInt(DOMAIN_SETTINGS_SCROLL_Y); } // Get the launching intent @@ -174,8 +178,12 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo sslEndDateLong = intent.getLongExtra("ssl_end_date", 0); currentIpAddresses = intent.getStringExtra("current_ip_addresses"); - // Set the content view. - setContentView(R.layout.domains_coordinatorlayout); + // Set the view. + if (bottomAppBar) { + setContentView(R.layout.domains_bottom_appbar); + } else { + setContentView(R.layout.domains_top_appbar); + } // Populate the class variables. coordinatorLayout = findViewById(R.id.domains_coordinatorlayout); @@ -242,7 +250,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo fragmentManager.executePendingTransactions(); // Populate the list of domains. `domainSettingsDatabaseId` highlights the domain that was highlighted before the rotation. - populateDomainsListView(domainSettingsDatabaseIdBeforeRotate); + populateDomainsListView(domainSettingsDatabaseIdBeforeRotate, domainsListViewPosition); } else { // The device is in single-paned mode. // Reset `restartAfterRotate`. restartAfterRotate = false; @@ -250,21 +258,26 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Store the current domain database ID. currentDomainDatabaseId = domainSettingsDatabaseIdBeforeRotate; - // Add `currentDomainDatabaseId` to `argumentsBundle`. + // Create an arguments bundle. Bundle argumentsBundle = new Bundle(); + + // Add the domain settings arguments. argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId); + argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY); - // Add `argumentsBundle` to `domainSettingsFragment`. + // Instantiate a new domain settings fragment. DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment(); + + // Add the arguments bundle to the domain settings fragment. domainSettingsFragment.setArguments(argumentsBundle); - // Show `deleteMenuItem`. + // Show the delete menu item. deleteMenuItem.setVisible(true); // Hide the add domain floating action button. addDomainFAB.hide(); - // Display `domainSettingsFragment`. + // Display the domain settings fragment. fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit(); } } else { // The device was not rotated or, if it was, domain settings were not displayed previously. @@ -279,23 +292,28 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo fragmentManager.executePendingTransactions(); // Populate the list of domains. `domainSettingsDatabaseId` highlights the domain that was highlighted before the rotation. - populateDomainsListView(goDirectlyToDatabaseId); + populateDomainsListView(goDirectlyToDatabaseId, domainsListViewPosition); } else { // The device is in single-paned mode. - // Add the domain ID to be loaded to `argumentsBundle`. + // Create an arguments bundle. Bundle argumentsBundle = new Bundle(); + + // Add the domain settings to arguments bundle. argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, goDirectlyToDatabaseId); + argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY); - // Add `argumentsBundle` to `domainSettingsFragment`. + // Instantiate a new domain settings fragment. DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment(); + + // Add the arguments bundle to the domain settings fragment`. domainSettingsFragment.setArguments(argumentsBundle); - // Show `deleteMenuItem`. + // Show the delete menu item. deleteMenuItem.setVisible(true); // Hide the add domain floating action button. addDomainFAB.hide(); - // Display `domainSettingsFragment`. + // Display the domain settings fragment. fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit(); } } else { // Highlight the first domain. @@ -305,7 +323,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo fragmentManager.executePendingTransactions(); // Populate the list of domains. `-1` highlights the first domain. - populateDomainsListView(-1); + populateDomainsListView(-1, domainsListViewPosition); } } @@ -316,88 +334,98 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo @Override public boolean onOptionsItemSelected(MenuItem menuItem) { // Get the ID of the menu item that was selected. - int menuItemID = menuItem.getItemId(); + int menuItemId = menuItem.getItemId(); // Get a handle for the fragment manager. FragmentManager fragmentManager = getSupportFragmentManager(); - switch (menuItemID) { - case android.R.id.home: // The home arrow is identified as `android.R.id.home`, not just `R.id.home`. - if (twoPanedMode) { // The device is in two-paned mode. - // Save the current domain settings if the domain settings fragment is displayed. - if (findViewById(R.id.domain_settings_scrollview) != null) { - saveDomainSettings(coordinatorLayout, resources); - } - - // Dismiss the undo delete `SnackBar` if it is shown. - if (undoDeleteSnackbar != null && undoDeleteSnackbar.isShown()) { - // Set the close flag. - closeActivityAfterDismissingSnackbar = true; - - // Dismiss the snackbar. - undoDeleteSnackbar.dismiss(); - } else { - // Go home. - NavUtils.navigateUpFromSameTask(this); - } - } else if (closeOnBack) { // Go directly back to the main WebView activity because the domains activity was launched from the options menu. - // Save the current domain settings. + // Run the command according to the selected menu item. + if (menuItemId == android.R.id.home) { // The home arrow is identified as `android.R.id.home`, not just `R.id.home`. + // Check if the device is in two-paned mode. + if (twoPanedMode) { // The device is in two-paned mode. + // Save the current domain settings if the domain settings fragment is displayed. + if (findViewById(R.id.domain_settings_scrollview) != null) { saveDomainSettings(coordinatorLayout, resources); + } + + // Dismiss the undo delete snackbar if it is shown. + if (undoDeleteSnackbar != null && undoDeleteSnackbar.isShown()) { + // Set the close flag. + closeActivityAfterDismissingSnackbar = true; + // Dismiss the snackbar. + undoDeleteSnackbar.dismiss(); + } else { // Go home. NavUtils.navigateUpFromSameTask(this); - } else if (findViewById(R.id.domain_settings_scrollview) != null) { // The device is in single-paned mode and the domain settings fragment is displayed. - // Save the current domain settings. - saveDomainSettings(coordinatorLayout, resources); + } + } else if (closeOnBack) { // Go directly back to the main WebView activity because the domains activity was launched from the options menu. + // Save the current domain settings. + saveDomainSettings(coordinatorLayout, resources); - // Display the domains list fragment. - DomainsListFragment domainsListFragment = new DomainsListFragment(); - fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit(); - fragmentManager.executePendingTransactions(); + // Go home. + NavUtils.navigateUpFromSameTask(this); + } else if (findViewById(R.id.domain_settings_scrollview) != null) { // The device is in single-paned mode and the domain settings fragment is displayed. + // Save the current domain settings. + saveDomainSettings(coordinatorLayout, resources); - // Populate the list of domains. `-1` highlights the first domain if in two-paned mode. It has no effect in single-paned mode. - populateDomainsListView(-1); + // Display the domains list fragment. + DomainsListFragment domainsListFragment = new DomainsListFragment(); + fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit(); + fragmentManager.executePendingTransactions(); - // Show the add domain floating action button. - addDomainFAB.show(); + // Populate the list of domains. `-1` highlights the first domain if in two-paned mode. It has no effect in single-paned mode. + populateDomainsListView(-1, domainsListViewPosition); - // Hide the delete menu item. - deleteMenuItem.setVisible(false); - } else { // The device is in single-paned mode and `DomainsListFragment` is displayed. - // Dismiss the undo delete `SnackBar` if it is shown. - if (undoDeleteSnackbar != null && undoDeleteSnackbar.isShown()) { - // Set the close flag. - closeActivityAfterDismissingSnackbar = true; - - // Dismiss the snackbar. - undoDeleteSnackbar.dismiss(); - } else { - // Go home. - NavUtils.navigateUpFromSameTask(this); - } - } - break; + // Show the add domain floating action button. + addDomainFAB.show(); + + // Hide the delete menu item. + deleteMenuItem.setVisible(false); + } else { // The device is in single-paned mode and domains list fragment is displayed. + // Dismiss the undo delete `SnackBar` if it is shown. + if (undoDeleteSnackbar != null && undoDeleteSnackbar.isShown()) { + // Set the close flag. + closeActivityAfterDismissingSnackbar = true; - case R.id.delete_domain: + // Dismiss the snackbar. + undoDeleteSnackbar.dismiss(); + } else { + // Go home. + NavUtils.navigateUpFromSameTask(this); + } + } + } else if (menuItemId == R.id.delete_domain) { // Delete. + // Get a handle for the activity. + Activity activity = this; + + // Check to see if the domain settings were loaded directly for editing of this app in single-paned mode. + if (closeOnBack && !twoPanedMode) { // The activity should delete the domain settings and exit straight to the the main WebView activity. + // Delete the selected domain. + domainsDatabaseHelper.deleteDomain(currentDomainDatabaseId); + + // Go home. + NavUtils.navigateUpFromSameTask(activity); + } else { // A snackbar should be shown before deleting the domain settings. // Reset close-on-back, which otherwise can cause errors if the system attempts to save settings for a domain that no longer exists. closeOnBack = false; - // Store a copy of `currentDomainDatabaseId` because it could change while the `Snackbar` is displayed. + // Store a copy of the current domain database ID because it could change while the snackbar is displayed. final int databaseIdToDelete = currentDomainDatabaseId; // Update the fragments and menu items. if (twoPanedMode) { // Two-paned mode. - // Store the deleted domain position, which is needed if `Undo` is selected in the `Snackbar`. + // Store the deleted domain position, which is needed if undo is selected in the snackbar. deletedDomainPosition = domainsListView.getCheckedItemPosition(); - // Disable the options `MenuItems`. + // Disable the options menu items. deleteMenuItem.setEnabled(false); deleteMenuItem.setIcon(R.drawable.delete_disabled); // Remove the domain settings fragment. fragmentManager.beginTransaction().remove(Objects.requireNonNull(fragmentManager.findFragmentById(R.id.domain_settings_fragment_container))).commit(); } else { // Single-paned mode. - // Display `DomainsListFragment`. + // Display the domains list fragment. DomainsListFragment domainsListFragment = new DomainsListFragment(); fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit(); fragmentManager.executePendingTransactions(); @@ -409,35 +437,36 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo deleteMenuItem.setVisible(false); } - // Get a `Cursor` that does not show the domain to be deleted. + // Get a cursor that does not show the domain to be deleted. Cursor domainsPendingDeleteCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomainExcept(databaseIdToDelete); - // Setup `domainsPendingDeleteCursorAdapter` with `this` context. `false` disables `autoRequery`. + // Setup the domains pending delete cursor adapter. CursorAdapter domainsPendingDeleteCursorAdapter = new CursorAdapter(this, domainsPendingDeleteCursor, false) { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - // Inflate the individual item layout. `false` does not attach it to the root. + // Inflate the individual item layout. return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { - // Set the domain name. - String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME)); + // Get the domain name string. + String domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)); + + // Get a handle for the domain name text view. TextView domainNameTextView = view.findViewById(R.id.domain_name_textview); + + // Display the domain name. domainNameTextView.setText(domainNameString); } }; - // Update the handle for the current `domains_listview`. + // Update the handle for the current domains list view. domainsListView = findViewById(R.id.domains_listview); - // Update the `ListView`. + // Update the list view. domainsListView.setAdapter(domainsPendingDeleteCursorAdapter); - // Get a handle for the activity. - Activity activity = this; - // Display a snackbar. undoDeleteSnackbar = Snackbar.make(domainsListView, R.string.domain_deleted, Snackbar.LENGTH_LONG) .setAction(R.string.undo, (View v) -> { @@ -448,32 +477,41 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo public void onDismissed(Snackbar snackbar, int event) { // Run commands based on the event. if (event == Snackbar.Callback.DISMISS_EVENT_ACTION) { // The user pushed the `Undo` button. - // Store the database ID in arguments bundle. + // Create an arguments bundle. Bundle argumentsBundle = new Bundle(); + + // Store the domains settings in the arguments bundle. argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseIdToDelete); + argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY); - // Add the arguments bundle to the domain settings fragment. + // Instantiate a new domain settings fragment. DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment(); + + // Add the arguments bundle to the domain settings fragment. domainSettingsFragment.setArguments(argumentsBundle); // Display the correct fragments. if (twoPanedMode) { // The device in in two-paned mode. - // Get a `Cursor` with the current contents of the domains database. + // Get a cursor with the current contents of the domains database. Cursor undoDeleteDomainsCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomain(); - // Setup `domainsCursorAdapter` with `this` context. `false` disables `autoRequery`. + // Setup the domains cursor adapter. CursorAdapter undoDeleteDomainsCursorAdapter = new CursorAdapter(getApplicationContext(), undoDeleteDomainsCursor, false) { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - // Inflate the individual item layout. `false` does not attach it to the root. + // Inflate the individual item layout. return getLayoutInflater().inflate(R.layout.domain_name_linearlayout, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { - // Set the domain name. - String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME)); + /// Get the domain name string. + String domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)); + + // Get a handle for the domain name text view. TextView domainNameTextView = view.findViewById(R.id.domain_name_textview); + + // Display the domain name. domainNameTextView.setText(domainNameString); } }; @@ -490,26 +528,19 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Enable the options delete menu item. deleteMenuItem.setEnabled(true); - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set the delete menu item icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - deleteMenuItem.setIcon(R.drawable.delete_night); - } else { - deleteMenuItem.setIcon(R.drawable.delete_day); - } + // Set the delete menu item icon. + deleteMenuItem.setIcon(R.drawable.delete_enabled); } else { // The device in in one-paned mode. - // Display `domainSettingsFragment`. + // Display the domain settings fragment. fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit(); // Hide the add domain floating action button. addDomainFAB.hide(); - // Show and enable `deleteMenuItem`. + // Show and enable the delete menu item. deleteMenuItem.setVisible(true); - // Display `domainSettingsFragment`. + // Display the domain settings fragment. fragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit(); } } else { // The snackbar was dismissed without the undo button being pushed. @@ -525,15 +556,8 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Enable the delete menu item. deleteMenuItem.setEnabled(true); - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set the delete menu item icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - deleteMenuItem.setIcon(R.drawable.delete_night); - } else { - deleteMenuItem.setIcon(R.drawable.delete_day); - } + // Set the delete menu item icon. + deleteMenuItem.setIcon(R.drawable.delete_enabled); } else { // Single-paned mode. // Show the delete menu item. deleteMenuItem.setVisible(true); @@ -559,7 +583,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Show the Snackbar. undoDeleteSnackbar.show(); - break; + } } // Consume the event. @@ -567,21 +591,40 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo } @Override - protected void onSaveInstanceState(@NonNull Bundle outState) { - // Store the current `DomainSettingsFragment` state in `outState`. - if (findViewById(R.id.domain_settings_scrollview) != null) { // `DomainSettingsFragment` is displayed. + protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) { + // Run the default commands. + super.onSaveInstanceState(savedInstanceState); + + // Get a handle for the domain settings scrollview. + ScrollView domainSettingsScrollView = findViewById(R.id.domain_settings_scrollview); + + // Check to see if the domain settings scrollview exists. + if (domainSettingsScrollView == null) { // The domain settings are not displayed. + // Store the domain settings status in the bundle. + savedInstanceState.putBoolean(DOMAIN_SETTINGS_DISPLAYED, false); + savedInstanceState.putInt(DOMAIN_SETTINGS_DATABASE_ID, -1); + savedInstanceState.putInt(DOMAIN_SETTINGS_SCROLL_Y, 0); + } else { // The domain settings are displayed. // Save any changes that have been made to the domain settings. saveDomainSettings(coordinatorLayout, resources); - // Store `DomainSettingsDisplayed`. - outState.putBoolean("domain_settings_displayed", true); - outState.putInt("domain_settings_database_id", DomainSettingsFragment.databaseId); - } else { // `DomainSettingsFragment` is not displayed. - outState.putBoolean("domain_settings_displayed", false); - outState.putInt("domain_settings_database_id", -1); + // Get the domain settings scroll Y. + int domainSettingsScrollY = domainSettingsScrollView.getScrollY(); + + // Store the domain settings status in the bundle. + savedInstanceState.putBoolean(DOMAIN_SETTINGS_DISPLAYED, true); + savedInstanceState.putInt(DOMAIN_SETTINGS_DATABASE_ID, DomainSettingsFragment.databaseId); + savedInstanceState.putInt(DOMAIN_SETTINGS_SCROLL_Y, domainSettingsScrollY); } - super.onSaveInstanceState(outState); + // Check to see if the domains listview exists. + if (domainsListView != null) { + // Get the domains listview position. + int domainsListViewPosition = domainsListView.getFirstVisiblePosition(); + + // Store the listview position in the bundle. + savedInstanceState.putInt(LISTVIEW_POSITION, domainsListViewPosition); + } } // Control what the navigation bar back button does. @@ -623,7 +666,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo fragmentManager.executePendingTransactions(); // Populate the list of domains. `-1` highlights the first domain if in two-paned mode. It has no effect in single-paned mode. - populateDomainsListView(-1); + populateDomainsListView(-1, domainsListViewPosition); // Show the add domain floating action button. addDomainFAB.show(); @@ -666,20 +709,25 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Display the newly created domain. if (twoPanedMode) { // The device in in two-paned mode. - populateDomainsListView(currentDomainDatabaseId); + populateDomainsListView(currentDomainDatabaseId, 0); } else { // The device is in single-paned mode. // Hide the add domain floating action button. addDomainFAB.hide(); - // Show and enable `deleteMenuItem`. + // Show and enable the delete menu item. DomainsActivity.deleteMenuItem.setVisible(true); - // Add the current domain database ID to the arguments bundle. + // Create an arguments bundle. Bundle argumentsBundle = new Bundle(); + + // Add the domain settings to the arguments bundle. The scroll Y should always be `0` on a new domain. argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId); + argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, 0); - // Add and arguments bundle to the domain setting fragment. + // Instantiate a new domain settings fragment. DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment(); + + // Add the arguments bundle to the domain setting fragment. domainSettingsFragment.setArguments(argumentsBundle); // Display the domain settings fragment. @@ -690,18 +738,17 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo public void saveDomainSettings(View view, Resources resources) { // Get handles for the domain settings. EditText domainNameEditText = view.findViewById(R.id.domain_settings_name_edittext); - Switch javaScriptSwitch = view.findViewById(R.id.javascript_switch); - Switch firstPartyCookiesSwitch = view.findViewById(R.id.first_party_cookies_switch); - Switch thirdPartyCookiesSwitch = view.findViewById(R.id.third_party_cookies_switch); - Switch domStorageSwitch = view.findViewById(R.id.dom_storage_switch); - Switch formDataSwitch = view.findViewById(R.id.form_data_switch); // Form data can be removed once the minimum API >= 26. - Switch easyListSwitch = view.findViewById(R.id.easylist_switch); - Switch easyPrivacySwitch = view.findViewById(R.id.easyprivacy_switch); - Switch fanboysAnnoyanceSwitch = view.findViewById(R.id.fanboys_annoyance_list_switch); - Switch fanboysSocialBlockingSwitch = view.findViewById(R.id.fanboys_social_blocking_list_switch); - Switch ultraListSwitch = view.findViewById(R.id.ultralist_switch); - Switch ultraPrivacySwitch = view.findViewById(R.id.ultraprivacy_switch); - Switch blockAllThirdPartyRequestsSwitch = view.findViewById(R.id.block_all_third_party_requests_switch); + SwitchCompat javaScriptSwitch = view.findViewById(R.id.javascript_switch); + SwitchCompat cookiesSwitch = view.findViewById(R.id.cookies_switch); + SwitchCompat domStorageSwitch = view.findViewById(R.id.dom_storage_switch); + SwitchCompat formDataSwitch = view.findViewById(R.id.form_data_switch); // Form data can be removed once the minimum API >= 26. + SwitchCompat easyListSwitch = view.findViewById(R.id.easylist_switch); + SwitchCompat easyPrivacySwitch = view.findViewById(R.id.easyprivacy_switch); + SwitchCompat fanboysAnnoyanceSwitch = view.findViewById(R.id.fanboys_annoyance_list_switch); + SwitchCompat fanboysSocialBlockingSwitch = view.findViewById(R.id.fanboys_social_blocking_list_switch); + SwitchCompat ultraListSwitch = view.findViewById(R.id.ultralist_switch); + SwitchCompat ultraPrivacySwitch = view.findViewById(R.id.ultraprivacy_switch); + SwitchCompat blockAllThirdPartyRequestsSwitch = view.findViewById(R.id.block_all_third_party_requests_switch); Spinner userAgentSpinner = view.findViewById(R.id.user_agent_spinner); EditText customUserAgentEditText = view.findViewById(R.id.custom_user_agent_edittext); Spinner fontSizeSpinner = view.findViewById(R.id.font_size_spinner); @@ -710,16 +757,15 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo Spinner webViewThemeSpinner = view.findViewById(R.id.webview_theme_spinner); Spinner wideViewportSpinner = view.findViewById(R.id.wide_viewport_spinner); Spinner displayWebpageImagesSpinner = view.findViewById(R.id.display_webpage_images_spinner); - Switch pinnedSslCertificateSwitch = view.findViewById(R.id.pinned_ssl_certificate_switch); + SwitchCompat pinnedSslCertificateSwitch = view.findViewById(R.id.pinned_ssl_certificate_switch); RadioButton currentWebsiteCertificateRadioButton = view.findViewById(R.id.current_website_certificate_radiobutton); - Switch pinnedIpAddressesSwitch = view.findViewById(R.id.pinned_ip_addresses_switch); + SwitchCompat pinnedIpAddressesSwitch = view.findViewById(R.id.pinned_ip_addresses_switch); RadioButton currentIpAddressesRadioButton = view.findViewById(R.id.current_ip_addresses_radiobutton); // Extract the data for the domain settings. String domainNameString = domainNameEditText.getText().toString(); boolean javaScript = javaScriptSwitch.isChecked(); - boolean firstPartyCookies = firstPartyCookiesSwitch.isChecked(); - boolean thirdPartyCookies = thirdPartyCookiesSwitch.isChecked(); + boolean cookies = cookiesSwitch.isChecked(); boolean domStorage = domStorageSwitch.isChecked(); boolean formData = formDataSwitch.isChecked(); // Form data can be removed once the minimum API >= 26. boolean easyList = easyListSwitch.isChecked(); @@ -771,7 +817,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo } // Save the domain settings. - domainsDatabaseHelper.updateDomain(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScript, firstPartyCookies, thirdPartyCookies, domStorage, formData, easyList, easyPrivacy, + domainsDatabaseHelper.updateDomain(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScript, cookies, domStorage, formData, easyList, easyPrivacy, fanboysAnnoyance, fanboysSocialBlocking, ultraList, ultraPrivacy, blockAllThirdPartyRequests, userAgentName, fontSizeInt, swipeToRefreshInt, webViewThemeInt, wideViewportInt, displayWebpageImagesInt, pinnedSslCertificate, pinnedIpAddress); @@ -789,7 +835,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo } } - private void populateDomainsListView(final int highlightedDomainDatabaseId) { + private void populateDomainsListView(final int highlightedDomainDatabaseId, int domainsListViewPosition) { // get a handle for the current `domains_listview`. domainsListView = findViewById(R.id.domains_listview); @@ -807,7 +853,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo @Override public void bindView(View view, Context context, Cursor cursor) { // Set the domain name. - String domainNameString = cursor.getString(cursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME)); + String domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME)); TextView domainNameTextView = view.findViewById(R.id.domain_name_textview); domainNameTextView.setText(domainNameString); } @@ -816,6 +862,9 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Update the list view. domainsListView.setAdapter(domainsCursorAdapter); + // Restore the scroll position. + domainsListView.setSelection(domainsListViewPosition); + // Display the domain settings in the second pane if operating in two pane mode and the database contains at least one domain. if (DomainsActivity.twoPanedMode && (domainsCursor.getCount() > 0)) { // Two-paned mode is enabled and there is at least one domain. // Initialize `highlightedDomainPosition`. @@ -827,7 +876,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo domainsCursor.moveToPosition(i); // Get the database ID for this position. - int currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper._ID)); + int currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID)); // Set `highlightedDomainPosition` if the database ID for this matches `highlightedDomainDatabaseId`. if (highlightedDomainDatabaseId == currentDatabaseId) { @@ -840,14 +889,19 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Get the database ID for the highlighted domain. domainsCursor.moveToPosition(highlightedDomainPosition); - currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper._ID)); + currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper._ID)); - // Store the database ID in the arguments bundle. + // Create an arguments bundle. Bundle argumentsBundle = new Bundle(); + + // Store the domain settings in the arguments bundle. argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, currentDomainDatabaseId); + argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY); - // Add and arguments bundle to the domain settings fragment. + // Instantiate a new domain settings fragment. DomainSettingsFragment domainSettingsFragment = new DomainSettingsFragment(); + + // Add the arguments bundle to the domain settings fragment. domainSettingsFragment.setArguments(argumentsBundle); // Display the domain settings fragment. @@ -856,15 +910,8 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Enable the delete options menu items. deleteMenuItem.setEnabled(true); - // Get the current theme status. - int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - - // Set the delete icon according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) { - deleteMenuItem.setIcon(R.drawable.delete_night); - } else { - deleteMenuItem.setIcon(R.drawable.delete_day); - } + // Set the delete icon. + deleteMenuItem.setIcon(R.drawable.delete_enabled); } else if (twoPanedMode) { // Two-paned mode is enabled but there are no domains. // Disable the options `MenuItems`. deleteMenuItem.setEnabled(false);