X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FViewSourceActivity.kt;h=a8d86ecc6a243d1e2cf28f5decdb1009ce8c23fa;hb=514e93baaa8389dc9c5abdb79e68c890c260b8d3;hp=532eda553eb258e2c0809f8a14f762214b5a41b1;hpb=1d656c562831f535aa33903d44198dd890393f4f;p=PrivacyBrowserAndroid.git diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.kt index 532eda55..a8d86ecc 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.kt @@ -1,29 +1,26 @@ /* - * Copyright © 2017-2021 Soren Stoutner . + * Copyright 2017-2023 Soren Stoutner . * - * This file is part of Privacy Browser . + * This file is part of Privacy Browser Android . * - * Privacy Browser is free software: you can redistribute it and/or modify + * 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. * - * Privacy Browser is distributed in the hope that it will be useful, + * 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. * * You should have received a copy of the GNU General Public License - * along with Privacy Browser. If not, see . + * along with Privacy Browser Android. If not, see . */ package com.stoutner.privacybrowser.activities -import android.content.res.Configuration -import android.os.Build import android.os.Bundle import android.text.SpannableStringBuilder -import android.text.Spanned import android.text.style.ForegroundColorSpan import android.util.TypedValue import android.view.KeyEvent @@ -53,12 +50,11 @@ import com.stoutner.privacybrowser.dialogs.AboutViewSourceDialog import com.stoutner.privacybrowser.dialogs.UntrustedSslCertificateDialog import com.stoutner.privacybrowser.dialogs.UntrustedSslCertificateDialog.UntrustedSslCertificateListener import com.stoutner.privacybrowser.helpers.ProxyHelper +import com.stoutner.privacybrowser.helpers.UrlHelper import com.stoutner.privacybrowser.viewmodelfactories.WebViewSourceFactory import com.stoutner.privacybrowser.viewmodels.WebViewSource -import java.util.Locale - -// Declare the public constants. +// Define the public constants. const val CURRENT_URL = "current_url" const val USER_AGENT = "user_agent" @@ -91,9 +87,6 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) } - // Set the theme. - setTheme(R.style.PrivacyBrowser) - // Run the default commands. super.onCreate(savedInstanceState) @@ -121,7 +114,7 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { val actionBar = supportActionBar!! // Add the custom layout to the action bar. - actionBar.setCustomView(R.layout.view_source_app_bar) + actionBar.setCustomView(R.layout.view_source_appbar_custom_view) // Instruct the action bar to display a custom layout. actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM @@ -134,7 +127,7 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { requestHeadersTextView = findViewById(R.id.request_headers_textview) responseMessageTitleTextView = findViewById(R.id.response_message_title_textview) responseMessageTextView = findViewById(R.id.response_message_textview) - responseHeadersTitleTextView = findViewById(R.id.response_headers_title_textivew) + responseHeadersTitleTextView = findViewById(R.id.response_headers_title_textview) val responseHeadersTextView = findViewById(R.id.response_headers_textview) responseBodyTitleTextView = findViewById(R.id.response_body_title_textview) val responseBodyTextView = findViewById(R.id.response_body_textview) @@ -142,26 +135,13 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { // Populate the URL text box. urlEditText.setText(currentUrl) - // Initialize the gray foreground color spans for highlighting the URLs. The deprecated `getColor()` must be used until the minimum API >= 23. - @Suppress("DEPRECATION") - initialGrayColorSpan = ForegroundColorSpan(resources.getColor(R.color.gray_500)) - @Suppress("DEPRECATION") - finalGrayColorSpan = ForegroundColorSpan(resources.getColor(R.color.gray_500)) - - // Get the current theme status. - val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - - // Set the red color span according to the theme. The deprecated `getColor()` must be used until the minimum API >= 23. - redColorSpan = if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - @Suppress("DEPRECATION") - ForegroundColorSpan(resources.getColor(R.color.red_a700)) - } else { - @Suppress("DEPRECATION") - ForegroundColorSpan(resources.getColor(R.color.red_900)) - } + // 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. - highlightUrlText() + 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) @@ -181,16 +161,12 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { urlEditText.setSelection(0) // Reapply the highlighting. - highlightUrlText() + UrlHelper.highlightSyntax(urlEditText, initialGrayColorSpan, finalGrayColorSpan, redColorSpan) } } // Set the refresh color scheme according to the theme. - if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) { - swipeRefreshLayout.setColorSchemeResources(R.color.blue_700) - } else { - swipeRefreshLayout.setColorSchemeResources(R.color.violet_500) - } + swipeRefreshLayout.setColorSchemeResources(R.color.blue_text) // Initialize a color background typed value. val colorBackgroundTypedValue = TypedValue() @@ -204,62 +180,53 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { // Set the swipe refresh background color. swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt) - // Populate the locale string. - val localeString = if (Build.VERSION.SDK_INT >= 24) { // SDK >= 24 has a list of locales. - // Get the list of locales. - val localeList = resources.configuration.locales + // Get the list of locales. + val localeList = resources.configuration.locales - // Initialize a string builder to extract the locales from the list. - val localesStringBuilder = StringBuilder() + // Initialize a string builder to extract the locales from the list. + val localesStringBuilder = StringBuilder() - // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages. - var q = 10 - - // Populate the string builder with the contents of the locales list. - for (i in 0 until localeList.size()) { - // Append a comma if there is already an item in the string builder. - if (i > 0) { - localesStringBuilder.append(",") - } + // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages. + var q = 10 - // Get the locale from the list. - val locale = localeList[i] + // Populate the string builder with the contents of the locales list. + for (i in 0 until localeList.size()) { + // Append a comma if there is already an item in the string builder. + if (i > 0) { + localesStringBuilder.append(",") + } - // Add the locale to the string. `locale` by default displays as `en_US`, but WebView uses the `en-US` format. - localesStringBuilder.append(locale.language) - localesStringBuilder.append("-") - localesStringBuilder.append(locale.country) + // Get the locale from the list. + val locale = localeList[i] - // If not the first locale, append `;q=0.x`, which drops by .1 for each removal from the main locale until q=0.1. - if (q < 10) { - localesStringBuilder.append(";q=0.") - localesStringBuilder.append(q) - } - - // Decrement `q` if it is greater than 1. - if (q > 1) { - q-- - } + // Add the locale to the string. `locale` by default displays as `en_US`, but WebView uses the `en-US` format. + localesStringBuilder.append(locale.language) + localesStringBuilder.append("-") + localesStringBuilder.append(locale.country) - // Add a second entry for the language only portion of the locale. - localesStringBuilder.append(",") - localesStringBuilder.append(locale.language) - - // Append `1;q=0.x`, which drops by .1 for each removal form the main locale until q=0.1. + // If not the first locale, append `;q=0.x`, which drops by .1 for each removal from the main locale until q=0.1. + if (q < 10) { localesStringBuilder.append(";q=0.") localesStringBuilder.append(q) + } - // Decrement `q` if it is greater than 1. - if (q > 1) { - q-- - } + // Decrement `q` if it is greater than 1. + if (q > 1) { + q-- } - // Store the populated string builder in the locale string. - localesStringBuilder.toString() - } else { // SDK < 24 only has a primary locale. - // Store the locale in the locale string. - Locale.getDefault().toString() + // Add a second entry for the language only portion of the locale. + localesStringBuilder.append(",") + localesStringBuilder.append(locale.language) + + // Append `1;q=0.x`, which drops by .1 for each removal form the main locale until q=0.1. + localesStringBuilder.append(";q=0.") + localesStringBuilder.append(q) + + // Decrement `q` if it is greater than 1. + if (q > 1) { + q-- + } } // Instantiate the proxy helper. @@ -278,13 +245,13 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { updateLayout(currentUrl) // Instantiate the WebView source factory. - val webViewSourceFactory: ViewModelProvider.Factory = WebViewSourceFactory(currentUrl, userAgent, localeString, proxy, contentResolver, MainWebViewActivity.executorService) + val webViewSourceFactory: ViewModelProvider.Factory = WebViewSourceFactory(currentUrl, userAgent, localesStringBuilder.toString(), proxy, contentResolver, MainWebViewActivity.executorService) // Instantiate the WebView source view model class. - webViewSource = ViewModelProvider(this, webViewSourceFactory).get(WebViewSource::class.java) + webViewSource = ViewModelProvider(this, webViewSourceFactory)[WebViewSource::class.java] // Create a source observer. - webViewSource.observeSource().observe(this, { sourceStringArray: Array -> + webViewSource.observeSource().observe(this) { sourceStringArray: Array -> // Populate the text views. This can take a long time, and freezes the user interface, if the response body is particularly large. requestHeadersTextView.text = sourceStringArray[0] responseMessageTextView.text = sourceStringArray[1] @@ -297,10 +264,10 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { //Stop the swipe to refresh indicator if it is running swipeRefreshLayout.isRefreshing = false - }) + } // Create an error observer. - webViewSource.observeErrors().observe(this, { errorString: String -> + webViewSource.observeErrors().observe(this) { errorString: String -> // Display an error snackbar if the string is not `""`. if (errorString != "") { if (errorString.startsWith("javax.net.ssl.SSLHandshakeException")) { @@ -314,7 +281,7 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { Snackbar.make(swipeRefreshLayout, errorString, Snackbar.LENGTH_LONG).show() } } - }) + } // Implement swipe to refresh. swipeRefreshLayout.setOnRefreshListener { @@ -398,64 +365,6 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { webViewSource.updateSource(urlEditText.text.toString(), true) } - private fun highlightUrlText() { - // Get a handle for the URL edit text. - val urlEditText = findViewById(R.id.url_edittext) - - // Get the URL string. - val urlString = urlEditText.text.toString() - - // Highlight the URL according to the protocol. - if (urlString.startsWith("file://")) { // This is a file URL. - // De-emphasize only the protocol. - urlEditText.text.setSpan(initialGrayColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE) - } else if (urlString.startsWith("content://")) { - // De-emphasize only the protocol. - urlEditText.text.setSpan(initialGrayColorSpan, 0, 10, Spanned.SPAN_INCLUSIVE_INCLUSIVE) - } else { // This is a web URL. - // Get the index of the `/` immediately after the domain name. - val endOfDomainName = urlString.indexOf("/", urlString.indexOf("//") + 2) - - // Get the base URL. - val baseUrl = if (endOfDomainName > 0) { // There is at least one character after the base URL. - // Get the base URL. - urlString.substring(0, endOfDomainName) - } else { // There are no characters after the base URL. - // Set the base URL to be the entire URL string. - urlString - } - - // Get the index of the last `.` in the domain. - val lastDotIndex = baseUrl.lastIndexOf(".") - - // Get the index of the penultimate `.` in the domain. - val penultimateDotIndex = baseUrl.lastIndexOf(".", lastDotIndex - 1) - - // Markup the beginning of the URL. - if (urlString.startsWith("http://")) { // Highlight the protocol of connections that are not encrypted. - urlEditText.text.setSpan(redColorSpan, 0, 7, Spanned.SPAN_INCLUSIVE_INCLUSIVE) - - // 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) - } - } else if (urlString.startsWith("https://")) { // 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) - } 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) - } - } - - // De-emphasize the text after the domain name. - if (endOfDomainName > 0) { - urlEditText.text.setSpan(finalGrayColorSpan, endOfDomainName, urlString.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) - } - } - } - private fun updateLayout(urlString: String) { if (urlString.startsWith("content://")) { // This is a content URL. // Hide the unused text views. @@ -479,4 +388,4 @@ class ViewSourceActivity: AppCompatActivity(), UntrustedSslCertificateListener { responseBodyTitleTextView.setText(R.string.response_body) } } -} \ No newline at end of file +}