/*
- * Copyright 2017-2022 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2017-2024 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
*
import android.content.Context
import android.database.Cursor
import android.os.Bundle
-import android.os.Handler
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ScrollView
import android.widget.Spinner
import android.widget.TextView
-import androidx.activity.OnBackPressedCallback
+import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat
import androidx.appcompat.widget.Toolbar
import com.google.android.material.snackbar.Snackbar
import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.dialogs.AddDomainDialog
import com.stoutner.privacybrowser.dialogs.AddDomainDialog.AddDomainListener
-import com.stoutner.privacybrowser.dialogs.AddDomainDialog.Companion.addDomain
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
+// Define the public constants.
+const val CLOSE_ON_BACK = "close_on_back"
+const val CURRENT_IP_ADDRESSES = "current_ip_addresses"
+const val LOAD_DOMAIN = "load_domain"
+const val SSL_END_DATE = "ssl_end_date"
+const val SSL_ISSUED_BY_CNAME = "ssl_issued_by_cname"
+const val SSL_ISSUED_BY_ONAME = "ssl_issued_by_oname"
+const val SSL_ISSUED_BY_UNAME = "ssl_issued_by_uname"
+const val SSL_ISSUED_TO_CNAME = "ssl_issued_to_cname"
+const val SSL_ISSUED_TO_ONAME = "ssl_issued_to_oname"
+const val SSL_ISSUED_TO_UNAME = "ssl_issued_to_uname"
+const val SSL_START_DATE = "ssl_start_date"
+
// Define the class constants.
private const val DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id"
private const val DOMAIN_SETTINGS_DISPLAYED = "domain_settings_displayed"
class DomainsActivity : AppCompatActivity(), AddDomainListener, DismissSnackbarInterface, SaveDomainSettingsInterface {
companion object {
- // Define the public constants.
- const val CLOSE_ON_BACK = "close_on_back"
- const val CURRENT_IP_ADDRESSES = "current_ip_addresses"
- const val CURRENT_URL = "current_url"
- const val LOAD_DOMAIN = "load_domain"
- const val SSL_END_DATE = "ssl_end_date"
- const val SSL_ISSUED_BY_CNAME = "ssl_issued_by_cname"
- const val SSL_ISSUED_BY_ONAME = "ssl_issued_by_oname"
- const val SSL_ISSUED_BY_UNAME = "ssl_issued_by_uname"
- const val SSL_ISSUED_TO_CNAME = "ssl_issued_to_cname"
- const val SSL_ISSUED_TO_ONAME = "ssl_issued_to_oname"
- const val SSL_ISSUED_TO_UNAME = "ssl_issued_to_uname"
- const val SSL_START_DATE = "ssl_start_date"
-
// Define the public variables.
var currentDomainDatabaseId = 0 // Used in `DomainsListFragment`.
var dismissingSnackbar = false // Used in `DomainsListFragment`.
// Get the status of close-on-back, which is true when the domains activity is called from the options menu.
closeOnBack = intent.getBooleanExtra(CLOSE_ON_BACK, false)
- // Get the current URL.
- val currentUrl = intent.getStringExtra(CURRENT_URL)
-
// Store the current SSL certificate information in class variables.
sslIssuedToCName = intent.getStringExtra(SSL_ISSUED_TO_CNAME)
sslIssuedToOName = intent.getStringExtra(SSL_ISSUED_TO_ONAME)
// Configure the add domain floating action button.
addDomainFAB.setOnClickListener {
// Create an add domain dialog.
- val addDomainDialog: DialogFragment = addDomain(currentUrl)
+ val addDomainDialog: DialogFragment = AddDomainDialog()
// Show the add domain dialog.
addDomainDialog.show(supportFragmentManager, resources.getString(R.string.add_domain))
}
- // Control what the navigation bar back button does.
+ // Control what the system back command does.
val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (twoPanedMode) { // The device is in two-paned mode.
override fun bindView(view: View, context: Context, cursor: Cursor) {
// Get the domain name string.
- val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME))
+ val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME))
// Get a handle for the domain name text view.
val domainNameTextView = view.findViewById<TextView>(R.id.domain_name_textview)
override fun bindView(view: View, context: Context, cursor: Cursor) {
/// Get the domain name string.
- val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME))
+ val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME))
// Get a handle for the domain name text view.
val domainNameTextView = view.findViewById<TextView>(R.id.domain_name_textview)
}
} else { // The snackbar was dismissed without the undo button being pushed.
// Delete the selected domain.
- domainsDatabaseHelper.deleteDomain(databaseIdToDelete)
-
- // Enable the delete menu item if the system was waiting for a snackbar to be dismissed.
- if (dismissingSnackbar) {
- // Create a runnable to enable the delete menu item.
- val enableDeleteMenuItemRunnable = Runnable {
- // 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
- }
-
- // Instantiate a handler running the main looper.
- val handler = Handler(mainLooper)
-
- // Enable or show the delete menu icon after 100 milliseconds to make sure that the previous domain has been deleted from the database.
- handler.postDelayed(enableDeleteMenuItemRunnable, 100)
+ 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.
}
}
- override fun onAddDomain(dialogFragment: DialogFragment) {
+ override fun addDomain(dialogFragment: DialogFragment) {
// Dismiss the undo delete snackbar if it is currently displayed.
if (undoDeleteSnackbar != null && undoDeleteSnackbar!!.isShown)
undoDeleteSnackbar!!.dismiss()
override fun saveDomainSettings(view: View) {
// Get handles for the domain settings.
val domainNameEditText = view.findViewById<EditText>(R.id.domain_settings_name_edittext)
- val javaScriptSwitch = view.findViewById<SwitchCompat>(R.id.javascript_switch)
- val cookiesSwitch = view.findViewById<SwitchCompat>(R.id.cookies_switch)
- val domStorageSwitch = view.findViewById<SwitchCompat>(R.id.dom_storage_switch)
- val formDataSwitch = view.findViewById<SwitchCompat>(R.id.form_data_switch) // Form data can be removed once the minimum API >= 26.
- val easyListSwitch = view.findViewById<SwitchCompat>(R.id.easylist_switch)
- val easyPrivacySwitch = view.findViewById<SwitchCompat>(R.id.easyprivacy_switch)
- val fanboysAnnoyanceSwitch = view.findViewById<SwitchCompat>(R.id.fanboys_annoyance_list_switch)
- val fanboysSocialBlockingSwitch = view.findViewById<SwitchCompat>(R.id.fanboys_social_blocking_list_switch)
- val ultraListSwitch = view.findViewById<SwitchCompat>(R.id.ultralist_switch)
- val ultraPrivacySwitch = view.findViewById<SwitchCompat>(R.id.ultraprivacy_switch)
- val blockAllThirdPartyRequestsSwitch = view.findViewById<SwitchCompat>(R.id.block_all_third_party_requests_switch)
+ val javaScriptSpinner = view.findViewById<Spinner>(R.id.javascript_spinner)
+ val cookiesSpinner = view.findViewById<Spinner>(R.id.cookies_spinner)
+ val domStorageSpinner = view.findViewById<Spinner>(R.id.dom_storage_spinner)
val userAgentSpinner = view.findViewById<Spinner>(R.id.user_agent_spinner)
val customUserAgentEditText = view.findViewById<EditText>(R.id.custom_user_agent_edittext)
- val xRequestedWithHeaderSpinner = view.findViewById<Spinner>(R.id.x_requested_with_header_spinner)
+ val easyListSpinner = view.findViewById<Spinner>(R.id.easylist_spinner)
+ val easyPrivacySpinner = view.findViewById<Spinner>(R.id.easyprivacy_spinner)
+ val fanboysAnnoyanceSpinner = view.findViewById<Spinner>(R.id.fanboys_annoyance_list_spinner)
+ val fanboysSocialBlockingSpinner = view.findViewById<Spinner>(R.id.fanboys_social_blocking_list_spinner)
+ val ultraListSpinner = view.findViewById<Spinner>(R.id.ultralist_spinner)
+ val ultraPrivacySpinner = view.findViewById<Spinner>(R.id.ultraprivacy_spinner)
+ val blockAllThirdPartyRequestsSpinner = view.findViewById<Spinner>(R.id.block_all_third_party_requests_spinner)
val fontSizeSpinner = view.findViewById<Spinner>(R.id.font_size_spinner)
val customFontSizeEditText = view.findViewById<EditText>(R.id.custom_font_size_edittext)
val swipeToRefreshSpinner = view.findViewById<Spinner>(R.id.swipe_to_refresh_spinner)
val webViewThemeSpinner = view.findViewById<Spinner>(R.id.webview_theme_spinner)
val wideViewportSpinner = view.findViewById<Spinner>(R.id.wide_viewport_spinner)
- val displayWebpageImagesSpinner = view.findViewById<Spinner>(R.id.display_webpage_images_spinner)
+ val displayWebpageImagesSpinner = view.findViewById<Spinner>(R.id.display_images_spinner)
val pinnedSslCertificateSwitch = view.findViewById<SwitchCompat>(R.id.pinned_ssl_certificate_switch)
val currentWebsiteCertificateRadioButton = view.findViewById<RadioButton>(R.id.current_website_certificate_radiobutton)
val pinnedIpAddressesSwitch = view.findViewById<SwitchCompat>(R.id.pinned_ip_addresses_switch)
// Extract the data for the domain settings.
val domainNameString = domainNameEditText.text.toString()
- val javaScript = javaScriptSwitch.isChecked
- val cookies = cookiesSwitch.isChecked
- val domStorage = domStorageSwitch.isChecked
- val formData = formDataSwitch.isChecked // Form data can be removed once the minimum API >= 26.
- val easyList = easyListSwitch.isChecked
- val easyPrivacy = easyPrivacySwitch.isChecked
- val fanboysAnnoyance = fanboysAnnoyanceSwitch.isChecked
- val fanboysSocialBlocking = fanboysSocialBlockingSwitch.isChecked
- val ultraList = ultraListSwitch.isChecked
- val ultraPrivacy = ultraPrivacySwitch.isChecked
- val blockAllThirdPartyRequests = blockAllThirdPartyRequestsSwitch.isChecked
+ val javaScriptInt = javaScriptSpinner.selectedItemPosition
+ val cookiesInt = cookiesSpinner.selectedItemPosition
+ val domStorageInt = domStorageSpinner.selectedItemPosition
val userAgentSwitchPosition = userAgentSpinner.selectedItemPosition
- val xRequestedWithHeaderSwitchInt = xRequestedWithHeaderSpinner.selectedItemPosition
+ val easyListInt = easyListSpinner.selectedItemPosition
+ val easyPrivacyInt = easyPrivacySpinner.selectedItemPosition
+ val fanboysAnnoyanceInt = fanboysAnnoyanceSpinner.selectedItemPosition
+ val fanboysSocialBlockingInt = fanboysSocialBlockingSpinner.selectedItemPosition
+ val ultraListInt = ultraListSpinner.selectedItemPosition
+ val ultraPrivacyInt = ultraPrivacySpinner.selectedItemPosition
+ val blockAllThirdPartyRequestsInt = blockAllThirdPartyRequestsSpinner.selectedItemPosition
val fontSizeSwitchPosition = fontSizeSpinner.selectedItemPosition
val swipeToRefreshInt = swipeToRefreshSpinner.selectedItemPosition
val webViewThemeInt = webViewThemeSpinner.selectedItemPosition
// Get the user agent name.
val userAgentName: String = when (userAgentSwitchPosition) {
- MainWebViewActivity.DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> resources.getString(R.string.system_default_user_agent) // Set the user agent name to be `System default user agent`.
- MainWebViewActivity.DOMAINS_CUSTOM_USER_AGENT -> customUserAgentEditText.text.toString() // Set the user agent name to be the custom user agent.
+ // Set the user agent name to be `System default user agent`.
+ DOMAINS_SYSTEM_DEFAULT_USER_AGENT -> resources.getString(R.string.system_default_user_agent)
+
+ // Set the user agent name to be the custom user agent.
+ DOMAINS_CUSTOM_USER_AGENT -> customUserAgentEditText.text.toString()
+
else -> {
// Get the array of user agent names.
val userAgentNameArray = resources.getStringArray(R.array.user_agent_names)
fontSizeInt = customFontSizeEditText.text.toString().toInt()
// Save the domain settings.
- domainsDatabaseHelper.updateDomain(currentDomainDatabaseId, domainNameString, javaScript, cookies, domStorage, formData, easyList, easyPrivacy, fanboysAnnoyance, fanboysSocialBlocking, ultraList,
- ultraPrivacy, blockAllThirdPartyRequests, userAgentName, xRequestedWithHeaderSwitchInt, fontSizeInt, swipeToRefreshInt, webViewThemeInt, wideViewportInt, displayWebpageImagesInt,
+ domainsDatabaseHelper.updateDomain(currentDomainDatabaseId, domainNameString, javaScriptInt, cookiesInt, domStorageInt, userAgentName, easyListInt, easyPrivacyInt, fanboysAnnoyanceInt,
+ fanboysSocialBlockingInt, ultraListInt, ultraPrivacyInt, blockAllThirdPartyRequestsInt, fontSizeInt, swipeToRefreshInt, webViewThemeInt, wideViewportInt, displayWebpageImagesInt,
pinnedSslCertificate, pinnedIpAddress)
// Update the pinned SSL certificate if a new one is checked.
val domainNameTextView = view.findViewById<TextView>(R.id.domain_name_textview)
// Get the domain name string.
- val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DomainsDatabaseHelper.DOMAIN_NAME))
+ val domainNameString = cursor.getString(cursor.getColumnIndexOrThrow(DOMAIN_NAME))
// Set the domain name.
domainNameTextView.text = domainNameString
domainsCursor.moveToPosition(i)
// Get the database ID for this position.
- val currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID))
+ val currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(ID))
// Set the highlighted domain position if the database ID for this matches the highlighted domain database ID.
if (highlightedDomainDatabaseId == currentDatabaseId)
domainsCursor.moveToPosition(highlightedDomainPosition)
// Get the database ID for the highlighted domain.
- currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(DomainsDatabaseHelper.ID))
+ currentDomainDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(ID))
// Create an arguments bundle.
val argumentsBundle = Bundle()