From: Soren Stoutner Date: Wed, 28 Aug 2024 23:07:22 +0000 (-0700) Subject: Refactor for Androidx AppCompat 1.7.0. X-Git-Url: https://gitweb.stoutner.com/?a=commitdiff_plain;h=6ab544e0d9c290a798f3711d155b69dafc3102c5;p=PrivacyBrowserAndroid.git Refactor for Androidx AppCompat 1.7.0. --- diff --git a/app/build.gradle b/app/build.gradle index adc38235..c2f97b40 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,15 +82,15 @@ android { dependencies { // Include the following AndroidX libraries. - implementation "androidx.activity:activity-ktx:1.9.0" + implementation "androidx.activity:activity-ktx:1.9.1" implementation 'androidx.arch.core:core-common:2.2.0' implementation 'androidx.arch.core:core-runtime:2.2.0' - implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0' implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.drawerlayout:drawerlayout:1.2.0' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4' implementation 'androidx.preference:preference-ktx:1.2.1' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.viewpager:viewpager:1.0.0' @@ -100,5 +100,5 @@ dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.9.20' // Include the Google material library. - implementation 'com.google.android.material:material:1.13.0-alpha04' + implementation 'com.google.android.material:material:1.13.0-alpha05' } diff --git a/app/src/main/assets/de/about_changelog.html b/app/src/main/assets/de/about_changelog.html index 66c530db..cac143e0 100644 --- a/app/src/main/assets/de/about_changelog.html +++ b/app/src/main/assets/de/about_changelog.html @@ -35,7 +35,7 @@

3.18.1 (Version Code 75)

-

22. Juli 2024 - Mindest-API 26, Ziel-API 34

+

22. Juli 2024 - Mindest-API 26, Ziel-API 34

diff --git a/app/src/main/assets/en/about_changelog.html b/app/src/main/assets/en/about_changelog.html index 1a7ec5a0..8dcbaef0 100644 --- a/app/src/main/assets/en/about_changelog.html +++ b/app/src/main/assets/en/about_changelog.html @@ -29,7 +29,7 @@

3.18.1 (version code 75)

-

22 July 2024 - minimum API 26, target API 34

+

22 July 2024 - minimum API 26, target API 34

diff --git a/app/src/main/assets/es/about_changelog.html b/app/src/main/assets/es/about_changelog.html index a1a7f0ee..7af1eff0 100644 --- a/app/src/main/assets/es/about_changelog.html +++ b/app/src/main/assets/es/about_changelog.html @@ -31,7 +31,7 @@

3.18.1 (código de versión 75)

-

22 de julio de 2024 - API mínimo 26, API objetivo 34

+

22 de julio de 2024 - API mínimo 26, API objetivo 34

diff --git a/app/src/main/assets/fr/about_changelog.html b/app/src/main/assets/fr/about_changelog.html index 7484fe89..6716a513 100644 --- a/app/src/main/assets/fr/about_changelog.html +++ b/app/src/main/assets/fr/about_changelog.html @@ -31,7 +31,7 @@

3.18.1 (version du code 75)

-

22 Juillet 2024 - API minimale : 26, API optimale : 34

+

22 Juillet 2024 - API minimale : 26, API optimale : 34

diff --git a/app/src/main/assets/it/about_changelog.html b/app/src/main/assets/it/about_changelog.html index 757344f6..cc45b73a 100644 --- a/app/src/main/assets/it/about_changelog.html +++ b/app/src/main/assets/it/about_changelog.html @@ -31,7 +31,7 @@

3.18.1 (versione codice 75)

-

22 Luglio 2024 - minima API 26, target API 34

+

22 Luglio 2024 - minima API 26, target API 34

diff --git a/app/src/main/assets/pt-rBR/about_changelog.html b/app/src/main/assets/pt-rBR/about_changelog.html index c156d847..2d01884e 100644 --- a/app/src/main/assets/pt-rBR/about_changelog.html +++ b/app/src/main/assets/pt-rBR/about_changelog.html @@ -31,7 +31,7 @@

3.18.1 (código de versão 75)

-

22 de julho de 2024 - minimum API 26, target API 34

+

22 de julho de 2024 - minimum API 26, target API 34

diff --git a/app/src/main/assets/ru/about_changelog.html b/app/src/main/assets/ru/about_changelog.html index 743e30a7..75327f1e 100644 --- a/app/src/main/assets/ru/about_changelog.html +++ b/app/src/main/assets/ru/about_changelog.html @@ -29,7 +29,7 @@

3.18.1 (код версии 75)

-

22 июля 2024 года - минимальный API 26, целевой API 34

+

22 июля 2024 года - минимальный API 26, целевой API 34

diff --git a/app/src/main/assets/tr/about_changelog.html b/app/src/main/assets/tr/about_changelog.html index 684d29a1..7a3b0f65 100644 --- a/app/src/main/assets/tr/about_changelog.html +++ b/app/src/main/assets/tr/about_changelog.html @@ -29,7 +29,7 @@

3.18.1 (version code 75)

-

22 Temmuz 2024 - minimum API 26, target API 34

+

22 Temmuz 2024 - minimum API 26, target API 34

diff --git a/app/src/main/assets/zh-rCN/about_changelog.html b/app/src/main/assets/zh-rCN/about_changelog.html index 77ae1cb6..fae0ebe4 100644 --- a/app/src/main/assets/zh-rCN/about_changelog.html +++ b/app/src/main/assets/zh-rCN/about_changelog.html @@ -31,7 +31,7 @@

3.18.1 (version code 75)

-

22 July 2024 - 最低支持API 26, 最高支持API 34

+

22 July 2024 - 最低支持API 26, 最高支持API 34

diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.kt index 4199dd4e..5d7ca2d7 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/BookmarksDatabaseViewActivity.kt @@ -189,7 +189,7 @@ class BookmarksDatabaseViewActivity : AppCompatActivity(), EditBookmarkDatabaseV subfolderSpacerTextView.text = bookmarksDatabaseHelper.getSubfolderSpacer(cursor.getLong(cursor.getColumnIndexOrThrow(FOLDER_ID))) } else { // The folder is in the home folder. // Reset the subfolder spacer. - subfolderSpacerTextView.text = "" + subfolderSpacerTextView.text = null } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.kt index e0e06876..47ffe90f 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.kt @@ -23,7 +23,10 @@ import android.app.Activity import android.content.Context import android.database.Cursor import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.view.Menu +import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup @@ -40,8 +43,11 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.Toolbar import androidx.core.app.NavUtils +import androidx.core.view.MenuProvider import androidx.cursoradapter.widget.CursorAdapter import androidx.fragment.app.DialogFragment +import androidx.fragment.app.commitNow +import androidx.lifecycle.Lifecycle import androidx.preference.PreferenceManager import com.google.android.material.floatingactionbutton.FloatingActionButton @@ -52,8 +58,6 @@ import com.stoutner.privacybrowser.dialogs.AddDomainDialog import com.stoutner.privacybrowser.dialogs.AddDomainDialog.AddDomainListener import com.stoutner.privacybrowser.fragments.DomainSettingsFragment import com.stoutner.privacybrowser.fragments.DomainsListFragment -import com.stoutner.privacybrowser.fragments.DomainsListFragment.DismissSnackbarInterface -import com.stoutner.privacybrowser.fragments.DomainsListFragment.SaveDomainSettingsInterface import com.stoutner.privacybrowser.helpers.DOMAIN_NAME import com.stoutner.privacybrowser.helpers.ID import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper @@ -61,6 +65,7 @@ import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper // Define the public constants. const val CLOSE_ON_BACK = "close_on_back" const val CURRENT_IP_ADDRESSES = "current_ip_addresses" +const val DOMAIN_SETTINGS_FRAGMENT_TAG = "domain_settings_fragment" const val LOAD_DOMAIN = "load_domain" const val SSL_END_DATE = "ssl_end_date" const val SSL_ISSUED_BY_CNAME = "ssl_issued_by_cname" @@ -77,19 +82,17 @@ private const val DOMAIN_SETTINGS_DISPLAYED = "domain_settings_displayed" private const val DOMAIN_SETTINGS_SCROLL_Y = "domain_settings_scroll_y" private const val LISTVIEW_POSITION = "listview_position" -class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarInterface, SaveDomainSettingsInterface { +class DomainsActivity : AppCompatActivity(), AddDomainListener, DomainsListFragment.DismissSnackbarInterface, DomainsListFragment.SaveDomainSettingsInterface { companion object { // Define the public variables. var currentDomainDatabaseId = 0 // Used in `DomainsListFragment`. + var deleteMenuItemEnabled = true var dismissingSnackbar = false // Used in `DomainsListFragment`. var domainsListViewPosition = 0 // Used in `DomainsListFragment`. var sslEndDateLong: Long = 0 // Used in `DomainsSettingsFragment`. var sslStartDateLong: Long = 0 // Used in `DomainSettingsFragment`. var twoPanedMode = false // Used in `DomainsListFragment`. - // Declare the public views. They are used in `DomainsListFragment`. - lateinit var deleteMenuItem: MenuItem - // Declare the SSL certificate and IP address strings. var currentIpAddresses: String? = null // Used in `DomainSettingsFragment`. var sslIssuedToCName: String? = null // Used in `DomainSettingsFragment`. @@ -103,6 +106,8 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI // Declare the class views. private lateinit var addDomainFAB: FloatingActionButton private lateinit var coordinatorLayout: View + + // Define the class views. private var domainsListView: ListView? = null private var undoDeleteSnackbar: Snackbar? = null @@ -235,16 +240,15 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI val domainsListFragment = DomainsListFragment() // Display the domains list fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commitNow() + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainsListFragment) + } // 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) // Show the add domain floating action button. addDomainFAB.show() - - // Hide the delete menu item. - deleteMenuItem.isVisible = false } else { // The device is in single-paned mode and the domain list fragment is displayed. // Dismiss the undo delete SnackBar if it is shown. if (undoDeleteSnackbar != null && undoDeleteSnackbar!!.isShown) { @@ -261,22 +265,279 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI } } - // Register the on back pressed callback. - onBackPressedDispatcher.addCallback(this, onBackPressedCallback) - } + // Get a handle for the activity (used in an inner class below). + val activity: Activity = this + + // Add the menu provider. This runs each time a fragment is replaced. + addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + // Inflate the menu. + menuInflater.inflate(R.menu.domains_options_menu, menu) + + // Get a handle for the delete menu item. + val deleteMenuItem = menu.findItem(R.id.delete_domain) + + // Get the domain settings fragment. + val domainSettingsFragment = supportFragmentManager.findFragmentByTag(DOMAIN_SETTINGS_FRAGMENT_TAG) + + // Update the visibility of the delete menu item. + if (twoPanedMode) // The device is in two-paned mode and a domain is selected. + deleteMenuItem.isVisible = true + else if ((domainSettingsFragment != null) && domainSettingsFragment.isVisible) // The device is in single-paned mode and the domain settings fragment is visible. + deleteMenuItem.isVisible = true + else // The device is in two-paned mode but no domain is selected (like after deleting a domain) or the device is in single-paned mode and the domains list is visible. + deleteMenuItem.isVisible = false + + // Update the status of the delete menu item. + deleteMenuItem.isEnabled = deleteMenuItemEnabled + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + // Run the command according to the selected menu item. + when (menuItem.itemId) { + 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) + + // 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. + finish() + } + } 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) + + // Go home. + finish() + } 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) + + // Instantiate a new domains list fragment. + val domainsListFragment = DomainsListFragment() + + // Display the domains list fragment. + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainsListFragment) + } + + // 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) + + // Show the add domain floating action button. + addDomainFAB.show() + } 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 + + // Dismiss the snackbar. + undoDeleteSnackbar!!.dismiss() + } else { + // Go home. + finish() + } + } + } + + R.id.delete_domain -> { // Delete. + // 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 the current domain database ID because it could change while the snackbar is displayed. + val 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. + deletedDomainPosition = domainsListView!!.checkedItemPosition + + // Disable the delete menu item. + deleteMenuItemEnabled = false + + // Get a handle for the domain settings fragment. + val domainSettingsFragment = supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container)!! + + // Get a handle for the domain settings fragment view. + val domainSettingsFragmentView = domainSettingsFragment.requireView() + + // Hide the domain settings fragment. + domainSettingsFragmentView.visibility = View.INVISIBLE + + // Disable the delete menu item. + deleteMenuItemEnabled = false + + // Invalidate the options menu. + invalidateMenu() + + } else { // Single-paned mode. + // Instantiate a new domains list fragment. + val domainsListFragment = DomainsListFragment() + + // Display the domains list fragment. + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainsListFragment) + } + + // Show the add domain floating action button. + addDomainFAB.show() + } + + // Get a cursor that does not show the domain to be deleted. + val domainsPendingDeleteCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomainExcept(databaseIdToDelete) + + // Setup the domains pending delete cursor adapter. + val domainsPendingDeleteCursorAdapter: CursorAdapter = object : CursorAdapter(applicationContext, domainsPendingDeleteCursor, false) { + override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View { + // Inflate the individual item layout. + return layoutInflater.inflate(R.layout.domain_name_linearlayout, parent, false) + } + + override fun bindView(view: View, context: Context, cursor: Cursor) { + // Get the domain name string. + val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME)) - override fun onCreateOptionsMenu(menu: Menu): Boolean { - // Inflate the menu. - menuInflater.inflate(R.menu.domains_options_menu, menu) + // Get a handle for the domain name text view. + val domainNameTextView = view.findViewById(R.id.domain_name_textview) - // Get a handle for the delete menu item. - deleteMenuItem = menu.findItem(R.id.delete_domain) + // Display the domain name. + domainNameTextView.text = domainNameString + } + } + + // Update the handle for the current domains list view. + domainsListView = findViewById(R.id.domains_listview) + + // Update the list view. + domainsListView!!.adapter = domainsPendingDeleteCursorAdapter + + // Display a snackbar. + undoDeleteSnackbar = Snackbar.make(domainsListView!!, R.string.domain_deleted, Snackbar.LENGTH_LONG) + .setAction(R.string.undo) {} // Do nothing because everything will be handled by `onDismissed()` below. + .addCallback(object : Snackbar.Callback() { + override fun onDismissed(snackbar: Snackbar, event: Int) { + // Run commands based on the event. + if (event == DISMISS_EVENT_ACTION) { // The user pushed the `Undo` button. + // Create an arguments bundle. + val argumentsBundle = Bundle() + + // Store the domains settings in the arguments bundle. + argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseIdToDelete) + argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY) + + // Instantiate a new domain settings fragment. + val domainSettingsFragment = DomainSettingsFragment() + + // Add the arguments bundle to the domain settings fragment. + domainSettingsFragment.arguments = 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. + val undoDeleteDomainsCursor = domainsDatabaseHelper.domainNameCursorOrderedByDomain + + // Setup the domains cursor adapter. + val undoDeleteDomainsCursorAdapter: CursorAdapter = object : CursorAdapter(applicationContext, undoDeleteDomainsCursor, false) { + override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View { + // Inflate the individual item layout. + return layoutInflater.inflate(R.layout.domain_name_linearlayout, parent, false) + } + + override fun bindView(view: View, context: Context, cursor: Cursor) { + /// Get the domain name string. + val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME)) + + // Get a handle for the domain name text view. + val domainNameTextView = view.findViewById(R.id.domain_name_textview) + + // Display the domain name. + domainNameTextView.text = domainNameString + } + } + + // Update the domains list view. + domainsListView!!.adapter = undoDeleteDomainsCursorAdapter + + // Select the previously deleted domain in the list view. + domainsListView!!.setItemChecked(deletedDomainPosition, true) + + // Enable the delete menu item. + deleteMenuItemEnabled = true + + // Display the domain settings fragment. + supportFragmentManager.commitNow { + replace(R.id.domain_settings_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG) + } + } else { // The device in in one-paned mode. + // Hide the add domain floating action button. + addDomainFAB.hide() + + // Display the domain settings fragment. + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG) + } + } + } else { // The snackbar was dismissed without the undo button being pushed. + // Delete the selected domain. + val rowsDeleted = domainsDatabaseHelper.deleteDomain(databaseIdToDelete) + + // Enable the delete menu item. + // The rows deleted should always be greater than 0, but in all cases they should be greater than -1. + // This has the effect of tricking the compiler into waiting until after the delete finishes to reenable the delete menu item, + // because the compiler (probably) can't tell that the response will never be less than -1, so it doesn't compile out the delay. + if (rowsDeleted > -1) { + // Enable the delete menu item if in two-paned mode. + if (twoPanedMode) { + // Enable the delete menu item. + deleteMenuItemEnabled = true + + // Invalidate the options menu. + invalidateMenu() + } + + // Reset the dismissing snackbar tracker. + dismissingSnackbar = false + } + + // Close the activity if back was pressed. + if (closeActivityAfterDismissingSnackbar) + NavUtils.navigateUpFromSameTask(activity) + } + } + }) + + // Show the Snackbar. + undoDeleteSnackbar!!.show() + } + } + } - // Only display the delete menu item (initially) in two-paned mode. - deleteMenuItem.isVisible = twoPanedMode + // Consume the event. + return true + } + }, this, Lifecycle.State.RESUMED) - // Display the fragments. This must be done from `onCreateOptionsMenu()` instead of `onCreate()` because `populateDomainsListView()` needs `deleteMenuItem` to be inflated. - if (appRestarted && domainSettingsDisplayedBeforeRestart) { // The app was restarted, possibly because the device was rotated, and domain settings were displayed previously. + // Display the fragments. + if (appRestarted && domainSettingsDisplayedBeforeRestart) { // The app was restarted and domain settings were displayed previously. // Reset the app restarted flag. appRestarted = false @@ -285,10 +546,20 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI val domainsListFragment = DomainsListFragment() // Display the domains list fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commitNow() + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainsListFragment) + } + + // Create a populate domains list view handler. + val populateDomainsListViewHandler = Handler(Looper.getMainLooper()) + + // Create a populate domains list view runnable. + val populateDomainsListViewRunnable = Runnable { + populateDomainsListView(domainSettingsDatabaseIdBeforeRestart, domainsListViewPosition) + } - // Populate the list of domains and highlight the domain that was highlighted before the restart. - populateDomainsListView(domainSettingsDatabaseIdBeforeRestart, domainsListViewPosition) + // Populate the domains list view. For some reason, beginning with appcompat 1.7.0, this needs to be in a runnable instead of being called directly, or the system crashes. + populateDomainsListViewHandler.post(populateDomainsListViewRunnable) } else { // The device is in single-paned mode. // Store the current domain database ID. currentDomainDatabaseId = domainSettingsDatabaseIdBeforeRestart @@ -306,14 +577,13 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI // Add the arguments bundle to the domain settings fragment. domainSettingsFragment.arguments = argumentsBundle - // Show the delete menu item. - deleteMenuItem.isVisible = true - // Hide the add domain floating action button. addDomainFAB.hide() // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commitNow() + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG) + } } } else { // The device was not restarted or, if it was, domain settings were not displayed previously. if (goDirectlyToDatabaseId >= 0) { // Load the indicated domain settings. @@ -326,10 +596,20 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI val domainsListFragment = DomainsListFragment() // Display the domains list fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commitNow() + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainsListFragment) + } - // Populate the list of domains. - populateDomainsListView(goDirectlyToDatabaseId, domainsListViewPosition) + // Create a populate domains list view handler. + val populateDomainsListViewHandler = Handler(Looper.getMainLooper()) + + // Create a populate domains list view runnable. + val populateDomainsListViewRunnable = Runnable { + populateDomainsListView(goDirectlyToDatabaseId, domainsListViewPosition) + } + + // Populate the domains list view. For some reason, beginning with appcompat 1.7.0, this needs to be in a runnable instead of being called directly, or the system crashes. + populateDomainsListViewHandler.post(populateDomainsListViewRunnable) } else { // The device is in single-paned mode. // Create an arguments bundle. val argumentsBundle = Bundle() @@ -344,263 +624,38 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI // Add the arguments bundle to the domain settings fragment. domainSettingsFragment.arguments = argumentsBundle - // Show the delete menu item. - deleteMenuItem.isVisible = true - // Hide the add domain floating action button. addDomainFAB.hide() // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commitNow() + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG) + } } - } else { // Highlight the first domain. - // Instantiate a new domains list fragment. + } else { // Display the domains list view. + // Instantiate a new domain settings fragment. val domainsListFragment = DomainsListFragment() - // Display the domain list fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commitNow() - - // Populate the list of domains. `-1` highlights the first domain. - populateDomainsListView(-1, domainsListViewPosition) - } - } - - // Success! - return true - } - - override fun onOptionsItemSelected(menuItem: MenuItem): Boolean { - // Run the command according to the selected menu item. - when (menuItem.itemId) { - 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) - - // 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. - finish() - } - } 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) - - // Go home. - finish() - } 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) - - // Instantiate a new domains list fragment. - val domainsListFragment = DomainsListFragment() + // Display the domains list fragment. + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainsListFragment) + } - // Display the domains list fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commitNow() + // Create a populate domains list view handler. + val populateDomainsListViewHandler = Handler(Looper.getMainLooper()) - // Populate the list of domains. `-1` highlights the first domain if in two-paned mode. It has no effect in single-paned mode. + // Create a populate domains list view runnable. + val populateDomainsListViewRunnable = Runnable { populateDomainsListView(-1, domainsListViewPosition) - - // Show the add domain floating action button. - addDomainFAB.show() - - // Hide the delete menu item. - deleteMenuItem.isVisible = 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 - - // Dismiss the snackbar. - undoDeleteSnackbar!!.dismiss() - } else { - // Go home. - finish() - } } - } - - R.id.delete_domain -> { // Delete. - // Get a handle for the activity (used in an inner class below). - val 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 the current domain database ID because it could change while the snackbar is displayed. - val 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. - deletedDomainPosition = domainsListView!!.checkedItemPosition - - // Disable the delete menu item. - deleteMenuItem.isEnabled = false - - // Remove the domain settings fragment. - supportFragmentManager.beginTransaction().remove(supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container)!!).commitNow() - } else { // Single-paned mode. - // Instantiate a new domains list fragment. - val domainsListFragment = DomainsListFragment() - - // Display the domains list fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commitNow() - - // Show the add domain floating action button. - addDomainFAB.show() - - // Hide the delete menu item. - deleteMenuItem.isVisible = false - } - - // Get a cursor that does not show the domain to be deleted. - val domainsPendingDeleteCursor = domainsDatabaseHelper.getDomainNameCursorOrderedByDomainExcept(databaseIdToDelete) - - // Setup the domains pending delete cursor adapter. - val domainsPendingDeleteCursorAdapter: CursorAdapter = object : CursorAdapter(this, domainsPendingDeleteCursor, false) { - override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View { - // Inflate the individual item layout. - return layoutInflater.inflate(R.layout.domain_name_linearlayout, parent, false) - } - - override fun bindView(view: View, context: Context, cursor: Cursor) { - // Get the domain name string. - val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME)) - - // Get a handle for the domain name text view. - val domainNameTextView = view.findViewById(R.id.domain_name_textview) - - // Display the domain name. - domainNameTextView.text = domainNameString - } - } - - // Update the handle for the current domains list view. - domainsListView = findViewById(R.id.domains_listview) - - // Update the list view. - domainsListView!!.adapter = domainsPendingDeleteCursorAdapter - - // Display a snackbar. - undoDeleteSnackbar = Snackbar.make(domainsListView!!, R.string.domain_deleted, Snackbar.LENGTH_LONG) - .setAction(R.string.undo) {} // Do nothing because everything will be handled by `onDismissed()` below. - .addCallback(object : Snackbar.Callback() { - override fun onDismissed(snackbar: Snackbar, event: Int) { - // Run commands based on the event. - if (event == DISMISS_EVENT_ACTION) { // The user pushed the `Undo` button. - // Create an arguments bundle. - val argumentsBundle = Bundle() - - // Store the domains settings in the arguments bundle. - argumentsBundle.putInt(DomainSettingsFragment.DATABASE_ID, databaseIdToDelete) - argumentsBundle.putInt(DomainSettingsFragment.SCROLL_Y, domainSettingsScrollY) - - // Instantiate a new domain settings fragment. - val domainSettingsFragment = DomainSettingsFragment() - - // Add the arguments bundle to the domain settings fragment. - domainSettingsFragment.arguments = 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. - val undoDeleteDomainsCursor = domainsDatabaseHelper.domainNameCursorOrderedByDomain - - // Setup the domains cursor adapter. - val undoDeleteDomainsCursorAdapter: CursorAdapter = object : CursorAdapter(applicationContext, undoDeleteDomainsCursor, false) { - override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View { - // Inflate the individual item layout. - return layoutInflater.inflate(R.layout.domain_name_linearlayout, parent, false) - } - - override fun bindView(view: View, context: Context, cursor: Cursor) { - /// Get the domain name string. - val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME)) - - // Get a handle for the domain name text view. - val domainNameTextView = view.findViewById(R.id.domain_name_textview) - - // Display the domain name. - domainNameTextView.text = domainNameString - } - } - - // Update the domains list view. - domainsListView!!.adapter = undoDeleteDomainsCursorAdapter - - // Select the previously deleted domain in the list view. - domainsListView!!.setItemChecked(deletedDomainPosition, true) - - // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domain_settings_fragment_container, domainSettingsFragment).commitNow() - - // Enable the delete menu item. - deleteMenuItem.isEnabled = true - } else { // The device in in one-paned mode. - // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commitNow() - - // Hide the add domain floating action button. - addDomainFAB.hide() - - // Show and enable the delete menu item. - deleteMenuItem.isVisible = true - - // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commitNow() - } - } else { // The snackbar was dismissed without the undo button being pushed. - // Delete the selected domain. - val rowsDeleted = domainsDatabaseHelper.deleteDomain(databaseIdToDelete) - - // Enable the delete menu item. - // The rows deleted should always be greater than 0, but in all cases they should be greater than -1. - // This has the effect of tricking the compiler into waiting until after the delete finishes to reenable the delete menu item, - // because the compiler (probably) can't tell that the response will never be less than -1, so it doesn't compile out the delay. - if (rowsDeleted > -1) { - // Enable or show the delete menu item according to the display mode. - if (twoPanedMode) - deleteMenuItem.isEnabled = true - else - deleteMenuItem.isVisible = true - - // Reset the dismissing snackbar tracker. - dismissingSnackbar = false - } - - // Close the activity if back was pressed. - if (closeActivityAfterDismissingSnackbar) - NavUtils.navigateUpFromSameTask(activity) - } - } - }) - - // Show the Snackbar. - undoDeleteSnackbar!!.show() - } + // Populate the domains list view. For some reason, beginning with appcompat 1.7.0, this needs to be in a runnable instead of being called directly, or the system crashes. + populateDomainsListViewHandler.post(populateDomainsListViewRunnable) } } - // Consume the event. - return true + // Register the on back pressed callback. + onBackPressedDispatcher.addCallback(this, onBackPressedCallback) } override fun onSaveInstanceState(outState: Bundle) { @@ -660,9 +715,6 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI // Hide the add domain floating action button. addDomainFAB.hide() - // Show and enable the delete menu item. - deleteMenuItem.isVisible = true - // Create an arguments bundle. val argumentsBundle = Bundle() @@ -677,7 +729,9 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI domainSettingsFragment.arguments = argumentsBundle // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commitNow() + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG) + } } } @@ -800,7 +854,7 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI 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 (twoPanedMode && domainsCursor.count > 0) { // Two-paned mode is enabled and there is at least one domain. + if (twoPanedMode && (domainsCursor.count > 0)) { // Two-paned mode is enabled and there is at least one domain. // Initialize the highlighted domain position tracker. var highlightedDomainPosition = 0 @@ -840,13 +894,21 @@ class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarI domainSettingsFragment.arguments = argumentsBundle // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domain_settings_fragment_container, domainSettingsFragment).commit() + supportFragmentManager.commitNow { + replace(R.id.domain_settings_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG) + } - // Enable the delete options menu items. - deleteMenuItem.isEnabled = true + // Enable the delete options menu item. + deleteMenuItemEnabled = true + + // Update the options menu. + invalidateMenu() } else if (twoPanedMode) { // Two-paned mode is enabled but there are no domains. // Disable the delete menu item. - deleteMenuItem.isEnabled = false + deleteMenuItemEnabled = false + + // Update the options menu. + invalidateMenu() } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt index a1d8ecc7..1e31f8f2 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt @@ -5598,7 +5598,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Check to see if the URL is `about:blank`. if (currentUrl == "about:blank") { // The WebView is blank. // Display the hint in the URL edit text. - urlEditText.setText("") + urlEditText.text = null // Request focus for the URL text box. urlEditText.requestFocus() @@ -6186,7 +6186,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook if (!loadingNewIntent) { // A new intent is not being loaded. if ((urlString == null) || (urlString == "about:blank")) { // The WebView is blank. // Display the hint in the URL edit text. - urlEditText.setText("") + urlEditText.text = null // Request focus for the URL text box. urlEditText.requestFocus() diff --git a/app/src/main/java/com/stoutner/privacybrowser/backgroundtasks/GetHeadersBackgroundTask.kt b/app/src/main/java/com/stoutner/privacybrowser/backgroundtasks/GetHeadersBackgroundTask.kt index cd91ecba..73b7754b 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/backgroundtasks/GetHeadersBackgroundTask.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/backgroundtasks/GetHeadersBackgroundTask.kt @@ -70,7 +70,7 @@ class GetHeadersBackgroundTask { // Get the colon string. val colonString = application.getString(R.string.colon) - val newLineString = System.getProperty("line.separator") + val newLineString = System.lineSeparator() if (urlString.startsWith("content://")) { // This is a content URL. // Attempt to read the content data. Return an error if this fails. diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt index 97875353..4c8da568 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt @@ -45,7 +45,10 @@ import android.webkit.WebView import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.view.MenuHost +import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle import com.google.android.material.snackbar.Snackbar @@ -214,12 +217,88 @@ class AboutVersionFragment : Fragment() { // Store the arguments in class variables. filterListsVersions = requireArguments().getStringArray(FILTERLISTS_VERSIONS)!! - - // Enable the options menu for this fragment. - setHasOptionsMenu(true) } override fun onCreateView(layoutInflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + // Add an options menu. + (requireActivity() as MenuHost).addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + // Inflate the about version menu. + menuInflater.inflate(R.menu.about_version_options_menu, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + // Run the appropriate commands. + when (menuItem.itemId) { + R.id.copy -> { // Copy. + // Get the about version string. + val aboutVersionString = getAboutVersionString() + + // Get a handle for the clipboard manager. + val clipboardManager = (requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager) + + // Place the about version string in a clip data. + val aboutVersionClipData = ClipData.newPlainText(getString(R.string.about), aboutVersionString) + + // Place the clip data on the clipboard. + clipboardManager.setPrimaryClip(aboutVersionClipData) + + // Display a snackbar if the API <= 32 (Android 12L). Beginning in Android 13 the OS displays a notification that covers up the snackbar. + if (Build.VERSION.SDK_INT <= 32) + Snackbar.make(aboutVersionLayout, R.string.version_info_copied, Snackbar.LENGTH_SHORT).show() + + // Consume the event. + return true + } + + R.id.share -> { // Share. + // Get the about version string. + val aboutString = getAboutVersionString() + + // Create a share intent. + val shareIntent = Intent(Intent.ACTION_SEND) + + // Add the about version string to the intent. + shareIntent.putExtra(Intent.EXTRA_TEXT, aboutString) + + // Set the MIME type. + shareIntent.type = "text/plain" + + // Set the intent to open in a new task. + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + // Make it so. + startActivity(Intent.createChooser(shareIntent, getString(R.string.share))) + + // Consume the event. + return true + } + + R.id.save_text -> { // Save text. + // Open the file picker. + saveAboutVersionTextActivityResultLauncher.launch(getString(R.string.privacy_browser_version_txt, BuildConfig.VERSION_NAME)) + + // Consume the event. + return true + } + + R.id.save_image -> { // Save image. + // Open the file picker. + saveAboutVersionImageActivityResultLauncher.launch(getString(R.string.privacy_browser_version_png, BuildConfig.VERSION_NAME)) + + // Consume the event. + return true + } + + else -> { // The home button was selected. + // Do not consume the event. + return false + } + } + } + + }, viewLifecycleOwner, Lifecycle.State.RESUMED) + // Inflate the layout. Setting false at the end of inflater.inflate does not attach the inflated layout as a child of container. The fragment will take care of attaching the root automatically. aboutVersionLayout = layoutInflater.inflate(R.layout.about_version_scrollview, container, false) @@ -575,84 +654,6 @@ class AboutVersionFragment : Fragment() { return aboutVersionLayout } - override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) { - // Inflate the about version menu. - menuInflater.inflate(R.menu.about_version_options_menu, menu) - - // Run the default commands. - super.onCreateOptionsMenu(menu, menuInflater) - } - - override fun onOptionsItemSelected(menuItem: MenuItem): Boolean { - // Run the appropriate commands. - when (menuItem.itemId) { - R.id.copy -> { // Copy. - // Get the about version string. - val aboutVersionString = getAboutVersionString() - - // Get a handle for the clipboard manager. - val clipboardManager = (requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager) - - // Place the about version string in a clip data. - val aboutVersionClipData = ClipData.newPlainText(getString(R.string.about), aboutVersionString) - - // Place the clip data on the clipboard. - clipboardManager.setPrimaryClip(aboutVersionClipData) - - // Display a snackbar if the API <= 32 (Android 12L). Beginning in Android 13 the OS displays a notification that covers up the snackbar. - if (Build.VERSION.SDK_INT <= 32) - Snackbar.make(aboutVersionLayout, R.string.version_info_copied, Snackbar.LENGTH_SHORT).show() - - // Consume the event. - return true - } - - R.id.share -> { // Share. - // Get the about version string. - val aboutString = getAboutVersionString() - - // Create a share intent. - val shareIntent = Intent(Intent.ACTION_SEND) - - // Add the about version string to the intent. - shareIntent.putExtra(Intent.EXTRA_TEXT, aboutString) - - // Set the MIME type. - shareIntent.type = "text/plain" - - // Set the intent to open in a new task. - shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - // Make it so. - startActivity(Intent.createChooser(shareIntent, getString(R.string.share))) - - // Consume the event. - return true - } - - R.id.save_text -> { // Save text. - // Open the file picker. - saveAboutVersionTextActivityResultLauncher.launch(getString(R.string.privacy_browser_version_txt, BuildConfig.VERSION_NAME)) - - // Consume the event. - return true - } - - R.id.save_image -> { // Save image. - // Open the file picker. - saveAboutVersionImageActivityResultLauncher.launch(getString(R.string.privacy_browser_version_png, BuildConfig.VERSION_NAME)) - - // Consume the event. - return true - } - - else -> { // The home button was selected. - // Run the parents class on return. - return super.onOptionsItemSelected(menuItem) - } - } - } - override fun onSaveInstanceState(savedInstanceState: Bundle) { // Run the default commands. super.onSaveInstanceState(savedInstanceState) diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt index 96a475b5..f43bca5a 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutWebViewFragment.kt @@ -1,7 +1,7 @@ /* - * Copyright © 2016-2023 Soren Stoutner . + * Copyright 2016-2023 Soren Stoutner . * - * This file is part of Privacy Browser Android . + * This file is part of Privacy Browser Android . * * 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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.kt b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.kt index 00c3447e..e59a9c44 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.kt @@ -1,7 +1,7 @@ /* * Copyright 2017-2024 Soren Stoutner . * - * This file is part of Privacy Browser Android . + * This file is part of Privacy Browser Android . * * 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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainsListFragment.kt b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainsListFragment.kt index 558bba2c..307480c8 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainsListFragment.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainsListFragment.kt @@ -1,7 +1,7 @@ /* - * Copyright © 2017-2020,2022 Soren Stoutner . + * Copyright 2017-2020, 2022, 2024 Soren Stoutner . * - * This file is part of Privacy Browser Android . + * This file is part of Privacy Browser Android . * * 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 @@ -29,10 +29,12 @@ import android.widget.AdapterView.OnItemClickListener import android.widget.ListView import androidx.fragment.app.Fragment +import androidx.fragment.app.commitNow import com.google.android.material.floatingactionbutton.FloatingActionButton import com.stoutner.privacybrowser.R +import com.stoutner.privacybrowser.activities.DOMAIN_SETTINGS_FRAGMENT_TAG import com.stoutner.privacybrowser.activities.DomainsActivity class DomainsListFragment : Fragment() { @@ -40,12 +42,12 @@ class DomainsListFragment : Fragment() { private lateinit var dismissSnackbarInterface: DismissSnackbarInterface private lateinit var saveDomainSettingsInterface: SaveDomainSettingsInterface - // Define the public dismiss snackbar interface. + // Define the dismiss snackbar interface. interface DismissSnackbarInterface { fun dismissSnackbar() } - // Define the public save domain interface. + // Define the save domain interface. interface SaveDomainSettingsInterface { fun saveDomainSettings(view: View) } @@ -74,16 +76,16 @@ class DomainsListFragment : Fragment() { // Dismiss the snackbar if it is visible. dismissSnackbarInterface.dismissSnackbar() - // Save the current domain settings if operating in two-paned mode and a domain is currently selected. - if (DomainsActivity.twoPanedMode && DomainsActivity.deleteMenuItem.isEnabled) { - // Get a handle for the domain settings fragment. - val domainSettingsFragment = supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container)!! + // Get a handle for the old domain settings fragment. + val oldDomainSettingsFragment = supportFragmentManager.findFragmentById(R.id.domain_settings_fragment_container) - // Get a handle for the domain settings fragment view. - val domainSettingsFragmentView = domainSettingsFragment.requireView() + // Save the current domain settings if operating in two-paned mode and a domain is currently selected. + if (DomainsActivity.twoPanedMode && (oldDomainSettingsFragment != null)) { + // Get a handle for the old domain settings fragment view. + val oldDomainSettingsFragmentView = oldDomainSettingsFragment.requireView() // Save the domain settings. - saveDomainSettingsInterface.saveDomainSettings(domainSettingsFragmentView) + saveDomainSettingsInterface.saveDomainSettings(oldDomainSettingsFragmentView) } // Store the new current domain database ID, converting it from long to int to match the format of the domains database. @@ -103,22 +105,14 @@ class DomainsListFragment : Fragment() { // Check to see if the device is in two paned mode. if (DomainsActivity.twoPanedMode) { // The device in in two-paned mode. - // Enable the delete menu item if the system is not waiting for a snackbar to be dismissed. - if (!DomainsActivity.dismissingSnackbar) { - // Enable the delete menu item. - DomainsActivity.deleteMenuItem.isEnabled = true - } - // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domain_settings_fragment_container, domainSettingsFragment).commit() + supportFragmentManager.commitNow { + replace(R.id.domain_settings_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG) + } } else { // The device in in single-paned mode // Save the domains listview position. DomainsActivity.domainsListViewPosition = domainsListView.firstVisiblePosition - // Show the delete menu item if the system is not waiting for a snackbar to be dismissed. - if (!DomainsActivity.dismissingSnackbar) - DomainsActivity.deleteMenuItem.isVisible = true - // Get a handle for the add domain floating action button. val addDomainFab = requireActivity().findViewById(R.id.add_domain_fab) @@ -126,11 +120,13 @@ class DomainsListFragment : Fragment() { addDomainFab.hide() // Display the domain settings fragment. - supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit() + supportFragmentManager.commitNow { + replace(R.id.domains_listview_fragment_container, domainSettingsFragment, DOMAIN_SETTINGS_FRAGMENT_TAG) + } } } // Return the domains list fragment. return domainsListFragmentView } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.kt b/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.kt index b5d14b44..957eb10b 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/SettingsFragment.kt @@ -1,7 +1,7 @@ /* * Copyright 2016-2024 Soren Stoutner . * - * This file is part of Privacy Browser Android . + * This file is part of Privacy Browser Android . * * 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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/WebViewTabFragment.kt b/app/src/main/java/com/stoutner/privacybrowser/fragments/WebViewTabFragment.kt index 2dae2419..f1e27ec0 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/WebViewTabFragment.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/WebViewTabFragment.kt @@ -1,7 +1,7 @@ /* - * Copyright 2019-2020,2022-2023 Soren Stoutner . + * Copyright 2019-2020, 2022-2023 Soren Stoutner . * - * This file is part of Privacy Browser Android . + * This file is part of Privacy Browser Android . * * 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 diff --git a/app/src/main/res/layout-w900dp/domains_fragments.xml b/app/src/main/res/layout-w900dp/domains_fragments.xml index 10f985be..2e0edff8 100644 --- a/app/src/main/res/layout-w900dp/domains_fragments.xml +++ b/app/src/main/res/layout-w900dp/domains_fragments.xml @@ -1,9 +1,9 @@