/* * Copyright © 2016-2021 Soren Stoutner . * * This file is part of Privacy Browser . * * Privacy Browser 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, * 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 . */ package com.stoutner.privacybrowser.fragments import android.annotation.SuppressLint import android.app.Activity import android.app.ActivityManager import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper import android.provider.OpenableColumns import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.ForegroundColorSpan import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.webkit.WebView import android.widget.TextView import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.Fragment import androidx.webkit.WebViewCompat import com.google.android.material.snackbar.Snackbar import com.stoutner.privacybrowser.R import com.stoutner.privacybrowser.BuildConfig import com.stoutner.privacybrowser.asynctasks.SaveAboutVersionImage import java.io.ByteArrayInputStream import java.io.InputStream import java.lang.Exception import java.nio.charset.StandardCharsets import java.security.cert.CertificateException import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.text.DateFormat import java.text.NumberFormat import kotlin.text.StringBuilder // Define the class constants. private const val BLOCKLIST_VERSIONS = "blocklist_versions" private const val MEBIBYTE = 1048576 class AboutVersionFragment : Fragment() { // Define the class variables. private var updateMemoryUsageBoolean = true // Declare the class variables. private lateinit var blocklistVersions: Array private lateinit var aboutVersionLayout: View private lateinit var appConsumedMemoryLabel: String private lateinit var appAvailableMemoryLabel: String private lateinit var appTotalMemoryLabel: String private lateinit var appMaximumMemoryLabel: String private lateinit var systemConsumedMemoryLabel: String private lateinit var systemAvailableMemoryLabel: String private lateinit var systemTotalMemoryLabel: String private lateinit var runtime: Runtime private lateinit var activityManager: ActivityManager private lateinit var memoryInfo: ActivityManager.MemoryInfo private lateinit var numberFormat: NumberFormat private lateinit var blueColorSpan: ForegroundColorSpan // Declare the class views. private lateinit var privacyBrowserTextView: TextView private lateinit var versionTextView: TextView private lateinit var hardwareTextView: TextView private lateinit var brandTextView: TextView private lateinit var manufacturerTextView: TextView private lateinit var modelTextView: TextView private lateinit var deviceTextView: TextView private lateinit var bootloaderTextView: TextView private lateinit var radioTextView: TextView private lateinit var softwareTextView: TextView private lateinit var androidTextView: TextView private lateinit var securityPatchTextView: TextView private lateinit var buildTextView: TextView private lateinit var webViewProviderTextView: TextView private lateinit var webViewVersionTextView: TextView private lateinit var orbotTextView: TextView private lateinit var i2pTextView: TextView private lateinit var openKeychainTextView: TextView private lateinit var memoryUsageTextView: TextView private lateinit var appConsumedMemoryTextView: TextView private lateinit var appAvailableMemoryTextView: TextView private lateinit var appTotalMemoryTextView: TextView private lateinit var appMaximumMemoryTextView: TextView private lateinit var systemConsumedMemoryTextView: TextView private lateinit var systemAvailableMemoryTextView: TextView private lateinit var systemTotalMemoryTextView: TextView private lateinit var blocklistsTextView: TextView private lateinit var easyListTextView: TextView private lateinit var easyPrivacyTextView: TextView private lateinit var fanboyAnnoyanceTextView: TextView private lateinit var fanboySocialTextView: TextView private lateinit var ultraListTextView: TextView private lateinit var ultraPrivacyTextView: TextView private lateinit var packageSignatureTextView: TextView private lateinit var certificateIssuerDnTextView: TextView private lateinit var certificateSubjectDnTextView: TextView private lateinit var certificateStartDateTextView: TextView private lateinit var certificateEndDateTextView: TextView private lateinit var certificateVersionTextView: TextView private lateinit var certificateSerialNumberTextView: TextView private lateinit var certificateSignatureAlgorithmTextView: TextView companion object { fun createTab(blocklistVersions: Array): AboutVersionFragment { // Create an arguments bundle. val argumentsBundle = Bundle() // Store the arguments in the bundle. argumentsBundle.putStringArray(BLOCKLIST_VERSIONS, blocklistVersions) // Create a new instance of the tab fragment. val aboutVersionFragment = AboutVersionFragment() // Add the arguments bundle to the fragment. aboutVersionFragment.arguments = argumentsBundle // Return the new fragment. return aboutVersionFragment } } // Define the save about version text activity result launcher. It must be defined before `onCreate()` is run or the app will crash. private val saveAboutVersionTextActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { fileNameUri: Uri? -> // Only save the file if the URI is not null, which happens if the user exited the file picker by pressing back. if (fileNameUri != null) { try { // Get the about version string. val aboutVersionString = getAboutVersionString() // Open an output stream. val outputStream = requireActivity().contentResolver.openOutputStream(fileNameUri)!! // Write the about version string to the output stream. outputStream.write(aboutVersionString.toByteArray(StandardCharsets.UTF_8)) // Close the output stream. outputStream.close() // Initialize the file name string from the file name URI last path segment. var fileNameString = fileNameUri.lastPathSegment // Query the exact file name if the API >= 26. if (Build.VERSION.SDK_INT >= 26) { // Get a cursor from the content resolver. val contentResolverCursor = requireActivity().contentResolver.query(fileNameUri, null, null, null)!! // Move to the first row. contentResolverCursor.moveToFirst() // Get the file name from the cursor. fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)) // Close the cursor. contentResolverCursor.close() } // Display a snackbar with the saved logcat information. Snackbar.make(aboutVersionLayout, getString(R.string.saved, fileNameString), Snackbar.LENGTH_SHORT).show() } catch (exception: Exception) { // Display a snackbar with the error message. Snackbar.make(aboutVersionLayout, getString(R.string.error_saving_file) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show() } } } // Define the save about version image activity result launcher. It must be defined before `onCreate()` is run or the app will crash. private val saveAboutVersionImageActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { fileNameUri: Uri? -> // Only save the file if the URI is not null, which happens if the user exited the file picker by pressing back. if (fileNameUri != null) { // Save the about version image. SaveAboutVersionImage(requireActivity(), fileNameUri, aboutVersionLayout.findViewById(R.id.about_version_linearlayout)).execute() } } override fun onCreate(savedInstanceState: Bundle?) { // Run the default commands. super.onCreate(savedInstanceState) // Store the arguments in class variables. blocklistVersions = requireArguments().getStringArray(BLOCKLIST_VERSIONS)!! // Enable the options menu for this fragment. setHasOptionsMenu(true) } override fun onCreateView(layoutInflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { // Inflate the layout. Setting false at the end of inflater.inflate does not attach the inflated layout as a child of container. The fragment will take care of attaching the root automatically. aboutVersionLayout = layoutInflater.inflate(R.layout.about_version_scrollview, container, false) // Get handles for the views. privacyBrowserTextView = aboutVersionLayout.findViewById(R.id.privacy_browser_textview) versionTextView = aboutVersionLayout.findViewById(R.id.version) hardwareTextView = aboutVersionLayout.findViewById(R.id.hardware) brandTextView = aboutVersionLayout.findViewById(R.id.brand) manufacturerTextView = aboutVersionLayout.findViewById(R.id.manufacturer) modelTextView = aboutVersionLayout.findViewById(R.id.model) deviceTextView = aboutVersionLayout.findViewById(R.id.device) bootloaderTextView = aboutVersionLayout.findViewById(R.id.bootloader) radioTextView = aboutVersionLayout.findViewById(R.id.radio) softwareTextView = aboutVersionLayout.findViewById(R.id.software) androidTextView = aboutVersionLayout.findViewById(R.id.android) securityPatchTextView = aboutVersionLayout.findViewById(R.id.security_patch) buildTextView = aboutVersionLayout.findViewById(R.id.build) webViewProviderTextView = aboutVersionLayout.findViewById(R.id.webview_provider) webViewVersionTextView = aboutVersionLayout.findViewById(R.id.webview_version) orbotTextView = aboutVersionLayout.findViewById(R.id.orbot) i2pTextView = aboutVersionLayout.findViewById(R.id.i2p) openKeychainTextView = aboutVersionLayout.findViewById(R.id.open_keychain) memoryUsageTextView = aboutVersionLayout.findViewById(R.id.memory_usage) appConsumedMemoryTextView = aboutVersionLayout.findViewById(R.id.app_consumed_memory) appAvailableMemoryTextView = aboutVersionLayout.findViewById(R.id.app_available_memory) appTotalMemoryTextView = aboutVersionLayout.findViewById(R.id.app_total_memory) appMaximumMemoryTextView = aboutVersionLayout.findViewById(R.id.app_maximum_memory) systemConsumedMemoryTextView = aboutVersionLayout.findViewById(R.id.system_consumed_memory) systemAvailableMemoryTextView = aboutVersionLayout.findViewById(R.id.system_available_memory) systemTotalMemoryTextView = aboutVersionLayout.findViewById(R.id.system_total_memory) blocklistsTextView = aboutVersionLayout.findViewById(R.id.blocklists) easyListTextView = aboutVersionLayout.findViewById(R.id.easylist) easyPrivacyTextView = aboutVersionLayout.findViewById(R.id.easyprivacy) fanboyAnnoyanceTextView = aboutVersionLayout.findViewById(R.id.fanboy_annoyance) fanboySocialTextView = aboutVersionLayout.findViewById(R.id.fanboy_social) ultraListTextView = aboutVersionLayout.findViewById(R.id.ultralist) ultraPrivacyTextView = aboutVersionLayout.findViewById(R.id.ultraprivacy) packageSignatureTextView = aboutVersionLayout.findViewById(R.id.package_signature) certificateIssuerDnTextView = aboutVersionLayout.findViewById(R.id.certificate_issuer_dn) certificateSubjectDnTextView = aboutVersionLayout.findViewById(R.id.certificate_subject_dn) certificateStartDateTextView = aboutVersionLayout.findViewById(R.id.certificate_start_date) certificateEndDateTextView = aboutVersionLayout.findViewById(R.id.certificate_end_date) certificateVersionTextView = aboutVersionLayout.findViewById(R.id.certificate_version) certificateSerialNumberTextView = aboutVersionLayout.findViewById(R.id.certificate_serial_number) certificateSignatureAlgorithmTextView = aboutVersionLayout.findViewById(R.id.certificate_signature_algorithm) // Setup the labels. val version = getString(R.string.version) + " " + BuildConfig.VERSION_NAME + " (" + getString(R.string.version_code) + " " + BuildConfig.VERSION_CODE + ")" val brandLabel = getString(R.string.brand) + " " val manufacturerLabel = getString(R.string.manufacturer) + " " val modelLabel = getString(R.string.model) + " " val deviceLabel = getString(R.string.device) + " " val bootloaderLabel = getString(R.string.bootloader) + " " val androidLabel = getString(R.string.android) + " " val buildLabel = getString(R.string.build) + " " val webViewVersionLabel = getString(R.string.webview_version) + " " appConsumedMemoryLabel = getString(R.string.app_consumed_memory) + " " appAvailableMemoryLabel = getString(R.string.app_available_memory) + " " appTotalMemoryLabel = getString(R.string.app_total_memory) + " " appMaximumMemoryLabel = getString(R.string.app_maximum_memory) + " " systemConsumedMemoryLabel = getString(R.string.system_consumed_memory) + " " systemAvailableMemoryLabel = getString(R.string.system_available_memory) + " " systemTotalMemoryLabel = getString(R.string.system_total_memory) + " " val easyListLabel = getString(R.string.easylist_label) + " " val easyPrivacyLabel = getString(R.string.easyprivacy_label) + " " val fanboyAnnoyanceLabel = getString(R.string.fanboy_annoyance_label) + " " val fanboySocialLabel = getString(R.string.fanboy_social_label) + " " val ultraListLabel = getString(R.string.ultralist_label) + " " val ultraPrivacyLabel = getString(R.string.ultraprivacy_label) + " " val issuerDNLabel = getString(R.string.issuer_dn) + " " val subjectDNLabel = getString(R.string.subject_dn) + " " val startDateLabel = getString(R.string.start_date) + " " val endDateLabel = getString(R.string.end_date) + " " val certificateVersionLabel = getString(R.string.certificate_version) + " " val serialNumberLabel = getString(R.string.serial_number) + " " val signatureAlgorithmLabel = getString(R.string.signature_algorithm) + " " // The WebView layout is only used to get the default user agent from `bare_webview`. It is not used to render content on the screen. // Once the minimum API >= 26 this can be accomplished with the WebView package info. val webViewLayout = layoutInflater.inflate(R.layout.bare_webview, container, false) val tabLayoutWebView = webViewLayout.findViewById(R.id.bare_webview) val userAgentString = tabLayoutWebView.settings.userAgentString // Get the device's information and store it in strings. val brand = Build.BRAND val manufacturer = Build.MANUFACTURER val model = Build.MODEL val device = Build.DEVICE val bootloader = Build.BOOTLOADER val radio = Build.getRadioVersion() val android = Build.VERSION.RELEASE + " (" + getString(R.string.api) + " " + Build.VERSION.SDK_INT + ")" val build = Build.DISPLAY // Select the substring that begins after `Chrome/` and goes until the next ` `. val webView = userAgentString.substring(userAgentString.indexOf("Chrome/") + 7, userAgentString.indexOf(" ", userAgentString.indexOf("Chrome/"))) // Get the Orbot version name if Orbot is installed. val orbot: String = try { // Store the version name. requireContext().packageManager.getPackageInfo("org.torproject.android", 0).versionName } catch (exception: PackageManager.NameNotFoundException) { // Orbot is not installed. // Store an empty string. "" } // Get the I2P version name if I2P is installed. val i2p: String = try { // Store the version name. requireContext().packageManager.getPackageInfo("net.i2p.android.router", 0).versionName } catch (exception: PackageManager.NameNotFoundException) { // I2P is not installed. // Store an empty string. "" } // Get the OpenKeychain version name if it is installed. val openKeychain: String = try { // Store the version name. requireContext().packageManager.getPackageInfo("org.sufficientlysecure.keychain", 0).versionName } catch (exception: PackageManager.NameNotFoundException) { // OpenKeychain is not installed. // Store an empty string. "" } // Create a spannable string builder for the hardware and software text views that need multiple colors of text. val brandStringBuilder = SpannableStringBuilder(brandLabel + brand) val manufacturerStringBuilder = SpannableStringBuilder(manufacturerLabel + manufacturer) val modelStringBuilder = SpannableStringBuilder(modelLabel + model) val deviceStringBuilder = SpannableStringBuilder(deviceLabel + device) val bootloaderStringBuilder = SpannableStringBuilder(bootloaderLabel + bootloader) val androidStringBuilder = SpannableStringBuilder(androidLabel + android) val buildStringBuilder = SpannableStringBuilder(buildLabel + build) val webViewVersionStringBuilder = SpannableStringBuilder(webViewVersionLabel + webView) val easyListStringBuilder = SpannableStringBuilder(easyListLabel + blocklistVersions[0]) val easyPrivacyStringBuilder = SpannableStringBuilder(easyPrivacyLabel + blocklistVersions[1]) val fanboyAnnoyanceStringBuilder = SpannableStringBuilder(fanboyAnnoyanceLabel + blocklistVersions[2]) val fanboySocialStringBuilder = SpannableStringBuilder(fanboySocialLabel + blocklistVersions[3]) val ultraListStringBuilder = SpannableStringBuilder(ultraListLabel + blocklistVersions[4]) val ultraPrivacyStringBuilder = SpannableStringBuilder(ultraPrivacyLabel + blocklistVersions[5]) // Set the blue color span according to the theme. The deprecated `getColor()` must be used until the minimum API >= 23. blueColorSpan = ForegroundColorSpan(resources.getColor(R.color.about_version_blue_text)) // Set the spans to display the device information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. brandStringBuilder.setSpan(blueColorSpan, brandLabel.length, brandStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) manufacturerStringBuilder.setSpan(blueColorSpan, manufacturerLabel.length, manufacturerStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) modelStringBuilder.setSpan(blueColorSpan, modelLabel.length, modelStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) deviceStringBuilder.setSpan(blueColorSpan, deviceLabel.length, deviceStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) bootloaderStringBuilder.setSpan(blueColorSpan, bootloaderLabel.length, bootloaderStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) androidStringBuilder.setSpan(blueColorSpan, androidLabel.length, androidStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) buildStringBuilder.setSpan(blueColorSpan, buildLabel.length, buildStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) webViewVersionStringBuilder.setSpan(blueColorSpan, webViewVersionLabel.length, webViewVersionStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) easyListStringBuilder.setSpan(blueColorSpan, easyListLabel.length, easyListStringBuilder.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) easyPrivacyStringBuilder.setSpan(blueColorSpan, easyPrivacyLabel.length, easyPrivacyStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) fanboyAnnoyanceStringBuilder.setSpan(blueColorSpan, fanboyAnnoyanceLabel.length, fanboyAnnoyanceStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) fanboySocialStringBuilder.setSpan(blueColorSpan, fanboySocialLabel.length, fanboySocialStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) ultraListStringBuilder.setSpan(blueColorSpan, ultraListLabel.length, ultraListStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) ultraPrivacyStringBuilder.setSpan(blueColorSpan, ultraPrivacyLabel.length, ultraPrivacyStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the strings in the text boxes. versionTextView.text = version brandTextView.text = brandStringBuilder manufacturerTextView.text = manufacturerStringBuilder modelTextView.text = modelStringBuilder deviceTextView.text = deviceStringBuilder bootloaderTextView.text = bootloaderStringBuilder androidTextView.text = androidStringBuilder buildTextView.text = buildStringBuilder webViewVersionTextView.text = webViewVersionStringBuilder easyListTextView.text = easyListStringBuilder easyPrivacyTextView.text = easyPrivacyStringBuilder fanboyAnnoyanceTextView.text = fanboyAnnoyanceStringBuilder fanboySocialTextView.text = fanboySocialStringBuilder ultraListTextView.text = ultraListStringBuilder ultraPrivacyTextView.text = ultraPrivacyStringBuilder // Only populate the radio text view if there is a radio in the device. // Null must be checked because some Samsung tablets report a null value for the radio instead of an empty string. Grrrr. if (radio != null && radio.isNotEmpty()) { // Setup the label. val radioLabel = getString(R.string.radio) + " " // Create a spannable string builder. val radioStringBuilder = SpannableStringBuilder(radioLabel + radio) // Set the span to display the radio in blue. radioStringBuilder.setSpan(blueColorSpan, radioLabel.length, radioStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the string in the text view. radioTextView.text = radioStringBuilder } else { // This device does not have a radio. // Hide the radio text view. radioTextView.visibility = View.GONE } // Build.VERSION.SECURITY_PATCH is only available for SDK_INT >= 23. if (Build.VERSION.SDK_INT >= 23) { // Setup the label. val securityPatchLabel = getString(R.string.security_patch) + " " // Get the security patch version. val securityPatch = Build.VERSION.SECURITY_PATCH // Create a spannable string builder. val securityPatchStringBuilder = SpannableStringBuilder(securityPatchLabel + securityPatch) // Set the span to display the security patch version in blue. securityPatchStringBuilder.setSpan(blueColorSpan, securityPatchLabel.length, securityPatchStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the string in the text view. securityPatchTextView.text = securityPatchStringBuilder } else { // The API < 23. // Hide the security patch text view. securityPatchTextView.visibility = View.GONE } // Only populate the WebView provider if the SDK >= 21. if (Build.VERSION.SDK_INT >= 21) { // Create the WebView provider label. val webViewProviderLabel = getString(R.string.webview_provider) + " " // Get the current WebView package info. val webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(requireContext())!! // Get the WebView provider name. val webViewPackageName = webViewPackageInfo.packageName // Create the spannable string builder. val webViewProviderStringBuilder = SpannableStringBuilder(webViewProviderLabel + webViewPackageName) // Apply the coloration. webViewProviderStringBuilder.setSpan(blueColorSpan, webViewProviderLabel.length, webViewProviderStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the WebView provider. webViewProviderTextView.text = webViewProviderStringBuilder } else { // The API < 21. // Hide the WebView provider text view. webViewProviderTextView.visibility = View.GONE } // Only populate the Orbot text view if it is installed. if (orbot.isNotEmpty()) { // Setup the label. val orbotLabel = getString(R.string.orbot) + " " // Create a spannable string builder. val orbotStringBuilder = SpannableStringBuilder(orbotLabel + orbot) // Set the span to display the Orbot version. orbotStringBuilder.setSpan(blueColorSpan, orbotLabel.length, orbotStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the string in the text view. orbotTextView.text = orbotStringBuilder } else { // Orbot is not installed. // Hide the Orbot text view. orbotTextView.visibility = View.GONE } // Only populate the I2P text view if it is installed. if (i2p.isNotEmpty()) { // Setup the label. val i2pLabel = getString(R.string.i2p) + " " // Create a spannable string builder. val i2pStringBuilder = SpannableStringBuilder(i2pLabel + i2p) // Set the span to display the I2P version. i2pStringBuilder.setSpan(blueColorSpan, i2pLabel.length, i2pStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the string in the text view. i2pTextView.text = i2pStringBuilder } else { // I2P is not installed. // Hide the I2P text view. i2pTextView.visibility = View.GONE } // Only populate the OpenKeychain text view if it is installed. if (openKeychain.isNotEmpty()) { // Setup the label. val openKeychainLabel = getString(R.string.openkeychain) + " " // Create a spannable string builder. val openKeychainStringBuilder = SpannableStringBuilder(openKeychainLabel + openKeychain) // Set the span to display the OpenKeychain version. openKeychainStringBuilder.setSpan(blueColorSpan, openKeychainLabel.length, openKeychainStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the string in the text view. openKeychainTextView.text = openKeychainStringBuilder } else { //OpenKeychain is not installed. // Hide the OpenKeychain text view. openKeychainTextView.visibility = View.GONE } // Display the package signature. try { // Get the first package signature. Suppress the lint warning about the need to be careful in implementing comparison of certificates for security purposes. // Once the minimum API >= 28, `GET_SIGNING_CERTIFICATES` can be used instead. @SuppressLint("PackageManagerGetSignatures") val packageSignature = requireContext().packageManager.getPackageInfo(requireContext().packageName,PackageManager.GET_SIGNATURES) .signatures[0] // Convert the signature to a byte array input stream. val certificateByteArrayInputStream: InputStream = ByteArrayInputStream(packageSignature.toByteArray()) // Display the certificate information on the screen. try { // Instantiate a certificate factory. val certificateFactory = CertificateFactory.getInstance("X509") // Generate an X509 certificate. val x509Certificate = certificateFactory.generateCertificate(certificateByteArrayInputStream) as X509Certificate // Store the individual sections of the certificate. val issuerDNPrincipal = x509Certificate.issuerDN val subjectDNPrincipal = x509Certificate.subjectDN val startDate = x509Certificate.notBefore val endDate = x509Certificate.notAfter val certificateVersion = x509Certificate.version val serialNumberBigInteger = x509Certificate.serialNumber val signatureAlgorithmNameString = x509Certificate.sigAlgName // Create a spannable string builder for each text view that needs multiple colors of text. val issuerDNStringBuilder = SpannableStringBuilder(issuerDNLabel + issuerDNPrincipal.toString()) val subjectDNStringBuilder = SpannableStringBuilder(subjectDNLabel + subjectDNPrincipal.toString()) val startDateStringBuilder = SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(startDate)) val endDataStringBuilder = SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(endDate)) val certificateVersionStringBuilder = SpannableStringBuilder(certificateVersionLabel + certificateVersion) val serialNumberStringBuilder = SpannableStringBuilder(serialNumberLabel + serialNumberBigInteger) val signatureAlgorithmStringBuilder = SpannableStringBuilder(signatureAlgorithmLabel + signatureAlgorithmNameString) // Setup the spans to display the device information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. issuerDNStringBuilder.setSpan(blueColorSpan, issuerDNLabel.length, issuerDNStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) subjectDNStringBuilder.setSpan(blueColorSpan, subjectDNLabel.length, subjectDNStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) endDataStringBuilder.setSpan(blueColorSpan, endDateLabel.length, endDataStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) certificateVersionStringBuilder.setSpan(blueColorSpan, certificateVersionLabel.length, certificateVersionStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) serialNumberStringBuilder.setSpan(blueColorSpan, serialNumberLabel.length, serialNumberStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) signatureAlgorithmStringBuilder.setSpan(blueColorSpan, signatureAlgorithmLabel.length, signatureAlgorithmStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the strings in the text boxes. certificateIssuerDnTextView.text = issuerDNStringBuilder certificateSubjectDnTextView.text = subjectDNStringBuilder certificateStartDateTextView.text = startDateStringBuilder certificateEndDateTextView.text = endDataStringBuilder certificateVersionTextView.text = certificateVersionStringBuilder certificateSerialNumberTextView.text = serialNumberStringBuilder certificateSignatureAlgorithmTextView.text = signatureAlgorithmStringBuilder } catch (certificateException: CertificateException) { // Do nothing if there is a certificate error. } // Get a handle for the runtime. runtime = Runtime.getRuntime() // Get a handle for the activity manager. activityManager = requireActivity().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager // Instantiate a memory info variable. memoryInfo = ActivityManager.MemoryInfo() // Define a number format. numberFormat = NumberFormat.getInstance() // Set the minimum and maximum number of fraction digits. numberFormat.minimumFractionDigits = 2 numberFormat.maximumFractionDigits = 2 // Update the memory usage. updateMemoryUsage(requireActivity()) } catch (e: PackageManager.NameNotFoundException) { // Do nothing if the package manager says Privacy Browser isn't installed. } // Scroll the tab if the saved instance state is not null. if (savedInstanceState != null) { aboutVersionLayout.post { aboutVersionLayout.scrollX = savedInstanceState.getInt("scroll_x") aboutVersionLayout.scrollY = savedInstanceState.getInt("scroll_y") } } // Return the tab layout. return aboutVersionLayout } override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) { // Inflate the about version menu. menuInflater.inflate(R.menu.about_version_options_menu, menu) // Run the default commands. super.onCreateOptionsMenu(menu, menuInflater) } override fun onOptionsItemSelected(menuItem: MenuItem): Boolean { // Run the appropriate commands. when (menuItem.itemId) { R.id.copy -> { // Copy. // Get the about version string. val aboutVersionString = getAboutVersionString() // Get a handle for the clipboard manager. val clipboardManager = (requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager) // Save the about version string in a clip data. val aboutVersionClipData = ClipData.newPlainText(getString(R.string.about), aboutVersionString) // Place the clip data on the clipboard. clipboardManager.setPrimaryClip(aboutVersionClipData) // Display a snackbar. Snackbar.make(aboutVersionLayout, R.string.version_info_copied, Snackbar.LENGTH_SHORT).show() // Consume the event. return true } R.id.share -> { // Share. // Get the about version string. val aboutString = getAboutVersionString() // Create an email intent. val emailIntent = Intent(Intent.ACTION_SEND) // Add the about version string to the intent. emailIntent.putExtra(Intent.EXTRA_TEXT, aboutString) // Set the MIME type. emailIntent.type = "text/plain" // Set the intent to open in a new task. emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // Make it so. startActivity(Intent.createChooser(emailIntent, getString(R.string.share))) // Consume the event. return true } R.id.save_text -> { // Save text. // Open the file picker. saveAboutVersionTextActivityResultLauncher.launch(getString(R.string.privacy_browser_version_txt, BuildConfig.VERSION_NAME)) // Consume the event. return true } R.id.save_image -> { // Save image. // Open the file picker. saveAboutVersionImageActivityResultLauncher.launch(getString(R.string.privacy_browser_version_png, BuildConfig.VERSION_NAME)) // Consume the event. return true } else -> { // The home button was selected. // Run the parents class on return. return super.onOptionsItemSelected(menuItem) } } } override fun onSaveInstanceState(savedInstanceState: Bundle) { // Run the default commands. super.onSaveInstanceState(savedInstanceState) // Save the scroll positions. savedInstanceState.putInt("scroll_x", aboutVersionLayout.scrollX) savedInstanceState.putInt("scroll_y", aboutVersionLayout.scrollY) } override fun onPause() { // Run the default commands. super.onPause() // Pause the updating of the memory usage. updateMemoryUsageBoolean = false } override fun onResume() { // Run the default commands. super.onResume() // Resume the updating of the memory usage. updateMemoryUsageBoolean = true } fun updateMemoryUsage(activity: Activity) { try { // Update the memory usage if enabled. if (updateMemoryUsageBoolean) { // Populate the memory info variable. activityManager.getMemoryInfo(memoryInfo) // Get the app memory information. val appAvailableMemoryLong = runtime.freeMemory() val appTotalMemoryLong = runtime.totalMemory() val appMaximumMemoryLong = runtime.maxMemory() // Calculate the app consumed memory. val appConsumedMemoryLong = appTotalMemoryLong - appAvailableMemoryLong // Get the system memory information. val systemTotalMemoryLong = memoryInfo.totalMem val systemAvailableMemoryLong = memoryInfo.availMem // Calculate the system consumed memory. val systemConsumedMemoryLong = systemTotalMemoryLong - systemAvailableMemoryLong // Convert the memory information into mebibytes. val appConsumedMemoryFloat = appConsumedMemoryLong.toFloat() / MEBIBYTE val appAvailableMemoryFloat = appAvailableMemoryLong.toFloat() / MEBIBYTE val appTotalMemoryFloat = appTotalMemoryLong.toFloat() / MEBIBYTE val appMaximumMemoryFloat = appMaximumMemoryLong.toFloat() / MEBIBYTE val systemConsumedMemoryFloat = systemConsumedMemoryLong.toFloat() / MEBIBYTE val systemAvailableMemoryFloat = systemAvailableMemoryLong.toFloat() / MEBIBYTE val systemTotalMemoryFloat = systemTotalMemoryLong.toFloat() / MEBIBYTE // Get the mebibyte string. val mebibyte = getString(R.string.mebibyte) // Calculate the mebibyte length. val mebibyteLength = mebibyte.length // Create spannable string builders. val appConsumedMemoryStringBuilder = SpannableStringBuilder(appConsumedMemoryLabel + numberFormat.format(appConsumedMemoryFloat.toDouble()) + " " + mebibyte) val appAvailableMemoryStringBuilder = SpannableStringBuilder(appAvailableMemoryLabel + numberFormat.format(appAvailableMemoryFloat.toDouble()) + " " + mebibyte) val appTotalMemoryStringBuilder = SpannableStringBuilder(appTotalMemoryLabel + numberFormat.format(appTotalMemoryFloat.toDouble()) + " " + mebibyte) val appMaximumMemoryStringBuilder = SpannableStringBuilder(appMaximumMemoryLabel + numberFormat.format(appMaximumMemoryFloat.toDouble()) + " " + mebibyte) val systemConsumedMemoryStringBuilder = SpannableStringBuilder(systemConsumedMemoryLabel + numberFormat.format(systemConsumedMemoryFloat.toDouble()) + " " + mebibyte) val systemAvailableMemoryStringBuilder = SpannableStringBuilder(systemAvailableMemoryLabel + numberFormat.format(systemAvailableMemoryFloat.toDouble()) + " " + mebibyte) val systemTotalMemoryStringBuilder = SpannableStringBuilder(systemTotalMemoryLabel + numberFormat.format(systemTotalMemoryFloat.toDouble()) + " " + mebibyte) // Setup the spans to display the memory information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. appConsumedMemoryStringBuilder.setSpan(blueColorSpan, appConsumedMemoryLabel.length, appConsumedMemoryStringBuilder.length - mebibyteLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE) appAvailableMemoryStringBuilder.setSpan(blueColorSpan, appAvailableMemoryLabel.length, appAvailableMemoryStringBuilder.length - mebibyteLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE) appTotalMemoryStringBuilder.setSpan(blueColorSpan, appTotalMemoryLabel.length, appTotalMemoryStringBuilder.length - mebibyteLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE) appMaximumMemoryStringBuilder.setSpan(blueColorSpan, appMaximumMemoryLabel.length, appMaximumMemoryStringBuilder.length - mebibyteLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE) systemConsumedMemoryStringBuilder.setSpan(blueColorSpan, systemConsumedMemoryLabel.length, systemConsumedMemoryStringBuilder.length - mebibyteLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE) systemAvailableMemoryStringBuilder.setSpan(blueColorSpan, systemAvailableMemoryLabel.length, systemAvailableMemoryStringBuilder.length - mebibyteLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE) systemTotalMemoryStringBuilder.setSpan(blueColorSpan, systemTotalMemoryLabel.length, systemTotalMemoryStringBuilder.length - mebibyteLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE) // Display the string in the text boxes. appConsumedMemoryTextView.text = appConsumedMemoryStringBuilder appAvailableMemoryTextView.text = appAvailableMemoryStringBuilder appTotalMemoryTextView.text = appTotalMemoryStringBuilder appMaximumMemoryTextView.text = appMaximumMemoryStringBuilder systemConsumedMemoryTextView.text = systemConsumedMemoryStringBuilder systemAvailableMemoryTextView.text = systemAvailableMemoryStringBuilder systemTotalMemoryTextView.text = systemTotalMemoryStringBuilder } // Schedule another memory update if the activity has not been destroyed. if (!activity.isDestroyed) { // Create a handler to update the memory usage. val updateMemoryUsageHandler = Handler(Looper.getMainLooper()) // Create a runnable to update the memory usage. val updateMemoryUsageRunnable = Runnable { updateMemoryUsage(activity) } // Update the memory usage after 1000 milliseconds updateMemoryUsageHandler.postDelayed(updateMemoryUsageRunnable, 1000) } } catch (exception: Exception) { // Do nothing. } } fun getAboutVersionString(): String { // Initialize an about version string builder. val aboutVersionStringBuilder = StringBuilder() // Populate the about version string builder. aboutVersionStringBuilder.append(privacyBrowserTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(versionTextView.text) aboutVersionStringBuilder.append("\n\n") aboutVersionStringBuilder.append(hardwareTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(brandTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(manufacturerTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(modelTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(deviceTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(bootloaderTextView.text) aboutVersionStringBuilder.append("\n") if (radioTextView.visibility == View.VISIBLE) { aboutVersionStringBuilder.append(radioTextView.text) aboutVersionStringBuilder.append("\n") } aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(softwareTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(androidTextView.text) aboutVersionStringBuilder.append("\n") if (securityPatchTextView.visibility == View.VISIBLE) { aboutVersionStringBuilder.append(securityPatchTextView.text) aboutVersionStringBuilder.append("\n") } aboutVersionStringBuilder.append(buildTextView.text) aboutVersionStringBuilder.append("\n") if (webViewProviderTextView.visibility == View.VISIBLE) { aboutVersionStringBuilder.append(webViewProviderTextView.text) aboutVersionStringBuilder.append("\n") } aboutVersionStringBuilder.append(webViewVersionTextView.text) aboutVersionStringBuilder.append("\n") if (orbotTextView.visibility == View.VISIBLE) { aboutVersionStringBuilder.append(orbotTextView.text) aboutVersionStringBuilder.append("\n") } if (i2pTextView.visibility == View.VISIBLE) { aboutVersionStringBuilder.append(i2pTextView.text) aboutVersionStringBuilder.append("\n") } if (openKeychainTextView.visibility == View.VISIBLE) { aboutVersionStringBuilder.append(openKeychainTextView.text) aboutVersionStringBuilder.append("\n") } aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(memoryUsageTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(appConsumedMemoryTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(appAvailableMemoryTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(appTotalMemoryTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(appMaximumMemoryTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(systemConsumedMemoryTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(systemAvailableMemoryTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(systemTotalMemoryTextView.text) aboutVersionStringBuilder.append("\n\n") aboutVersionStringBuilder.append(blocklistsTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(easyListTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(easyPrivacyTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(fanboyAnnoyanceTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(fanboySocialTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(ultraListTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(ultraPrivacyTextView.text) aboutVersionStringBuilder.append("\n\n") aboutVersionStringBuilder.append(packageSignatureTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(certificateIssuerDnTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(certificateSubjectDnTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(certificateStartDateTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(certificateEndDateTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(certificateVersionTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(certificateSerialNumberTextView.text) aboutVersionStringBuilder.append("\n") aboutVersionStringBuilder.append(certificateSignatureAlgorithmTextView.text) // Return the string. return aboutVersionStringBuilder.toString() } }