X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Ffragments%2FAboutVersionFragment.kt;h=7dfeda99553fd10c2e77d6ba3122417cbd014c2c;hb=HEAD;hp=ceb060b796c38e51a768379208f35edfa66f0216;hpb=f95501b3f5d609aac90ef75acad30b7d19448c3d;p=PrivacyBrowserAndroid.git diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt index ceb060b7..64a7dda4 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/AboutVersionFragment.kt @@ -1,20 +1,20 @@ -/* - * Copyright 2016-2023 Soren Stoutner . +/* SPDX-License-Identifier: GPL-3.0-or-later + * SPDX-FileCopyrightText: 2016-2025 Soren Stoutner * - * This file is part of Privacy Browser Android . + * This file is part of Privacy Browser Android . * - * Privacy Browser Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. * - * Privacy Browser Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. * - * You should have received a copy of the GNU General Public License - * along with Privacy Browser Android. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ package com.stoutner.privacybrowser.fragments @@ -41,11 +41,14 @@ 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.core.view.MenuHost +import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment -import androidx.webkit.WebViewCompat +import androidx.lifecycle.Lifecycle import com.google.android.material.snackbar.Snackbar @@ -71,7 +74,8 @@ import java.text.NumberFormat import kotlin.text.StringBuilder // Define the class constants. -private const val FILTERLISTS_VERSIONS = "filterlists_versions" +private const val FILTERLISTS_VERSIONS = "A" +private const val SCROLL_Y = "B" private const val MEBIBYTE = 1048576 class AboutVersionFragment : Fragment() { @@ -161,23 +165,17 @@ class AboutVersionFragment : Fragment() { private val saveAboutVersionTextActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { fileUri -> // Only save the file if the URI is not null, which happens if the user exited the file picker by pressing back. if (fileUri != null) { - // Initialize the file name string from the file URI last path segment. - var fileNameString = fileUri.lastPathSegment + // Get a cursor from the content resolver. + val contentResolverCursor = requireActivity().contentResolver.query(fileUri, null, null, null)!! - // 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(fileUri, null, null, null)!! + // Move to the first row. + contentResolverCursor.moveToFirst() - // Move to the first row. - contentResolverCursor.moveToFirst() + // Get the file name from the cursor. + val fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) - // Get the file name from the cursor. - fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) - - // Close the cursor. - contentResolverCursor.close() - } + // Close the cursor. + contentResolverCursor.close() try { // Get the about version string. @@ -219,12 +217,88 @@ class AboutVersionFragment : Fragment() { // Store the arguments in class variables. filterListsVersions = requireArguments().getStringArray(FILTERLISTS_VERSIONS)!! - - // Enable the options menu for this fragment. - setHasOptionsMenu(true) } override fun onCreateView(layoutInflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + // Add an options menu. + (requireActivity() as MenuHost).addMenuProvider(object : MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + // Inflate the about version menu. + menuInflater.inflate(R.menu.about_version_options_menu, menu) + } + + override fun onMenuItemSelected(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) + + // Place 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 if the API <= 32 (Android 12L). Beginning in Android 13 the OS displays a notification that covers up the snackbar. + if (Build.VERSION.SDK_INT <= 32) + 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 a share intent. + val shareIntent = Intent(Intent.ACTION_SEND) + + // Add the about version string to the intent. + shareIntent.putExtra(Intent.EXTRA_TEXT, aboutString) + + // Set the MIME type. + shareIntent.type = "text/plain" + + // Set the intent to open in a new task. + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + // Make it so. + startActivity(Intent.createChooser(shareIntent, 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. + // Do not consume the event. + return false + } + } + } + + }, viewLifecycleOwner, Lifecycle.State.RESUMED) + // 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) @@ -306,8 +380,8 @@ class AboutVersionFragment : Fragment() { val serialNumberLabel = getString(R.string.serial_number) val signatureAlgorithmLabel = getString(R.string.signature_algorithm) - // Get the current WebView package info. This can be replaced by the direct call once the minimum API >= 26. - val webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(requireContext())!! + // Get the current WebView package info. + val webViewPackageInfo = WebView.getCurrentWebViewPackage()!! // Get the device's information and store it in strings. val brand = Build.BRAND @@ -325,9 +399,9 @@ class AboutVersionFragment : Fragment() { // 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. + // If the safe call (`?.`) is null, the Elvis operator (`?"`) returns the following value instead, which is an empty string. + requireContext().packageManager.getPackageInfo("org.torproject.android", 0).versionName ?: "" + } catch (exception: PackageManager.NameNotFoundException) { // Store an empty string. "" } @@ -348,9 +422,9 @@ class AboutVersionFragment : Fragment() { // 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. + // If the safe call (`?.`) is null, the Elvis operator (`?"`) returns the following value instead, which is an empty string. + requireContext().packageManager.getPackageInfo("org.sufficientlysecure.keychain", 0).versionName ?: "" + } catch (exception: PackageManager.NameNotFoundException) { // Store an empty string. "" } @@ -494,8 +568,7 @@ class AboutVersionFragment : Fragment() { // 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. Once the minimum API >= 33, the newer `getPackageInfo()` may be used. @Suppress("DEPRECATION") - @SuppressLint("PackageManagerGetSignatures") val packageSignature = requireContext().packageManager.getPackageInfo(requireContext().packageName,PackageManager.GET_SIGNATURES) - .signatures[0] + @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()) @@ -572,8 +645,7 @@ class AboutVersionFragment : Fragment() { // 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") + aboutVersionLayout.scrollY = savedInstanceState.getInt(SCROLL_Y) } } @@ -581,91 +653,12 @@ class AboutVersionFragment : Fragment() { 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) - - // Place 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 if the API <= 32 (Android 12L). Beginning in Android 13 the OS displays a notification that covers up the snackbar. - if (Build.VERSION.SDK_INT <= 32) - 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 a share intent. - val shareIntent = Intent(Intent.ACTION_SEND) - - // Add the about version string to the intent. - shareIntent.putExtra(Intent.EXTRA_TEXT, aboutString) - - // Set the MIME type. - shareIntent.type = "text/plain" - - // Set the intent to open in a new task. - shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - - // Make it so. - startActivity(Intent.createChooser(shareIntent, 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) + // Save the scroll position. + savedInstanceState.putInt(SCROLL_Y, aboutVersionLayout.scrollY) } override fun onPause() {