/*
- * Copyright © 2016-2022 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2016-2022 Soren Stoutner <soren@stoutner.com>.
*
* This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
*
import com.stoutner.privacybrowser.BuildConfig
import com.stoutner.privacybrowser.asynctasks.SaveAboutVersionImage
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.lang.Exception
}
// 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()) { fileUri: Uri? ->
+ private val saveAboutVersionTextActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { fileUri: Uri? ->
// 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) {
- try {
- // Get the about version string.
- val aboutVersionString = getAboutVersionString()
+ // Initialize the file name string from the file URI last path segment.
+ var fileNameString = fileUri.lastPathSegment
- // Open an output stream.
- val outputStream = requireActivity().contentResolver.openOutputStream(fileUri)!!
+ // 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)!!
- // Write the about version string to the output stream.
- outputStream.write(aboutVersionString.toByteArray(StandardCharsets.UTF_8))
+ // Move to the first row.
+ contentResolverCursor.moveToFirst()
- // Close the output stream.
- outputStream.close()
+ // Get the file name from the cursor.
+ fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
- // Initialize the file name string from the file URI last path segment.
- var fileNameString = fileUri.lastPathSegment
+ // Close the cursor.
+ contentResolverCursor.close()
+ }
- // 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)!!
+ try {
+ // Get the about version string.
+ val aboutVersionString = getAboutVersionString()
- // Move to the first row.
- contentResolverCursor.moveToFirst()
+ // Open an output stream.
+ val outputStream = requireActivity().contentResolver.openOutputStream(fileUri)!!
- // Get the file name from the cursor.
- fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
+ // Save about version using a coroutine with Dispatchers.IO.
+ CoroutineScope(Dispatchers.Main).launch {
+ withContext(Dispatchers.IO) {
+ // Write the about version string to the output stream.
+ outputStream.write(aboutVersionString.toByteArray(StandardCharsets.UTF_8))
- // Close the cursor.
- contentResolverCursor.close()
+ // Close the output stream.
+ outputStream.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()
+ Snackbar.make(aboutVersionLayout, getString(R.string.error_saving_file, fileNameString, 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()) { fileUri: Uri? ->
+ private val saveAboutVersionImageActivityResultLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("image/png")) { fileUri: Uri? ->
// 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) {
// Save the about version image.
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 version = getString(R.string.version_code, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
val brandLabel = getString(R.string.brand) + " "
val manufacturerLabel = getString(R.string.manufacturer) + " "
val modelLabel = getString(R.string.model) + " "
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 fanboyAnnoyanceLabel = getString(R.string.fanboys_annoyance_label) + " "
+ val fanboySocialLabel = getString(R.string.fanboys_social_label) + " "
val ultraListLabel = getString(R.string.ultralist_label) + " "
val ultraPrivacyLabel = getString(R.string.ultraprivacy_label) + " "
val issuerDNLabel = getString(R.string.issuer_dn) + " "
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 android = getString(R.string.api, Build.VERSION.RELEASE, Build.VERSION.SDK_INT)
val build = Build.DISPLAY
val kernel = System.getProperty("os.version")
// Get the Orbot version name if Orbot is installed.
val orbot: String = try {
- // Store the version name.
+ // Store the version name. The newer `getPackageInfo()` may be used once the minimum API >= 33.
+ @Suppress("DEPRECATION")
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.
- ""
+ // Check to see if the F-Droid flavor is installed. The newer `getPackageInfo()` may be used once the minimum API >= 33.
+ @Suppress("DEPRECATION")
+ requireContext().getString(R.string.fdroid_flavor, requireContext().packageManager.getPackageInfo("net.i2p.android.router", 0).versionName)
+ } catch (exception: PackageManager.NameNotFoundException) { // The F-Droid flavor is not installed.
+ try {
+ // Check to see if the F-Droid flavor is installed. The newer `getPackageInfo()` may be used once the minimum API >= 33.
+ @Suppress("DEPRECATION")
+ requireContext().getString(R.string.google_play_flavor, requireContext().packageManager.getPackageInfo("net.i2p.android", 0).versionName)
+ } catch (exception: PackageManager.NameNotFoundException) { // The Google Play flavor is not installed either.
+ // Store an empty string.
+ ""
+ }
}
// Get the OpenKeychain version name if it is installed.
val openKeychain: String = try {
- // Store the version name.
+ // Store the version name. The newer `getPackageInfo()` may be used once the minimum API >= 33.
+ @Suppress("DEPRECATION")
requireContext().packageManager.getPackageInfo("org.sufficientlysecure.keychain", 0).versionName
} catch (exception: PackageManager.NameNotFoundException) { // OpenKeychain is not installed.
// Store an empty string.
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(requireContext().getColor(R.color.about_version_blue_text))
+ blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.alt_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)
// 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.
+ // 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]
// Return the string.
return aboutVersionStringBuilder.toString()
}
-}
\ No newline at end of file
+}