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
// 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)
}
}
+ 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)
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).
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)
// 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)
// 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)
// 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.
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"
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"
<?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>
<?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>
<!-- 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>
<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>
<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>