X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FLogcatActivity.kt;h=367511041dbc0376e6246627c1a7ac88b38c8e5f;hb=cbeede13395a246b8a32adebbee3872031259f82;hp=23e6d525d44dfecd441d60a60f325999622df2dd;hpb=322b36f275782a06ed66b950083f28cc37f5690a;p=PrivacyBrowserAndroid.git diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt index 23e6d525..36751104 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt @@ -1,28 +1,26 @@ /* - * Copyright © 2019-2021 Soren Stoutner . + * Copyright 2019-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.ClipData import android.content.ClipboardManager -import android.content.Intent -import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.OpenableColumns @@ -30,20 +28,24 @@ import android.util.TypedValue import android.view.Menu import android.view.MenuItem import android.view.WindowManager -import android.widget.EditText import android.widget.TextView import android.widget.ScrollView +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar -import androidx.fragment.app.DialogFragment import androidx.preference.PreferenceManager import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.snackbar.Snackbar +import com.stoutner.privacybrowser.BuildConfig import com.stoutner.privacybrowser.R -import com.stoutner.privacybrowser.dialogs.SaveDialog + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.BufferedReader import java.io.IOException @@ -54,7 +56,7 @@ import java.nio.charset.StandardCharsets // Define the class constants. private const val SCROLLVIEW_POSITION = "scrollview_position" -class LogcatActivity : AppCompatActivity(), SaveDialog.SaveListener { +class LogcatActivity : AppCompatActivity() { // Define the class variables. private var scrollViewYPositionInt = 0 @@ -63,6 +65,55 @@ class LogcatActivity : AppCompatActivity(), SaveDialog.SaveListener { private lateinit var logcatScrollView: ScrollView private lateinit var logcatTextView: TextView + // Define the save logcat activity result launcher. It must be defined before `onCreate()` is run or the app will crash. + private val saveLogcatActivityResultLauncher = 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) { + try { + // Get the logcat string. + val logcatString = logcatTextView.text.toString() + + // Open an output stream. + val outputStream = contentResolver.openOutputStream(fileUri)!! + + // Save the logcat using a coroutine with Dispatchers.IO. + CoroutineScope(Dispatchers.Main).launch { + withContext(Dispatchers.IO) { + // Write the logcat string to the output stream. + outputStream.write(logcatString.toByteArray(StandardCharsets.UTF_8)) + + // Close the output stream. + outputStream.close() + } + } + + // Initialize the file name string from the file URI last path segment. + var fileNameString = fileUri.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 = contentResolver.query(fileUri, null, null, null)!! + + // Move to the fist row. + contentResolverCursor.moveToFirst() + + // Get the file name from the cursor. + fileNameString = contentResolverCursor.getString(contentResolverCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)) + + // Close the cursor. + contentResolverCursor.close() + } + + // Display a snackbar with the saved logcat information. + Snackbar.make(logcatTextView, getString(R.string.saved, fileNameString), Snackbar.LENGTH_SHORT).show() + } catch (exception: Exception) { + // Display a snackbar with the error message. + Snackbar.make(logcatTextView, getString(R.string.error_saving_logcat, exception.toString()), Snackbar.LENGTH_INDEFINITE).show() + } + } + } + public override fun onCreate(savedInstanceState: Bundle?) { // Get a handle for the shared preferences. val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) @@ -76,9 +127,6 @@ class LogcatActivity : AppCompatActivity(), SaveDialog.SaveListener { window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) } - // Set the theme. - setTheme(R.style.PrivacyBrowser) - // Run the default commands. super.onCreate(savedInstanceState) @@ -156,19 +204,17 @@ class LogcatActivity : AppCompatActivity(), SaveDialog.SaveListener { // Place the clip data on the clipboard. clipboardManager.setPrimaryClip(logcatClipData) - // Display a snackbar. - Snackbar.make(logcatTextView, R.string.logcat_copied, Snackbar.LENGTH_SHORT).show() + // 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(logcatTextView, R.string.logcat_copied, Snackbar.LENGTH_SHORT).show() // Consume the event. true } R.id.save -> { // Save was selected. - // Instantiate the save alert dialog. - val saveDialogFragment: DialogFragment = SaveDialog.save(SaveDialog.SAVE_LOGCAT) - - // Show the save alert dialog. - saveDialogFragment.show(supportFragmentManager, getString(R.string.save_logcat)) + // Open the file picker. + saveLogcatActivityResultLauncher.launch(getString(R.string.privacy_browser_logcat_txt, BuildConfig.VERSION_NAME)) // Consume the event. true @@ -239,83 +285,4 @@ class LogcatActivity : AppCompatActivity(), SaveDialog.SaveListener { // Stop the swipe to refresh animation if it is displayed. swipeRefreshLayout.isRefreshing = false } - - // The activity result is called after browsing for a file in the save alert dialog. - public override fun onActivityResult(requestCode: Int, resultCode: Int, returnedIntent: Intent?) { - // Run the default commands. - super.onActivityResult(requestCode, resultCode, returnedIntent) - - // Only do something if the user didn't press back from the file picker. - if (resultCode == RESULT_OK) { - // Get a handle for the save dialog fragment. - val saveDialogFragment = supportFragmentManager.findFragmentByTag(getString(R.string.save_logcat)) as DialogFragment? - - // Only update the file name if the dialog still exists. - if (saveDialogFragment != null) { - // Get a handle for the save dialog. - val saveDialog = saveDialogFragment.dialog!! - - // Get a handle for the file name edit text. - val fileNameEditText = saveDialog.findViewById(R.id.file_name_edittext) - - // Get the file name URI from the intent. - val fileNameUri = returnedIntent!!.data - - // Get the file name string from the URI. - val fileNameString = fileNameUri.toString() - - // Set the file name text. - fileNameEditText.setText(fileNameString) - - // Move the cursor to the end of the file name edit text. - fileNameEditText.setSelection(fileNameString.length) - } - } - } - - override fun onSave(saveType: Int, dialogFragment: DialogFragment) { - // Get a handle for the dialog. - val dialog = dialogFragment.dialog!! - - // Get a handle for the file name edit text. - val fileNameEditText = dialog.findViewById(R.id.file_name_edittext) - - // Get the file path string. - var fileNameString = fileNameEditText.text.toString() - - try { - // Get the logcat as a string. - val logcatString = logcatTextView.text.toString() - - // Open an output stream. - val outputStream = contentResolver.openOutputStream(Uri.parse(fileNameString))!! - - // Write the logcat string to the output stream. - outputStream.write(logcatString.toByteArray(StandardCharsets.UTF_8)) - - // Close the output stream. - outputStream.close() - - // Get the actual file name if the API >= 26. - if (Build.VERSION.SDK_INT >= 26) { - // Get a cursor from the content resolver. - val contentResolverCursor = contentResolver.query(Uri.parse(fileNameString), 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(logcatTextView, getString(R.string.file_saved) + " " + fileNameString, Snackbar.LENGTH_SHORT).show() - } catch (exception: Exception) { - // Display a snackbar with the error message. - Snackbar.make(logcatTextView, getString(R.string.error_saving_file) + " " + exception.toString(), Snackbar.LENGTH_INDEFINITE).show() - } - } -} \ No newline at end of file +}