]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Add searching of filter lists. https://redmine.stoutner.com/issues/1293 master
authorSoren Stoutner <soren@stoutner.com>
Thu, 28 May 2026 21:17:10 +0000 (14:17 -0700)
committerSoren Stoutner <soren@stoutner.com>
Thu, 28 May 2026 21:17:10 +0000 (14:17 -0700)
17 files changed:
app/src/main/java/com/stoutner/privacybrowser/activities/FilterListsActivity.kt
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt
app/src/main/java/com/stoutner/privacybrowser/adapters/FilterListArrayAdapter.kt
app/src/main/java/com/stoutner/privacybrowser/dataclasses/FilterListEntryDataClass.kt
app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewFilterListEntryDialog.kt
app/src/main/res/layout/filter_lists_bottom_appbar.xml
app/src/main/res/layout/filter_lists_top_appbar.xml
app/src/main/res/layout/logcat_bottom_appbar.xml
app/src/main/res/layout/logcat_top_appbar.xml
app/src/main/res/layout/main_framelayout_bottom_appbar.xml
app/src/main/res/layout/main_framelayout_top_appbar.xml
app/src/main/res/layout/url_app_bar.xml
app/src/main/res/layout/view_headers_appbar_custom_view.xml
app/src/main/res/values-de/strings.xml
app/src/main/res/values-es/strings.xml
app/src/main/res/values-it/strings.xml
app/src/main/res/values-ru/strings.xml

index f07baf54695e7f038d5ef3bdee203075d6f063c0..92ef65919f111cb00fcd90166651ab84b8ce5c66 100644 (file)
@@ -24,9 +24,12 @@ import android.database.Cursor
 import android.database.MatrixCursor
 import android.os.Bundle
 import android.os.Handler
+import android.text.Editable
+import android.text.TextWatcher
 import android.view.View
 import android.view.WindowManager
 import android.widget.AdapterView
+import android.widget.EditText
 import android.widget.ListView
 import android.widget.Spinner
 import android.widget.TextView
@@ -41,6 +44,7 @@ import androidx.preference.PreferenceManager
 import com.stoutner.privacybrowser.R
 import com.stoutner.privacybrowser.adapters.FilterListArrayAdapter
 import com.stoutner.privacybrowser.dataclasses.FilterListDataClass
+import com.stoutner.privacybrowser.dataclasses.FilterListEntryDataClass
 import com.stoutner.privacybrowser.dialogs.ViewFilterListEntryDialog
 import com.stoutner.privacybrowser.helpers.easyListDataClass
 import com.stoutner.privacybrowser.helpers.easyPrivacyDataClass
@@ -74,9 +78,12 @@ class FilterListsActivity : AppCompatActivity(), ViewFilterListEntryDialog.ViewF
     private lateinit var filterListListView: ListView
     private lateinit var filterSublistSpinner: Spinner
 
-    // Define the class variables
+    // Define the class variables.
     private var filterSublistSpinnerSelectedPosition = 0
 
+    // Declare the class variables.
+    private lateinit var currentFilterListDataClassArrayAdapter: FilterListArrayAdapter
+
     public override fun onCreate(savedInstanceState: Bundle?) {
         // Get a handle for the shared preferences.
         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext)
@@ -117,11 +124,12 @@ class FilterListsActivity : AppCompatActivity(), ViewFilterListEntryDialog.ViewF
         appBarSpinner = findViewById(R.id.spinner)
         filterSublistSpinner = findViewById(R.id.filter_sublist_spinner)
         filterListListView = findViewById(R.id.filter_list_listview)
+        val searchEditText = findViewById<EditText>(R.id.search_edittext)
 
         // Store the activity context.
         activityContext = this
 
-        // Setup a matrix cursor for the app bar spinner.
+        // Set up a matrix cursor for the app bar spinner.
         val appBarSpinnerCursor = MatrixCursor(arrayOf("_id", "Filter List"))
         appBarSpinnerCursor.addRow(arrayOf<Any>(ULTRAPRIVACY, getString(R.string.ultraprivacy) + " - " + ultraPrivacyDataClass.versionString))
         appBarSpinnerCursor.addRow(arrayOf<Any>(ULTRALIST, getString(R.string.ultralist) + " - " + ultraListDataClass.versionString))
@@ -164,6 +172,7 @@ class FilterListsActivity : AppCompatActivity(), ViewFilterListEntryDialog.ViewF
             }
         }
 
+        // Handle taps on the filter sublist spinner dropdown.
         filterSublistSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
             override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                 // Update the filter sublist spinner selected position.
@@ -181,10 +190,14 @@ class FilterListsActivity : AppCompatActivity(), ViewFilterListEntryDialog.ViewF
                 }
 
                 // Get an adapter for the current filter list data class.
-                val currentFilterListDataClassArrayAdapter = FilterListArrayAdapter(activityContext, currentFilterListDataClass)
+                currentFilterListDataClassArrayAdapter = FilterListArrayAdapter(activityContext, currentFilterListDataClass)
 
                 // Populate the list view with the current filter list data class adapter.
                 filterListListView.adapter = currentFilterListDataClassArrayAdapter
+
+                // Reapply the search if the search edit is not empty.
+                if (searchEditText.text.isNotEmpty())
+                    currentFilterListDataClassArrayAdapter.filter.filter(searchEditText.text)
             }
 
             override fun onNothingSelected(parent: AdapterView<*>?) {
@@ -192,6 +205,22 @@ class FilterListsActivity : AppCompatActivity(), ViewFilterListEntryDialog.ViewF
             }
         }
 
+        // Search for the string in the list view whenever a character changes in the search edit text.
+        searchEditText.addTextChangedListener(object : TextWatcher {
+            override fun beforeTextChanged(charSequence: CharSequence, start: Int, count: Int, after: Int) {
+                // Do nothing.
+            }
+
+            override fun onTextChanged(charSequence: CharSequence, start: Int, before: Int, count: Int) {
+                // Do nothing.
+            }
+
+            override fun afterTextChanged(editable: Editable) {
+                // Search for the text in the list view.
+                currentFilterListDataClassArrayAdapter.filter.filter(editable.toString())
+            }
+        })
+
         // Listen for taps on entries in the list view.
         filterListListView.onItemClickListener = AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, position: Int, _: Long ->
             // Display the view filter list entry dialog.  the list view is 0 based, so the position must be incremented by 1.
@@ -258,9 +287,12 @@ class FilterListsActivity : AppCompatActivity(), ViewFilterListEntryDialog.ViewF
         // Determine if this is the last entry in the list.
         val isLastEntry = (id == filterListListView.count)
 
+        // Get the filter list entry data class.  The list is 0 based, so the position is the ID - 1.
+        val filterListEntryDataClass = filterListListView.getItemAtPosition(id - 1) as FilterListEntryDataClass
+
         // Create a view filter list entry dialog.
         val viewFilterListsEntryDialogFragment: DialogFragment = ViewFilterListEntryDialog.entry(id, isLastEntry, appBarSpinner.selectedItemPosition,
-                                                                                                 filterSublistSpinner.selectedItemPosition)
+                                                                                                 filterSublistSpinner.selectedItemPosition, filterListEntryDataClass)
 
         // Make it so.
         viewFilterListsEntryDialogFragment.show(supportFragmentManager, getString(R.string.filterlist_entry))
index 846aef5e81a5021a43e9ebe8a1060c5733accfad..04295ede39ddf927570043122cd9dcdd274fad05 100644 (file)
@@ -4385,20 +4385,6 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             }
         })
 
-        // Set the `check mark` button for the find on page edit text keyboard to close the soft keyboard.
-        findOnPageEditText.setOnKeyListener { _: View?, keyCode: Int, keyEvent: KeyEvent ->
-            if ((keyEvent.action == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {  // The `enter` key was pressed.
-                // Hide the soft keyboard.
-                inputMethodManager.hideSoftInputFromWindow(currentWebView!!.windowToken, 0)
-
-                // Consume the event.
-                return@setOnKeyListener true
-            } else {  // A different key was pressed.
-                // Do not consume the event.
-                return@setOnKeyListener false
-            }
-        }
-
         // Implement swipe to refresh.
         swipeRefreshLayout.setOnRefreshListener {
             // Reload the website.
index a7244394d6ce906ad564a160d36e65858e2b68a2..6d0c406f645ada94f39df9ee4bab78ecd7d91a26 100644 (file)
@@ -24,13 +24,34 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ArrayAdapter
+import android.widget.Filter
 import android.widget.TextView
 
 import com.stoutner.privacybrowser.R
 import com.stoutner.privacybrowser.dataclasses.FilterListEntryDataClass
 
+import java.util.Locale.getDefault
+
 // `0` is the `textViewResourceId`, which is unused in this implementation.
-class FilterListArrayAdapter(context: Context, filterListEntryDataClassList: List<FilterListEntryDataClass>) : ArrayAdapter<FilterListEntryDataClass>(context, 0, filterListEntryDataClassList) {
+class FilterListArrayAdapter(context: Context, private val entireFilterListEntryDataClassList: List<FilterListEntryDataClass>) : ArrayAdapter<FilterListEntryDataClass>(context, 0, entireFilterListEntryDataClassList) {
+    // Define the class variables.
+    private var searchedFilterListEntryDataClassList = entireFilterListEntryDataClassList
+
+    override fun getCount(): Int {
+        // Return the searched filter list entry data class list size.
+        return searchedFilterListEntryDataClassList.size
+    }
+
+    override fun getItem(position: Int): FilterListEntryDataClass {
+        // Return the searched filter list entry data class list item.
+        return searchedFilterListEntryDataClassList[position]
+    }
+
+    override fun getItemId(position: Int): Long {
+        // Return the position as a long.
+        return position.toLong()
+    }
+
     override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
         // Copy the input view to a new view.
         var newView = convertView
@@ -44,7 +65,7 @@ class FilterListArrayAdapter(context: Context, filterListEntryDataClassList: Lis
         val originalEntryTextView = newView!!.findViewById<TextView>(R.id.original_entry_textview)
 
         // Get this filter list entry data class.
-        val filterListEntryDataClass = getItem(position)!!
+        val filterListEntryDataClass = getItem(position)
 
         // The ID is one greater than the position because it is 0 based.
         val id = position + 1
@@ -55,4 +76,41 @@ class FilterListArrayAdapter(context: Context, filterListEntryDataClassList: Lis
         // Return the modified view.
         return newView
     }
+
+    override fun getFilter(): Filter {
+        // Return the custom filter.
+        return object : Filter() {
+            override fun performFiltering(charSequence: CharSequence?): FilterResults {
+                // Get the search string, converting to lower case.
+                val searchString = charSequence?.toString()?.lowercase(getDefault())
+
+                // Create a new filter results.
+                val filterResults = FilterResults()
+
+                // Filter the list.
+                filterResults.values = if (searchString.isNullOrEmpty()) {  // The search string is null or empty.
+                    // Return the entire filter list entry data class list.
+                    entireFilterListEntryDataClassList
+                } else {  // The search string contains data.
+                    // Filter the list by the original entry string.
+                    entireFilterListEntryDataClassList.filter {
+                        it.originalEntryString.lowercase(getDefault()).contains(searchString)
+                    }
+                }
+
+                // Return the filter results.
+                return filterResults
+            }
+
+            override fun publishResults(charSequence: CharSequence?, filterResults: FilterResults) {
+                @Suppress("UNCHECKED_CAST")
+                // Store the searched data filter list entry data class list.
+                searchedFilterListEntryDataClassList = filterResults.values as List<FilterListEntryDataClass>
+
+                // Update the GUI.
+                notifyDataSetChanged()
+            }
+
+        }
+    }
 }
index 3af1a6f32b3e018315a505290f7df0fee65c8d0b..5e4427cb437711d9f315a577ba19c90138daf613 100644 (file)
 
 package com.stoutner.privacybrowser.dataclasses
 
+import android.os.Parcelable
+
+import kotlinx.parcelize.Parcelize
+
 enum class FilterOptionDisposition(val int: Int) {
     Null (0),
     Apply (1),
     Override (2)
 }
 
+@Parcelize
 data class FilterListEntryDataClass (
     // The strings.
     var originalEntryString: String = "",
@@ -48,4 +53,4 @@ data class FilterListEntryDataClass (
     var filterList: FilterList = FilterList.UltraPrivacy,
     var sublist: Sublist = Sublist.MainAllowList,
     var thirdParty: FilterOptionDisposition = FilterOptionDisposition.Null
-)
+) : Parcelable
index 7ce0049e4aeb92096eb10bb03a09e2a2c9841e29..304596a80f5a6683023998638a374ff7a608e392 100644 (file)
@@ -43,20 +43,15 @@ import com.stoutner.privacybrowser.activities.REGULAR_EXPRESSION_ALLOWLIST
 import com.stoutner.privacybrowser.activities.REGULAR_EXPRESSION_BLOCKLIST
 import com.stoutner.privacybrowser.activities.ULTRALIST
 import com.stoutner.privacybrowser.activities.ULTRAPRIVACY
-import com.stoutner.privacybrowser.dataclasses.FilterListDataClass
 import com.stoutner.privacybrowser.dataclasses.FilterListEntryDataClass
 import com.stoutner.privacybrowser.dataclasses.FilterOptionDisposition
-import com.stoutner.privacybrowser.helpers.easyListDataClass
-import com.stoutner.privacybrowser.helpers.easyPrivacyDataClass
-import com.stoutner.privacybrowser.helpers.fanboysAnnoyanceDataClass
-import com.stoutner.privacybrowser.helpers.ultraListDataClass
-import com.stoutner.privacybrowser.helpers.ultraPrivacyDataClass
 
 // Define the class constants.
 private const val ENTRY_ID = "entry_id"
 private const val FILTER_LIST_INT = "filter_list_int"
 private const val IS_LAST_ENTRY = "is_last_entry"
 private const val SUBLIST_INT = "sublist_int"
+private const val FILTER_LIST_ENTRY_DATA_CLASS = "filter_list_entry_data_class"
 
 // Define the private variables.
 private var blueColor = 0
@@ -64,7 +59,7 @@ private var redColor = 0
 
 class ViewFilterListEntryDialog : DialogFragment() {
     companion object {
-        fun entry(entryId: Int, isLastEntry: Boolean, filterListInt: Int, sublistInt: Int): ViewFilterListEntryDialog {
+        fun entry(entryId: Int, isLastEntry: Boolean, filterListInt: Int, sublistInt: Int, filterListEntryDataClass: FilterListEntryDataClass): ViewFilterListEntryDialog {
             // Create a bundle.
             val bundle = Bundle()
 
@@ -73,6 +68,7 @@ class ViewFilterListEntryDialog : DialogFragment() {
             bundle.putBoolean(IS_LAST_ENTRY, isLastEntry)
             bundle.putInt(FILTER_LIST_INT, filterListInt)
             bundle.putInt(SUBLIST_INT, sublistInt)
+            bundle.putParcelable(FILTER_LIST_ENTRY_DATA_CLASS, filterListEntryDataClass)
 
             // Create a new instance of the view filter list entry dialog.
             val viewFilterListEntryDialog = ViewFilterListEntryDialog()
@@ -111,6 +107,8 @@ class ViewFilterListEntryDialog : DialogFragment() {
         val isLastEntry = requireArguments().getBoolean(IS_LAST_ENTRY)
         val filterListInt = requireArguments().getInt(FILTER_LIST_INT)
         val sublistInt = requireArguments().getInt(SUBLIST_INT)
+        // The deprecated `getParcelable()` can be replaced once the minimum API >= 33.
+        @Suppress("DEPRECATION") val filterListEntryDataClass = requireArguments().getParcelable<FilterListEntryDataClass>(FILTER_LIST_ENTRY_DATA_CLASS)!!
 
         // Use an alert dialog builder to create the alert dialog.
         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
@@ -176,9 +174,6 @@ class ViewFilterListEntryDialog : DialogFragment() {
         // Disable the next button if the last filter list entry is displayed.
         nextButton.isEnabled = !isLastEntry
 
-        // Get the filter list entry data class.
-        val filterListEntryDataClass = getFilterListEntryDataClass(filterListInt, sublistInt, entryId)
-
         // Populate the filter list entry views.
         filterListTextView.text = getFilterListName(filterListInt)
         sublistTextView.text = getSublistName(sublistInt)
@@ -259,31 +254,6 @@ class ViewFilterListEntryDialog : DialogFragment() {
         return stringBuilder.toString()
     }
 
-    private fun getFilterListEntryDataClass(filterListInt: Int, sublistInt: Int, entryId: Int) : FilterListEntryDataClass {
-        // Get the filter list entry according to the filter list.
-        return when (filterListInt) {
-            ULTRAPRIVACY -> getFilterListEntryDataClassFromSublist(ultraPrivacyDataClass, sublistInt, entryId)
-            ULTRALIST -> getFilterListEntryDataClassFromSublist(ultraListDataClass, sublistInt, entryId)
-            EASYPRIVACY -> getFilterListEntryDataClassFromSublist(easyPrivacyDataClass, sublistInt, entryId)
-            EASYLIST -> getFilterListEntryDataClassFromSublist(easyListDataClass, sublistInt, entryId)
-            FANBOYS_ANNOYANCE_LIST -> getFilterListEntryDataClassFromSublist(fanboysAnnoyanceDataClass!!, sublistInt, entryId)
-            else -> getFilterListEntryDataClassFromSublist(FilterListDataClass(), sublistInt, entryId)
-        }
-    }
-
-    private fun getFilterListEntryDataClassFromSublist(filterListDataClass: FilterListDataClass, sublistInt: Int, entryId: Int) : FilterListEntryDataClass {
-        // Return the filter list entry data class.  The list is 0 based, so the entry ID needs to be decremented by 1.
-        return when (sublistInt) {
-            MAIN_ALLOWLIST -> filterListDataClass.mainAllowList[entryId - 1]
-            INITIAL_DOMAIN_ALLOWLIST -> filterListDataClass.initialDomainAllowList[entryId - 1]
-            REGULAR_EXPRESSION_ALLOWLIST -> filterListDataClass.regularExpressionAllowList[entryId - 1]
-            MAIN_BLOCKLIST -> filterListDataClass.mainBlockList[entryId - 1]
-            INITIAL_DOMAIN_BLOCKLIST -> filterListDataClass.initialDomainBlockList[entryId - 1]
-            REGULAR_EXPRESSION_BLOCKLIST -> filterListDataClass.regularExpressionBlockList[entryId - 1]
-            else -> FilterListEntryDataClass()
-        }
-    }
-
     private fun getFilterListName(filterListInt: Int) : String {
         // Return the filter list name.
         return when (filterListInt) {
index 43cd95aafe46097a4c8c0a99aadf90189cded7c5..b8c47f945fc26e22098977f94377acd2aa6319e5 100644 (file)
@@ -2,7 +2,7 @@
 
 <!--
   SPDX-License-Identifier: GPL-3.0-or-later
-  SPDX-FileCopyrightText: 2025 Soren Stoutner <soren@stoutner.com>
+  SPDX-FileCopyrightText: 2025-2026 Soren Stoutner <soren@stoutner.com>
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
 
             android:background="?android:attr/colorBackground"
             android:theme="@style/PrivacyBrowserAppBar" >
 
+            <!-- `android:imeOptions="actionDone"` sets the keyboard to have a `check mark` key instead of a `new line` key. -->
+            <EditText
+                android:id="@+id/search_edittext"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:layout_marginStart="7dp"
+                android:layout_marginEnd="15dp"
+                android:hint="@string/search"
+                android:inputType="text"
+                android:imeOptions="actionDone"
+                android:importantForAutofill="no" />
+
             <LinearLayout
                 android:layout_height="wrap_content"
                 android:layout_width="match_parent"
index a67562e56bda292d942812f1086591317e84f924..17c97a71a355c260179e09575360812614b24ffb 100644 (file)
@@ -2,7 +2,7 @@
 
 <!--
   SPDX-License-Identifier: GPL-3.0-or-later
-  SPDX-FileCopyrightText: 2025 Soren Stoutner <soren@stoutner.com>
+  SPDX-FileCopyrightText: 2025-2026 Soren Stoutner <soren@stoutner.com>
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
 
                     android:layout_width="wrap_content"
                     android:layout_marginBottom="15dp" />
             </LinearLayout>
+
+            <!-- `android:imeOptions="actionDone"` sets the keyboard to have a `check mark` key instead of a `new line` key. -->
+            <EditText
+                android:id="@+id/search_edittext"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:layout_marginStart="7dp"
+                android:layout_marginEnd="15dp"
+                android:hint="@string/search"
+                android:inputType="text"
+                android:imeOptions="actionDone"
+                android:importantForAutofill="no" />
         </com.google.android.material.appbar.AppBarLayout>
 
         <!-- `android:dividerHeight` must be at least `1dp` or the list view is inconsistent in calculating how many entries are displayed. -->
index e88f3c9f3367f0598da55439a7f33e6cf434b148..6bd72f5ab7547c2070847f22bfc33e7d4c1ba1d2 100644 (file)
@@ -2,7 +2,7 @@
 
 <!--
   SPDX-License-Identifier: GPL-3.0-or-later
-  SPDX-FileCopyrightText: 2018-2024 Soren Stoutner <soren@stoutner.com>
+  SPDX-FileCopyrightText: 2018-2024, 2026 Soren Stoutner <soren@stoutner.com>
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
 
@@ -23,7 +23,6 @@
 <androidx.coordinatorlayout.widget.CoordinatorLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     android:fitsSystemWindows="true" >
@@ -79,7 +78,7 @@
                     android:lines="1"
                     android:imeOptions="actionDone"
                     android:inputType="text"
-                    tools:ignore="Autofill" />
+                    android:importantForAutofill="no" />
 
                 <TextView
                     android:id="@+id/search_count_textview"
index cbdbb3c64b46c3eeda70578142cd7923f7849eb9..b6ff058f7b35adf2608f8dc23f60d5bea58d6687 100644 (file)
@@ -2,7 +2,7 @@
 
 <!--
   SPDX-License-Identifier: GPL-3.0-or-later
-  SPDX-FileCopyrightText: 2018-2024 Soren Stoutner <soren@stoutner.com>
+  SPDX-FileCopyrightText: 2018-2024, 2026 Soren Stoutner <soren@stoutner.com>
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
 
@@ -23,7 +23,6 @@
 <androidx.coordinatorlayout.widget.CoordinatorLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     android:fitsSystemWindows="true" >
@@ -66,7 +65,7 @@
                     android:lines="1"
                     android:imeOptions="actionDone"
                     android:inputType="text"
-                    tools:ignore="Autofill" />
+                    android:importantForAutofill="no" />
 
                 <TextView
                     android:id="@+id/search_count_textview"
index e47cf1a6f2bbc479aa16bb5991928f2bb40a38d5..bad73cff7e5f661b1c61da64ddcfb59781ba82c6 100644 (file)
@@ -2,7 +2,7 @@
 
 <!--
   SPDX-License-Identifier: GPL-3.0-or-later
-  SPDX-FileCopyrightText: 2015-2017, 2019-2024 Soren Stoutner <soren@stoutner.com>
+  SPDX-FileCopyrightText: 2015-2017, 2019-2024, 2026 Soren Stoutner <soren@stoutner.com>
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
 
                                 android:lines="1"
                                 android:imeOptions="actionDone"
                                 android:inputType="text"
-                                tools:ignore="Autofill" />
+                                android:importantForAutofill="no" />
 
                             <TextView
                                 android:id="@+id/find_on_page_count_textview"
index 10f6ca1a20f14f67b1c65ae58168ff644cd019a4..68ca2515f0670568b73cee15f21b3dc97ea12561 100644 (file)
@@ -2,7 +2,7 @@
 
 <!--
   SPDX-License-Identifier: GPL-3.0-or-later
-  SPDX-FileCopyrightText: 2015-2017, 2019-2025 Soren Stoutner <soren@stoutner.com>
+  SPDX-FileCopyrightText: 2015-2017, 2019-2026 Soren Stoutner <soren@stoutner.com>
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
 
@@ -79,7 +79,7 @@
                             android:lines="1"
                             android:imeOptions="actionDone"
                             android:inputType="text"
-                            tools:ignore="Autofill" />
+                            android:importantForAutofill="no" />
 
                         <TextView
                             android:id="@+id/find_on_page_count_textview"
index 4d1883da9b721824499fe17499dc1cd808929b83..ac058b011528206e63ff912fbe64c59d2c08a184 100644 (file)
@@ -1,22 +1,23 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright 2015-2019, 2022-2024 Soren Stoutner <soren@stoutner.com>.
+  SPDX-License-Identifier: GPL-3.0-or-later
+  SPDX-FileCopyrightText: 2015-2019, 2022-2024, 2026 Soren Stoutner <soren@stoutner.com>
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/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
-  the Free Software Foundation, either version 3 of the License, or
-  (at your option) any later version.
+  This program is free software: you can redistribute it and/or modify it under
+  the terms of the GNU General Public License as published by the Free Software
+  Foundation, either version 3 of the License, or (at your option) any later
+  version.
 
-  Privacy Browser Android is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+  details.
 
-  You should have received a copy of the GNU General Public License
-  along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>. -->
+  You should have received a copy of the GNU General Public License along with
+  this program.  If not, see <https://www.gnu.org/licenses/>. -->
 
 
 <LinearLayout
@@ -53,6 +54,6 @@
         android:imeOptions="actionGo"
         android:inputType="textUri"
         android:selectAllOnFocus="true"
-        android:autofillHints="" />
+        android:importantForAutofill="no" />
     </RelativeLayout>
 </LinearLayout>
index 0dc83f6fafffb4ae0c05eab3e27546c7fb212d36..dfe806006ef8daa8f62648e37de6405d4c8a86ab 100644 (file)
@@ -1,27 +1,27 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright 2015-2020, 2022-2024 Soren Stoutner <soren@stoutner.com>.
+  SPDX-License-Identifier: GPL-3.0-or-later
+  SPDX-FileCopyrightText: 2015-2020, 2022-2024, 2026 Soren Stoutner <soren@stoutner.com>
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/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
-  the Free Software Foundation, either version 3 of the License, or
-  (at your option) any later version.
+  This program is free software: you can redistribute it and/or modify it under
+  the terms of the GNU General Public License as published by the Free Software
+  Foundation, either version 3 of the License, or (at your option) any later
+  version.
 
-  Privacy Browser Android is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+  details.
 
-  You should have received a copy of the GNU General Public License
-  along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>. -->
+  You should have received a copy of the GNU General Public License along with
+  this program.  If not, see <https://www.gnu.org/licenses/>. -->
 
 <!-- `android:imeOptions="actionGo"` sets the keyboard to have a `go` key instead of a `new line` key. `android:inputType="textUri"` disables spell check in the `EditText`. -->
 <EditText
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/url_edittext"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
@@ -29,4 +29,4 @@
     android:imeOptions="actionGo"
     android:inputType="textUri"
     android:selectAllOnFocus="true"
-    tools:ignore="Autofill" />
+    android:importantForAutofill="no" />
index d8c190b551427140cdbbf2cccdaee70165cfc543..13254331604392d7ecc52e7de1f6591c7eca00d8 100644 (file)
     <string name="gui">Benutzer-Oberfläche</string>
 
     <!-- Proxy. -->
+    <string name="orbot_or_tor_services_not_installed_title">Orbot oder Tor-Services nicht installiert</string>
+    <string name="orbot_or_tor_services_not_installed_message">Die Nutzung eines Proxys über Tor steht erst zur Verfügung, wenn die Apps "Orbot" oder "TorServices" auf Ihrem Gerät installiert sind.</string>
     <string name="i2p_not_installed_title">I2P ist nicht installiert</string>
     <string name="i2p_not_installed_message">Der I2P-Proxy kann erst nach Installation der I2P-App genutzt werden.</string>
     <string name="custom_proxy_invalid">Die benutzerdefinierte Proxy-URL ist ungültig.</string>
index 7967a23c3d462b8a8cc26a18aba3312a3369f086..97d17a653077f595712b52669e1112fb58dcc658 100644 (file)
     <string name="gui">Interfaz</string>
 
     <!-- Proxy. -->
+    <string name="orbot_or_tor_services_not_installed_title">Orbot o TorServices No Instalados</string>
+    <string name="orbot_or_tor_services_not_installed_message">El uso de Tor como proxy no funcionará a menos que tenga instalada la aplicación Orbot o TorServices.</string>
     <string name="i2p_not_installed_title">I2P No Instalado</string>
     <string name="i2p_not_installed_message">El proxy a través de I2P no funcionará a menos que la aplicación I2P esté instalada.</string>
     <string name="custom_proxy_invalid">La URL del proxy personalizado no es válida.</string>
index 09e33bcf1c1bd25a90d152215b569c2e47cb4581..d489eb8c111e6c98f3f8b8bac480640caf5e34ef 100644 (file)
     <string name="gui">Interfaccia</string>
 
     <!-- Proxy. -->
+    <string name="orbot_or_tor_services_not_installed_title">Orbot or TorServices non installati</string>
+    <string name="orbot_or_tor_services_not_installed_message">Il Proxy attraverso Tor non funziona se non sono installate le app Orbot o TorServices.</string>
     <string name="i2p_not_installed_title">I2P Non Installato</string>
     <string name="i2p_not_installed_message">Il Proxy con I2P non funziona se non è installata la app I2P.</string>
     <string name="custom_proxy_invalid">La URL del proxy personalizzato non è valida.</string>
index 4baf0c5c90883113e74aa2f8cb7839430dcc50f7..4a55575452dce40d7ff4f87ea889ea9a4a03b354 100644 (file)
     <string name="gui">Интерфейс</string>
 
     <!-- Proxy. -->
+    <string name="orbot_or_tor_services_not_installed_title">Orbot или TorServices не установлены</string>
+    <string name="orbot_or_tor_services_not_installed_message">Проксирование через Tor работает только если установлено приложение Orbot или TorServices.</string>
     <string name="i2p_not_installed_title">I2P не установлен</string>
     <string name="i2p_not_installed_message">Прокси через I2P работать не будет, если приложение I2P не установлено.</string>
     <string name="custom_proxy_invalid">URL пользовательского прокси недействителен.</string>