From: Soren Stoutner Date: Fri, 10 Jul 2026 00:57:47 +0000 (-0700) Subject: Move between requests entries without reloading the dialog. https://redmine.stoutner... X-Git-Url: https://gitweb.stoutner.com/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=PrivacyBrowserAndroid.git Move between requests entries without reloading the dialog. https://redmine.stoutner.com/issues/1225 --- diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.kt index 2520575d..9ad0d84b 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/RequestsActivity.kt @@ -45,7 +45,6 @@ import com.stoutner.privacybrowser.adapters.RequestsArrayAdapter import com.stoutner.privacybrowser.dataclasses.RequestDataClass import com.stoutner.privacybrowser.dataclasses.RequestDisposition import com.stoutner.privacybrowser.dialogs.ViewRequestDialog -import com.stoutner.privacybrowser.dialogs.ViewRequestDialog.ViewRequestListener // Define the public constants. const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "block_all_third_party_requests" @@ -60,7 +59,7 @@ private const val ALLOWED_RESOURCE_REQUESTS = 2 private const val BLOCKED_RESOURCE_REQUESTS = 3 private const val THIRD_PARTY_RESOURCE_REQUESTS = 4 -class RequestsActivity : AppCompatActivity(), ViewRequestListener { +class RequestsActivity : AppCompatActivity() { companion object { // The resource requests are populated by `MainWebViewActivity` before `RequestsActivity` is launched. lateinit var resourceRequestsDataClassList: List @@ -268,25 +267,20 @@ class RequestsActivity : AppCompatActivity(), ViewRequestListener { outState.putInt(LISTVIEW_POSITION, listViewPosition) } - override fun onNext(currentId: Int) { - // Show the next dialog. - launchViewRequestDialog(currentId + 1) - } - - override fun onPrevious(currentId: Int) { - // Show the previous dialog. - launchViewRequestDialog(currentId - 1) - } - private fun launchViewRequestDialog(id: Int) { - // Determine if this is the last request in the list. - val isLastRequest = (id == requestsListView.count) + // Create a request data class array list. + val requestDataClassArrayList = arrayListOf() + + // Get the number of requests. + val numberOfRequests = requestsListView.count - // Get the request data class. The resource requests list view is zero based. - val requestDataClass = (requestsListView.getItemAtPosition(id - 1) as RequestDataClass) + // Populate the request data class array list. The requests list view is 0 based. + for (i in 0..): ViewRequestDialog { // Create a bundle. val bundle = Bundle() // Store the request details. bundle.putInt(ID, id) - bundle.putBoolean(IS_LAST_REQUEST, isLastRequest) - bundle.putParcelable(REQUEST_DATA_CLASS, requestDataClass) + bundle.putParcelableArrayList(REQUEST_DATA_CLASS_ARRAY_LIST, requestDataClassArrayList) // Create a new instance of the view request dialog. val viewRequestDialog = ViewRequestDialog() @@ -71,32 +65,56 @@ class ViewRequestDialog : DialogFragment() { } } - // Define the class variables. - private lateinit var viewRequestListener: ViewRequestListener - - // The public interface is used to send information back to the parent activity. - interface ViewRequestListener { - // Show the previous request. - fun onPrevious(currentId: Int) - - // Show the next request. - fun onNext(currentId: Int) - } - - override fun onAttach(context: Context) { - // Run the default commands. - super.onAttach(context) - - // Get a handle for the listener from the launching context. - viewRequestListener = context as ViewRequestListener - } + // Define the private variables. + private var blueColor = 0 + private var currentId = 0 + private var redColor = 0 + private var transparentColor = 0 + private var yellowColor = 0 + + // Declare the class variables. + private lateinit var requestDataClassArrayList: ArrayList + + // Declare the class views. + private lateinit var alertDialog: AlertDialog + private lateinit var requestDispositionTextView: TextView + private lateinit var webpageUrlTextView: TextView + private lateinit var requestUrlTextView: TextView + private lateinit var requestUrlWithSeparatorsTextView: TextView + private lateinit var truncatedRequestUrlTextView: TextView + private lateinit var truncatedRequestUrlWithSeparatorsTextView: TextView + private lateinit var thirdPartyRequestTextView: TextView + private lateinit var filterListEntryTextView: TextView + private lateinit var filterListLabelTextView: TextView + private lateinit var filterListTextView: TextView + private lateinit var sublistLabelTextView: TextView + private lateinit var sublistTextView: TextView + private lateinit var appliedEntryListLabelTextView: TextView + private lateinit var appliedEntryListTextView: TextView + private lateinit var domainLabelTextView: TextView + private lateinit var domainTextView: TextView + private lateinit var domainListLabelTextView: TextView + private lateinit var domainListTextView: TextView + private lateinit var thirdPartyFilterListEntryLabelTextView: TextView + private lateinit var thirdPartyFilterListEntryTextView: TextView + private lateinit var initialMatchLabelTextView: TextView + private lateinit var initialMatchTextView: TextView + private lateinit var finalMatchLabelTextView: TextView + private lateinit var finalMatchTextView: TextView + private lateinit var appliedFilterOptionsLabelTextView: TextView + private lateinit var appliedFilterOptionsTextView: TextView + private lateinit var originalFilterOptionsLabelTextView: TextView + private lateinit var originalFilterOptionsTextView: TextView + private lateinit var originalEntryLabelTextView: TextView + private lateinit var originalEntryTextView: TextView + private lateinit var previousButton: Button + private lateinit var nextButton: Button override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { // Get the arguments from the bundle. - // The deprecated `getParcelable()` method can be updated once the minimum API >= 33. - val id = requireArguments().getInt(ID) - val isLastRequest = requireArguments().getBoolean(IS_LAST_REQUEST) - @Suppress("DEPRECATION") val requestDataClass = requireArguments().getParcelable(REQUEST_DATA_CLASS)!! + currentId = requireArguments().getInt(ID) + @Suppress("DEPRECATION") // The deprecated `getParcelable()` method can be updated once the minimum API >= 33. + requestDataClassArrayList = requireArguments().getParcelableArrayList(REQUEST_DATA_CLASS_ARRAY_LIST)!! // Use an alert dialog builder to create the alert dialog. val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog) @@ -104,7 +122,7 @@ class ViewRequestDialog : DialogFragment() { // Set the icon. dialogBuilder.setIcon(R.drawable.block_ads_enabled) - // Set the title. + // Set the title. This must be set here or it can't be updated later. dialogBuilder.setTitle(resources.getString(R.string.request_details) + " - " + id) // Set the view. @@ -113,20 +131,14 @@ class ViewRequestDialog : DialogFragment() { // Set the close button. Using `null` as the listener closes the dialog without doing anything else. dialogBuilder.setNeutralButton(R.string.close, null) - // Set the previous button. - dialogBuilder.setNegativeButton(R.string.previous) { _: DialogInterface?, _: Int -> - // Load the previous request. - viewRequestListener.onPrevious(id) - } + // Set the previous button. The button actions are set later so they can run without closing the dialog. + dialogBuilder.setNegativeButton(R.string.previous, null) - // Set the next button. - dialogBuilder.setPositiveButton(R.string.next) { _: DialogInterface?, _: Int -> - // Load the next request. - viewRequestListener.onNext(id) - } + // Set the next button. The button actions are set later so they can run without closing the dialog. + dialogBuilder.setPositiveButton(R.string.next, null) // Create an alert dialog from the alert dialog builder. - val alertDialog = dialogBuilder.create() + alertDialog = dialogBuilder.create() // Get a handle for the shared preferences. val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) @@ -142,49 +154,109 @@ class ViewRequestDialog : DialogFragment() { alertDialog.show() // Get handles for the dialog views. - val requestDispositionTextView = alertDialog.findViewById(R.id.request_disposition_textview)!! - val webpageUrlTextView = alertDialog.findViewById(R.id.webpage_url_textview)!! - val requestUrlTextView = alertDialog.findViewById(R.id.request_url_textview)!! - val requestUrlWithSeparatorsTextView = alertDialog.findViewById(R.id.request_url_with_separators_textview)!! - val truncatedRequestUrlTextView = alertDialog.findViewById(R.id.truncated_request_url_textview)!! - val truncatedRequestUrlWithSeparatorsTextView = alertDialog.findViewById(R.id.truncated_request_url_with_separators_textview)!! - val thirdPartyRequestTextView = alertDialog.findViewById(R.id.third_party_request_textview)!! - val filterListEntryTextView = alertDialog.findViewById(R.id.filterlist_entry_textview)!! - val filterListLabelTextView = alertDialog.findViewById(R.id.filterlist_label_textview)!! - val filterListTextView = alertDialog.findViewById(R.id.filterlist_textview)!! - val sublistLabelTextView = alertDialog.findViewById(R.id.sublist_label_textview)!! - val sublistTextView = alertDialog.findViewById(R.id.sublist_textview)!! - val appliedEntryListLabelTextView = alertDialog.findViewById(R.id.applied_entry_list_label_textview)!! - val appliedEntryListTextView = alertDialog.findViewById(R.id.applied_entry_list_textview)!! - val domainLabelTextView = alertDialog.findViewById(R.id.domain_label_textview)!! - val domainTextView = alertDialog.findViewById(R.id.domain_textview)!! - val domainListLabelTextView = alertDialog.findViewById(R.id.domain_list_label_textview)!! - val domainListTextView = alertDialog.findViewById(R.id.domain_list_textview)!! - val thirdPartyFilterListEntryLabelTextView = alertDialog.findViewById(R.id.third_party_filter_list_entry_label_textview)!! - val thirdPartyFilterListEntryTextView = alertDialog.findViewById(R.id.third_party_filter_list_entry_textview)!! - val initialMatchLabelTextView = alertDialog.findViewById(R.id.initial_match_label_textview)!! - val initialMatchTextView = alertDialog.findViewById(R.id.initial_match_textview)!! - val finalMatchLabelTextView = alertDialog.findViewById(R.id.final_match_label_textview)!! - val finalMatchTextView = alertDialog.findViewById(R.id.final_match_textview)!! - val appliedFilterOptionsLabelTextView = alertDialog.findViewById(R.id.applied_filter_options_label_textview)!! - val appliedFilterOptionsTextView = alertDialog.findViewById(R.id.applied_filter_options_textview)!! - val originalFilterOptionsLabelTextView = alertDialog.findViewById(R.id.original_filter_options_label_textview)!! - val originalFilterOptionsTextView = alertDialog.findViewById(R.id.original_filter_options_textview)!! - val originalEntryLabelTextView = alertDialog.findViewById(R.id.original_entry_label_textview)!! - val originalEntryTextView = alertDialog.findViewById(R.id.original_entry_textview)!! - val previousButton = alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE) - val nextButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE) + requestDispositionTextView = alertDialog.findViewById(R.id.request_disposition_textview)!! + webpageUrlTextView = alertDialog.findViewById(R.id.webpage_url_textview)!! + requestUrlTextView = alertDialog.findViewById(R.id.request_url_textview)!! + requestUrlWithSeparatorsTextView = alertDialog.findViewById(R.id.request_url_with_separators_textview)!! + truncatedRequestUrlTextView = alertDialog.findViewById(R.id.truncated_request_url_textview)!! + truncatedRequestUrlWithSeparatorsTextView = alertDialog.findViewById(R.id.truncated_request_url_with_separators_textview)!! + thirdPartyRequestTextView = alertDialog.findViewById(R.id.third_party_request_textview)!! + filterListEntryTextView = alertDialog.findViewById(R.id.filterlist_entry_textview)!! + filterListLabelTextView = alertDialog.findViewById(R.id.filterlist_label_textview)!! + filterListTextView = alertDialog.findViewById(R.id.filterlist_textview)!! + sublistLabelTextView = alertDialog.findViewById(R.id.sublist_label_textview)!! + sublistTextView = alertDialog.findViewById(R.id.sublist_textview)!! + appliedEntryListLabelTextView = alertDialog.findViewById(R.id.applied_entry_list_label_textview)!! + appliedEntryListTextView = alertDialog.findViewById(R.id.applied_entry_list_textview)!! + domainLabelTextView = alertDialog.findViewById(R.id.domain_label_textview)!! + domainTextView = alertDialog.findViewById(R.id.domain_textview)!! + domainListLabelTextView = alertDialog.findViewById(R.id.domain_list_label_textview)!! + domainListTextView = alertDialog.findViewById(R.id.domain_list_textview)!! + thirdPartyFilterListEntryLabelTextView = alertDialog.findViewById(R.id.third_party_filter_list_entry_label_textview)!! + thirdPartyFilterListEntryTextView = alertDialog.findViewById(R.id.third_party_filter_list_entry_textview)!! + initialMatchLabelTextView = alertDialog.findViewById(R.id.initial_match_label_textview)!! + initialMatchTextView = alertDialog.findViewById(R.id.initial_match_textview)!! + finalMatchLabelTextView = alertDialog.findViewById(R.id.final_match_label_textview)!! + finalMatchTextView = alertDialog.findViewById(R.id.final_match_textview)!! + appliedFilterOptionsLabelTextView = alertDialog.findViewById(R.id.applied_filter_options_label_textview)!! + appliedFilterOptionsTextView = alertDialog.findViewById(R.id.applied_filter_options_textview)!! + originalFilterOptionsLabelTextView = alertDialog.findViewById(R.id.original_filter_options_label_textview)!! + originalFilterOptionsTextView = alertDialog.findViewById(R.id.original_filter_options_textview)!! + originalEntryLabelTextView = alertDialog.findViewById(R.id.original_entry_label_textview)!! + originalEntryTextView = alertDialog.findViewById(R.id.original_entry_textview)!! + previousButton = alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE) + nextButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE) + + // Set the previous button on click listener. This must be done here instead of in the builder to prevent the dialog from closing when the button is pressed. + previousButton.setOnClickListener { + // Decrement the current ID. + --currentId + + // Repopulate the dialog. + populateDialog() + } + + // Set the next button on click listener. This must be done here instead of in the builder to prevent the dialog from closing when the button is pressed. + nextButton.setOnClickListener { + // Increment the current ID. + ++currentId + + // Repopulate the dialog. + populateDialog() + } + + // Get a handle for the context. + val context = requireContext() + + // Get handles for the colors. + blueColor = getColor(context, R.color.requests_blue_background) + redColor = getColor(context, R.color.red_background) + transparentColor = getColor(context, R.color.transparent) + yellowColor = getColor(context, R.color.yellow_background) + + // Populate the dialog. + populateDialog() + + // Return the alert dialog. + return alertDialog + } + + private fun convertListToString(stringList: List, separatorString: String): String { + // Create a string builder. + val stringBuilder = StringBuilder() + + // Add each of the strings from the list. + for (string in stringList) { + // Append four space if the string builder is already populated. + if (stringBuilder.isNotEmpty()) + stringBuilder.append(separatorString) + + // Append the string. + stringBuilder.append(string) + } + + // Return the string. + return stringBuilder.toString() + } + + private fun populateDialog() { + // Get the request data class. The array list is 0 based. + val requestDataClass = requestDataClassArrayList[currentId - 1] + + // Update the dialog title. + alertDialog.setTitle(resources.getString(R.string.request_details) + " - " + currentId) // Disable the previous button if the first resource request is displayed. - previousButton.isEnabled = (id != 1) + previousButton.isEnabled = (currentId != 1) // Disable the next button if the last resource request is displayed. - nextButton.isEnabled = !isLastRequest + nextButton.isEnabled = (currentId != requestDataClassArrayList.size) - // Get handles for the colors. - blueColor = getColor(requireContext(), R.color.requests_blue_background) - redColor = getColor(requireContext(), R.color.red_background) - val yellowColor = getColor(requireContext(), R.color.yellow_background) + // Reset the matched URL background colors. + requestUrlTextView.setBackgroundColor(transparentColor) + truncatedRequestUrlTextView.setBackgroundColor(transparentColor) + requestUrlWithSeparatorsTextView.setBackgroundColor(transparentColor) + truncatedRequestUrlWithSeparatorsTextView.setBackgroundColor(transparentColor) // Set the request disposition text and associated backgrounds. when (requestDataClass.disposition) { @@ -194,6 +266,9 @@ class ViewRequestDialog : DialogFragment() { // Set the request disposition background color to be transparent. requestDispositionTextView.setBackgroundColor(getColor(requireContext(), R.color.transparent)) + + // Reset the third-party request background color. + thirdPartyRequestTextView.setBackgroundColor(transparentColor) } RequestDisposition.Allowed -> { @@ -226,11 +301,17 @@ class ViewRequestDialog : DialogFragment() { // Set the domain list text view background color to be the same as the domain text view background color. setFilterListBackgroundColor(requestDataClass.filterListDomain, domainListTextView, true) - // Set the matching text view background color to blue if they are populated. + // Set the initial match text view background color to blue if it is populated. if (requestDataClass.filterListInitialMatch) initialMatchTextView.setBackgroundColor(blueColor) + else + initialMatchTextView.setBackgroundColor(transparentColor) + + // Set the final match text view background color to blue if it is populated. if (requestDataClass.filterListFinalMatch) finalMatchTextView.setBackgroundColor(blueColor) + else + finalMatchTextView.setBackgroundColor(transparentColor) } RequestDisposition.Blocked -> { @@ -263,11 +344,17 @@ class ViewRequestDialog : DialogFragment() { // Set the domain list text view background color to be the same as the domain text view background color. setFilterListBackgroundColor(requestDataClass.filterListDomain, domainListTextView, false) - // Set the matching text view background color to red if they are populated. + // Set the initial match text view background color to red if it is populated. if (requestDataClass.filterListInitialMatch) initialMatchTextView.setBackgroundColor(redColor) + else + initialMatchTextView.setBackgroundColor(transparentColor) + + // Set the final match text view background color to red if it is populated. if (requestDataClass.filterListFinalMatch) finalMatchTextView.setBackgroundColor(redColor) + else + finalMatchTextView.setBackgroundColor(transparentColor) } RequestDisposition.ThirdParty -> { @@ -324,6 +411,31 @@ class ViewRequestDialog : DialogFragment() { originalEntryLabelTextView.visibility = View.GONE originalEntryTextView.visibility = View.GONE } else { // A blocked or allowed request. + // Show the extra views. + filterListEntryTextView.visibility = View.VISIBLE + filterListLabelTextView.visibility = View.VISIBLE + filterListTextView.visibility = View.VISIBLE + sublistLabelTextView.visibility = View.VISIBLE + sublistTextView.visibility = View.VISIBLE + appliedEntryListLabelTextView.visibility = View.VISIBLE + appliedEntryListTextView.visibility = View.VISIBLE + domainLabelTextView.visibility = View.VISIBLE + domainTextView.visibility = View.VISIBLE + domainListLabelTextView.visibility = View.VISIBLE + domainListTextView.visibility = View.VISIBLE + thirdPartyFilterListEntryLabelTextView.visibility = View.VISIBLE + thirdPartyFilterListEntryTextView.visibility = View.VISIBLE + initialMatchLabelTextView.visibility = View.VISIBLE + initialMatchTextView.visibility = View.VISIBLE + finalMatchLabelTextView.visibility = View.VISIBLE + finalMatchTextView.visibility = View.VISIBLE + appliedFilterOptionsLabelTextView.visibility = View.VISIBLE + appliedFilterOptionsTextView.visibility = View.VISIBLE + originalFilterOptionsLabelTextView.visibility = View.VISIBLE + originalFilterOptionsTextView.visibility = View.VISIBLE + originalEntryLabelTextView.visibility = View.VISIBLE + originalEntryTextView.visibility = View.VISIBLE + // Populate the filter list. when (requestDataClass.filterList) { FilterList.UltraPrivacy -> filterListTextView.text = getString(R.string.ultraprivacy) @@ -361,27 +473,6 @@ class ViewRequestDialog : DialogFragment() { originalFilterOptionsTextView.text = requestDataClass.filterListOriginalFilterOptionsString originalEntryTextView.text = requestDataClass.filterListOriginalEntryString } - - // Return the alert dialog. - return alertDialog - } - - private fun convertListToString(stringList: List, separatorString: String): String { - // Create a string builder. - val stringBuilder = StringBuilder() - - // Add each of the strings from the list. - for (string in stringList) { - // Append four space if the string builder is already populated. - if (stringBuilder.isNotEmpty()) - stringBuilder.append(separatorString) - - // Append the string. - stringBuilder.append(string) - } - - // Return the string. - return stringBuilder.toString() } private fun populateFilterOption(filterOptionDisposition: FilterOptionDisposition) : String { @@ -396,15 +487,17 @@ class ViewRequestDialog : DialogFragment() { private fun setFilterListBackgroundColor(filterOptionDisposition: FilterOptionDisposition, textView: TextView, isAllowList: Boolean) { // Set the text view background. if (isAllowList) { // This is an allow list. - if (filterOptionDisposition == FilterOptionDisposition.Apply) // The filter option is applied on an allow list, set the background to blue. - textView.setBackgroundColor(blueColor) - else if (filterOptionDisposition == FilterOptionDisposition.Override) // The filter option is overridden on an allow list, set the background to red. - textView.setBackgroundColor(redColor) + when (filterOptionDisposition) { // For allow lists, set applied entries to blue and overridden entries to red. + FilterOptionDisposition.Null -> textView.setBackgroundColor(transparentColor) + FilterOptionDisposition.Apply -> textView.setBackgroundColor(blueColor) + FilterOptionDisposition.Override -> textView.setBackgroundColor(redColor) + } } else { // This is a block list. - if (filterOptionDisposition == FilterOptionDisposition.Apply) // The filter option is applied on a block list, set the background to be red. - textView.setBackgroundColor(redColor) - else if (filterOptionDisposition == FilterOptionDisposition.Override) // The filter option is overridden on a block list, set the background to be blue. - textView.setBackgroundColor(blueColor) + when (filterOptionDisposition) { // For block lists, set applied entries to red and overridden entries to blue. + FilterOptionDisposition.Null -> textView.setBackgroundColor(transparentColor) + FilterOptionDisposition.Apply -> textView.setBackgroundColor(redColor) + FilterOptionDisposition.Override -> textView.setBackgroundColor(blueColor) + } } } }