]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/commitdiff
Add a scroll to top/bottom navigation view entry. https://redmine.stoutner.com/issue...
authorSoren Stoutner <soren@stoutner.com>
Wed, 25 Oct 2023 19:47:26 +0000 (12:47 -0700)
committerSoren Stoutner <soren@stoutner.com>
Wed, 25 Oct 2023 19:47:26 +0000 (12:47 -0700)
app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt
app/src/main/java/com/stoutner/privacybrowser/activities/ViewHeadersActivity.kt
app/src/main/java/com/stoutner/privacybrowser/helpers/UrlHelper.kt
app/src/main/res/layout/view_headers_bottom_appbar.xml
app/src/main/res/layout/view_headers_top_appbar.xml
app/src/main/res/menu/webview_navigation_menu_bottom_appbar.xml
app/src/main/res/menu/webview_navigation_menu_top_appbar.xml
app/src/main/res/values-es/strings.xml
app/src/main/res/values-it/strings.xml
app/src/main/res/values/strings.xml

index 2c7560d6c459a90370f0bed7f6f77a2fdefa81ec..b0d20e5909124f42c128329595f27f5bc42dd67e 100644 (file)
@@ -283,6 +283,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
     private lateinit var navigationForwardMenuItem: MenuItem
     private lateinit var navigationHistoryMenuItem: MenuItem
     private lateinit var navigationRequestsMenuItem: MenuItem
+    private lateinit var navigationScrollToBottomMenuItem: MenuItem
     private lateinit var navigationView: NavigationView
     private lateinit var optionsAddOrEditDomainMenuItem: MenuItem
     private lateinit var optionsBlockAllThirdPartyRequestsMenuItem: MenuItem
@@ -606,6 +607,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
             // Get handles for the navigation menu items.
             navigationBackMenuItem = navigationMenu.findItem(R.id.back)
             navigationForwardMenuItem = navigationMenu.findItem(R.id.forward)
+            navigationScrollToBottomMenuItem = navigationMenu.findItem(R.id.scroll_to_bottom)
             navigationHistoryMenuItem = navigationMenu.findItem(R.id.history)
             navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests)
 
@@ -2272,6 +2274,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 }
             }
 
+            R.id.scroll_to_bottom -> {  // Scroll to Bottom.
+                // Check if the WebView is scrolled to the top.
+                if (currentWebView!!.scrollY == 0) {  // The WebView is at the top; scroll to the bottom.  Using a large Y number is more efficient than trying to calculate the exact WebView length.
+                    currentWebView!!.scrollTo(0, 1_000_000_000)
+                } else {  // The WebView is not at the top; scroll to the top.
+                    currentWebView!!.scrollTo(0, 0)
+                }
+            }
+
             R.id.history -> {  // History.
                 // Instantiate the URL history dialog.
                 val urlHistoryDialogFragment: DialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView!!.webViewFragmentId)
@@ -4435,9 +4446,28 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook
                 if (newState == DrawerLayout.STATE_SETTLING || newState == DrawerLayout.STATE_DRAGGING) {  // A drawer is opening or closing.
                     // Update the navigation menu items if the WebView is not null.
                     if (currentWebView != null) {
+                        // Set the enabled status of the menu items.
                         navigationBackMenuItem.isEnabled = currentWebView!!.canGoBack()
                         navigationForwardMenuItem.isEnabled = currentWebView!!.canGoForward()
+                        navigationScrollToBottomMenuItem.isEnabled = (currentWebView!!.canScrollVertically(-1) || currentWebView!!.canScrollVertically(1))
                         navigationHistoryMenuItem.isEnabled = currentWebView!!.canGoBack() || currentWebView!!.canGoForward()
+
+                        // Update the scroll menu item.
+                        if (currentWebView!!.scrollY == 0) {  // The WebView is scrolled to the top.
+                            // Set the title.
+                            navigationScrollToBottomMenuItem.title = getString(R.string.scroll_to_bottom)
+
+                            // Set the icon.
+                            navigationScrollToBottomMenuItem.icon = AppCompatResources.getDrawable(applicationContext, R.drawable.move_down_enabled)
+                        } else {  // The WebView is not scrolled to the top.
+                            // Set the title.
+                            navigationScrollToBottomMenuItem.title = getString(R.string.scroll_to_top)
+
+                            // Set the icon.
+                            navigationScrollToBottomMenuItem.icon = AppCompatResources.getDrawable(applicationContext, R.drawable.move_up_enabled)
+                        }
+
+                        // Display the number of blocked requests.
                         navigationRequestsMenuItem.title = getString(R.string.requests) + " - " + currentWebView!!.getRequestsCount(BLOCKED_REQUESTS)
 
                         // Hide the keyboard (if displayed).
index c8878c5fa772a4e53b824155ad4eb6ccbdfe8a3a..145ced5d4d1e9ee568a523fe44d1801487bf84e2 100644 (file)
@@ -146,27 +146,23 @@ class ViewHeadersActivity: AppCompatActivity(), UntrustedSslCertificateListener
         responseBodyTitleTextView = findViewById(R.id.response_body_title_textview)
         val responseBodyTextView = findViewById<TextView>(R.id.response_body_textview)
 
-        // Populate the URL text box.
-        urlEditText.setText(currentUrl)
-
         // Initialize the gray foreground color spans for highlighting the URLs.
         initialGrayColorSpan = ForegroundColorSpan(getColor(R.color.gray_500))
         finalGrayColorSpan = ForegroundColorSpan(getColor(R.color.gray_500))
         redColorSpan = ForegroundColorSpan(getColor(R.color.red_text))
 
-        // Apply text highlighting to the URL.
-        UrlHelper.highlightSyntax(urlEditText, initialGrayColorSpan, finalGrayColorSpan, redColorSpan)
-
         // Get a handle for the input method manager, which is used to hide the keyboard.
         val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
 
         // Remove the formatting from the URL when the user is editing the text.
         urlEditText.onFocusChangeListener = OnFocusChangeListener { _: View?, hasFocus: Boolean ->
             if (hasFocus) {  // The user is editing the URL text box.
-                // Remove the highlighting.
-                urlEditText.text.removeSpan(redColorSpan)
-                urlEditText.text.removeSpan(initialGrayColorSpan)
-                urlEditText.text.removeSpan(finalGrayColorSpan)
+                // Get the foreground color spans.
+                val foregroundColorSpans: Array<ForegroundColorSpan> = urlEditText.text.getSpans(0, urlEditText.text.length, ForegroundColorSpan::class.java)
+
+                // Remove each foreground color span that highlights the text.
+                for (foregroundColorSpan in foregroundColorSpans)
+                    urlEditText.text.removeSpan(foregroundColorSpan)
             } else {  // The user has stopped editing the URL text box.
                 // Hide the soft keyboard.
                 inputMethodManager.hideSoftInputFromWindow(urlEditText.windowToken, 0)
@@ -174,11 +170,20 @@ class ViewHeadersActivity: AppCompatActivity(), UntrustedSslCertificateListener
                 // Move to the beginning of the string.
                 urlEditText.setSelection(0)
 
+                // Store the URL text in the intent, so update layout uses the new text if the app is restarted.
+                intent.putExtra(CURRENT_URL, urlEditText.text.toString())
+
                 // Reapply the highlighting.
                 UrlHelper.highlightSyntax(urlEditText, initialGrayColorSpan, finalGrayColorSpan, redColorSpan)
             }
         }
 
+        // Populate the URL text box.
+        urlEditText.setText(currentUrl)
+
+        // Apply the initial text highlighting to the URL.
+        UrlHelper.highlightSyntax(urlEditText, initialGrayColorSpan, finalGrayColorSpan, redColorSpan)
+
         // Set the refresh color scheme according to the theme.
         swipeRefreshLayout.setColorSchemeResources(R.color.blue_text)
 
index a375e0a99e3496a7717f36cad19ad881833f2574..f98b918f7b49347df11f9b1daaad6ed1a77479ec 100644 (file)
@@ -202,7 +202,7 @@ object UrlHelper {
         // Highlight the URL according to the protocol.
         if (urlString.startsWith("file://") || urlString.startsWith("content://")) {  // This is a file or content URL.
             // De-emphasize everything before the file name.
-            urlEditText.text.setSpan(initialGrayColorSpan, 0, urlString.lastIndexOf("/") + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+            urlEditText.text.setSpan(initialGrayColorSpan, 0, urlString.lastIndexOf("/") + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
         } else {  // This is a web URL.
             // Get the index of the `/` immediately after the domain name.
             val endOfDomainName = urlString.indexOf("/", urlString.indexOf("//") + 2)
@@ -222,29 +222,29 @@ object UrlHelper {
             // Markup the beginning of the URL.
             if (urlString.startsWith("http://")) {  // The protocol is not encrypted.
                 // Highlight the protocol in red.
-                urlEditText.text.setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                urlEditText.text.setSpan(redColorSpan, 0, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
 
                 // De-emphasize subdomains.
                 if (penultimateDotIndex > 0) // There is more than one subdomain in the domain name.
-                    urlEditText.text.setSpan(initialGrayColorSpan, 7, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                    urlEditText.text.setSpan(initialGrayColorSpan, 7, penultimateDotIndex + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
             } else if (urlString.startsWith("https://") || urlString.startsWith("view-source:https://")) {  // The protocol is encrypted.
                 // De-emphasize the protocol of connections that are encrypted.
                 if (penultimateDotIndex > 0)  // There is more than one subdomain in the domain name.  De-emphasize the protocol and the additional subdomains.
-                    urlEditText.text.setSpan(initialGrayColorSpan, 0, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                    urlEditText.text.setSpan(initialGrayColorSpan, 0, penultimateDotIndex + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                 else  // There is only one subdomain in the domain name.  De-emphasize only the protocol.
-                    urlEditText.text.setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                    urlEditText.text.setSpan(initialGrayColorSpan, 0, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
             } else if (urlString.startsWith("view-source:http://")) {  // An insecure source is being viewed.
                 // Check to see if subdomains should be de-emphasized.
                 if (penultimateDotIndex > 0) {  // There are subdomains that should be de-emphasized.
                     // De-emphasize the `view-source:` text.
-                    urlEditText.text.setSpan(initialGrayColorSpan, 0, penultimateDotIndex + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                    urlEditText.text.setSpan(initialGrayColorSpan, 0, penultimateDotIndex + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                 } else {  // There are no subdomains that need to be de-emphasized.
                     // De-emphasize the `view-source:` text.
-                    urlEditText.text.setSpan(initialGrayColorSpan, 0, 11, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                    urlEditText.text.setSpan(initialGrayColorSpan, 0, 11, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                 }
 
                 // Highlight the protocol in red.
-                urlEditText.text.setSpan(redColorSpan, 12, 19, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
+                urlEditText.text.setSpan(redColorSpan, 12, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
             }
 
             // De-emphasize the text after the domain name.
index 0b896c71d31b6dc1c0fd54fd3cd97955c418e6d3..287b982d56953774cafe7fcefcb5b325a0bbfd07 100644 (file)
@@ -77,7 +77,6 @@
                         android:layout_height="wrap_content"
                         android:layout_width="wrap_content"
                         android:orientation="horizontal"
-                        android:layout_marginBottom="16dp"
                         android:layout_gravity="center_horizontal" >
 
                             <Button
@@ -87,6 +86,7 @@
                                 android:text="@string/ciphers"
                                 android:layout_gravity="center_horizontal"
                                 android:layout_marginEnd="10dp"
+                                android:layout_marginBottom="16dp"
                                 android:onClick="showCiphers"
                                 app:backgroundTint="@color/button_background_selector"
                                 android:textColor="@color/button_text_selector"
index 47f5a28338e6fb513155c17d439c87b7e75fb334..bd6e6ff00b770254fbdc2c6f4ed4026cb1ed8f5b 100644 (file)
                         android:layout_height="wrap_content"
                         android:layout_width="wrap_content"
                         android:orientation="horizontal"
-                        android:layout_marginBottom="16dp"
                         android:layout_gravity="center_horizontal" >
 
                         <Button
                             android:text="@string/ciphers"
                             android:layout_gravity="center_horizontal"
                             android:layout_marginEnd="10dp"
+                            android:layout_marginBottom="16dp"
                             android:onClick="showCiphers"
                             app:backgroundTint="@color/button_background_selector"
                             android:textColor="@color/button_text_selector"
index a14f46171cc989bf1a014f62d1ef3a779baa3030..87bc956dc22e40f1e77c3b333beb2a5d93cad243 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright 2016-2022 Soren Stoutner <soren@stoutner.com>.
+  Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
 
             android:icon="@drawable/history"
             android:orderInCategory="110" />
 
+        <item
+            android:id="@+id/scroll_to_bottom"
+            android:title="@string/scroll_to_bottom"
+            android:icon="@drawable/move_down_enabled"
+            android:orderInCategory="120" />
+
         <item
             android:id="@+id/forward"
             android:title="@string/forward"
             android:icon="@drawable/forward"
-            android:orderInCategory="120" />
+            android:orderInCategory="130" />
 
         <item
             android:id="@+id/back"
             android:title="@string/back"
             android:icon="@drawable/back"
-            android:orderInCategory="130" />
+            android:orderInCategory="140" />
 
         <item
             android:id="@+id/home"
             android:title="@string/home"
             android:icon="@drawable/home"
-            android:orderInCategory="140" />
+            android:orderInCategory="150" />
     </group>
 
     <!-- If a group has an id, a line is drawn above it in the navigation view. -->
             android:id="@+id/clear_and_exit"
             android:title="@string/clear_and_exit"
             android:icon="@drawable/download_with_external_app_enabled"
-            android:orderInCategory="150" />
+            android:orderInCategory="160" />
     </group>
 </menu>
index 8ca53059b8ada2bffb41686fdbb9394507b849f6..90912a69567e0e3c37191eca419cfcc42756f546 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright 2016-2022 Soren Stoutner <soren@stoutner.com>.
+  Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
 
   This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
 
             android:icon="@drawable/forward"
             android:orderInCategory="40" />
 
+        <item
+            android:id="@+id/scroll_to_bottom"
+            android:title="@string/scroll_to_bottom"
+            android:icon="@drawable/move_down_enabled"
+            android:orderInCategory="50" />
+
         <item
             android:id="@+id/history"
             android:title="@string/history"
             android:icon="@drawable/history"
-            android:orderInCategory="50" />
+            android:orderInCategory="60" />
 
         <item
             android:id="@+id/open"
             android:title="@string/open"
             android:icon="@drawable/proxy_enabled"
-            android:orderInCategory="60" />
+            android:orderInCategory="70" />
     </group>
 
     <!-- If a group has an id, a line is drawn above it in the navigation view. -->
             android:id="@+id/requests"
             android:title="@string/requests"
             android:icon="@drawable/block_ads_enabled"
-            android:orderInCategory="70" />
+            android:orderInCategory="80" />
 
         <item
             android:id="@+id/downloads"
             android:title="@string/downloads"
             android:icon="@drawable/download"
-            android:orderInCategory="80" />
+            android:orderInCategory="90" />
     </group>
 
     <!-- If a group has an id, a line is drawn above it in the navigation view. -->
             android:id="@+id/domains"
             android:title="@string/domains"
             android:icon="@drawable/domains"
-            android:orderInCategory="90" />
+            android:orderInCategory="100" />
 
         <item
             android:id="@+id/settings"
             android:title="@string/settings"
             android:icon="@drawable/settings"
-            android:orderInCategory="100" />
+            android:orderInCategory="110" />
 
         <item
             android:id="@+id/import_export"
             android:title="@string/import_export"
             android:icon="@drawable/import_export"
-            android:orderInCategory="110" />
+            android:orderInCategory="120" />
 
         <item
             android:id="@+id/logcat"
             android:title="@string/logcat"
             android:icon="@drawable/clear_logcat_enabled"
-            android:orderInCategory="120" />
+            android:orderInCategory="130" />
 
         <item
             android:id="@+id/webview_devtools"
             android:title="@string/webview_devtools"
             android:icon="@drawable/webview_devtools"
-            android:orderInCategory="130" />
+            android:orderInCategory="140" />
     </group>
 
     <!-- If a group has an id, a line is drawn above it in the navigation view. -->
             android:id="@+id/guide"
             android:title="@string/guide"
             android:icon="@drawable/guide"
-            android:orderInCategory="140" />
+            android:orderInCategory="150" />
 
         <item
             android:id="@+id/about"
             android:title="@string/about"
             android:icon="@drawable/about"
-            android:orderInCategory="150" />
+            android:orderInCategory="160" />
     </group>
 </menu>
index 5c0a40b11545e66d3c5474cc1ac3888ae4bc36a1..b69c1f5d1fa9b9b3bef39a9a053ba9d394a59f3d 100644 (file)
 
     <!-- View Headers. Android removes double spaces, but extra spaces can be manually specified with the Unicode `\u0020` formatting.-->
     <string name="colon">: \u0020</string>
+    <string name="ssl_information">Información sobre SSL</string>
+    <string name="applied_cipher">Cifrado aplicado</string>
+    <string name="peer_principal">Pares principales</string>
+    <string name="certificate_type">Tipo de certificado</string>
+    <string name="certificate_hash_code">Código hash del certificado</string>
+    <string name="ciphers">Cifrados</string>
+    <string name="available_ciphers">Cifrados disponibles</string>
+    <string name="certificate">Certificado</string>
     <string name="request_headers">Cabeceras de solicitud</string>
     <string name="response_message">Mensaje de respuesta</string>
     <string name="response_headers">Cabeceras de respuesta</string>
index b25787c71cec5611bb6a1f3901495106fe7b8617..1b5de9dbdf3407653deee91a257c70d947e580dc 100644 (file)
     <string name="error_saving_file">Error di salvataggio di %1$s:\u0020 %2$s</string>
     <string name="unknown_error">Errore sconosciuto</string>
 
-    <!-- View Headers. -->
+    <!-- View Headers. Android removes double spaces, but extra spaces can be manually specified with the Unicode `\u0020` formatting.-->
+    <string name="colon">: \u0020</string>
+    <string name="ssl_information">Informazioni SSL</string>
+    <string name="applied_cipher">Cifratura Applicata</string>
+    <string name="peer_principal">Peer Principale</string>
+    <string name="certificate_type">Tipo di Certificato</string>
+    <string name="certificate_hash_code">Hash Code del Certificato</string>
+    <string name="ciphers">Cifrature</string>
+    <string name="available_ciphers">Cifrature Disponibili</string>
+    <string name="certificate">Certificato</string>
     <string name="request_headers">Richiesta Intestazioni</string>
     <string name="response_message">Messaggio di Risposta</string>
     <string name="response_headers">Risposta Intestazioni</string>
index 2183fe048c1299536be06e0aad82a3a0863a2482..96aa8d549580b409d37ba5d5bb4ef1f79af92b0d 100644 (file)
     <string name="home">Home</string>
     <string name="back">Back</string>
     <string name="forward">Forward</string>
+    <string name="scroll_to_bottom">Scroll to Bottom</string>
+        <string name="scroll_to_top">Scroll to Top</string>
     <string name="history">History</string>
         <string name="clear_history">Clear History</string>
     <string name="open">Open</string>