]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt
Convert LogcatActivity to Kotlin. https://redmine.stoutner.com/issues/770
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / LogcatActivity.kt
diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/LogcatActivity.kt
new file mode 100644 (file)
index 0000000..23e6d52
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+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
+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.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.R
+import com.stoutner.privacybrowser.dialogs.SaveDialog
+
+import java.io.BufferedReader
+import java.io.IOException
+import java.io.InputStreamReader
+import java.lang.Exception
+import java.nio.charset.StandardCharsets
+
+// Define the class constants.
+private const val SCROLLVIEW_POSITION = "scrollview_position"
+
+class LogcatActivity : AppCompatActivity(), SaveDialog.SaveListener {
+    // Define the class variables.
+    private var scrollViewYPositionInt = 0
+
+    // Define the class views.
+    private lateinit var swipeRefreshLayout: SwipeRefreshLayout
+    private lateinit var logcatScrollView: ScrollView
+    private lateinit var logcatTextView: TextView
+
+    public override fun onCreate(savedInstanceState: Bundle?) {
+        // Get a handle for the shared preferences.
+        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
+
+        // Get the preferences.
+        val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
+        val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+        }
+
+        // Set the theme.
+        setTheme(R.style.PrivacyBrowser)
+
+        // Run the default commands.
+        super.onCreate(savedInstanceState)
+
+        // Set the content view.
+        if (bottomAppBar) {
+            setContentView(R.layout.logcat_bottom_appbar)
+        } else {
+            setContentView(R.layout.logcat_top_appbar)
+        }
+
+        // Get handles for the views.
+        val toolbar = findViewById<Toolbar>(R.id.toolbar)
+        swipeRefreshLayout = findViewById(R.id.swiperefreshlayout)
+        logcatScrollView = findViewById(R.id.scrollview)
+        logcatTextView = findViewById(R.id.logcat_textview)
+
+        // Set the toolbar as the action bar.
+        setSupportActionBar(toolbar)
+
+        // Get a handle for the action bar.
+        val actionBar = supportActionBar!!
+
+        // Display the back arrow in the action bar.
+        actionBar.setDisplayHomeAsUpEnabled(true)
+
+        // Implement swipe to refresh.
+        swipeRefreshLayout.setOnRefreshListener {
+            // Get the current logcat.
+            getLogcat()
+        }
+
+        // Set the swipe refresh color scheme according to the theme.
+        swipeRefreshLayout.setColorSchemeResources(R.color.blue_text)
+
+        // Initialize a color background typed value.
+        val colorBackgroundTypedValue = TypedValue()
+
+        // Get the color background from the theme.
+        theme.resolveAttribute(android.R.attr.colorBackground, colorBackgroundTypedValue, true)
+
+        // Get the color background int from the typed value.
+        val colorBackgroundInt = colorBackgroundTypedValue.data
+
+        // Set the swipe refresh background color.
+        swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt)
+
+        // Check to see if the activity has been restarted.
+        if (savedInstanceState != null) {
+            // Get the saved scrollview position.
+            scrollViewYPositionInt = savedInstanceState.getInt(SCROLLVIEW_POSITION)
+        }
+
+        // Get the logcat.
+        getLogcat()
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu): Boolean {
+        // Inflate the menu.  This adds items to the action bar.
+        menuInflater.inflate(R.menu.logcat_options_menu, menu)
+
+        // Display the menu.
+        return true
+    }
+
+    override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
+        // Run the commands that correlate to the selected menu item.
+        return when (menuItem.itemId) {
+            R.id.copy -> {  // Copy was selected.
+                // Get a handle for the clipboard manager.
+                val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
+
+                // Save the logcat in a clip data.
+                val logcatClipData = ClipData.newPlainText(getString(R.string.logcat), logcatTextView.text)
+
+                // Place the clip data on the clipboard.
+                clipboardManager.setPrimaryClip(logcatClipData)
+
+                // Display a snackbar.
+                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))
+
+                // Consume the event.
+                true
+            }
+
+            R.id.clear -> {  // Clear was selected.
+                try {
+                    // Clear the logcat.  `-c` clears the logcat.  `-b all` clears all the buffers (instead of just crash, main, and system).
+                    val process = Runtime.getRuntime().exec("logcat -b all -c")
+
+                    // Wait for the process to finish.
+                    process.waitFor()
+
+                    // Reset the scroll view Y position int.
+                    scrollViewYPositionInt = 0
+
+                    // Reload the logcat.
+                    getLogcat()
+                } catch (exception: Exception) {
+                    // Do nothing.
+                }
+
+                // Consume the event.
+                true
+            }
+
+            else -> {  // The home button was pushed.
+                // Do not consume the event.  The system will process the home command.
+                super.onOptionsItemSelected(menuItem)
+            }
+        }
+    }
+
+    public override fun onSaveInstanceState(savedInstanceState: Bundle) {
+        // Run the default commands.
+        super.onSaveInstanceState(savedInstanceState)
+
+        // Get the scrollview Y position.
+        val scrollViewYPositionInt = logcatScrollView.scrollY
+
+        // Store the scrollview Y position in the bundle.
+        savedInstanceState.putInt(SCROLLVIEW_POSITION, scrollViewYPositionInt)
+    }
+
+    private fun getLogcat() {
+        try {
+            // Get the logcat.  `-b all` gets all the buffers (instead of just crash, main, and system).  `-v long` produces more complete information.  `-d` dumps the logcat and exits.
+            val getLogcatProcess = Runtime.getRuntime().exec("logcat -b all -v long -d")
+
+            // Wrap the logcat in a buffered reader.
+            val logcatBufferedReader = BufferedReader(InputStreamReader(getLogcatProcess.inputStream))
+
+            // Display the logcat.
+            logcatTextView.text = logcatBufferedReader.readText()
+
+            // Close the buffered reader.
+            logcatBufferedReader.close()
+        } catch (exception: IOException) {
+            // Do nothing.
+        }
+
+        // Update the scroll position after the text is populated.
+        logcatTextView.post {
+            // Set the scroll position.
+            logcatScrollView.scrollY = scrollViewYPositionInt
+        }
+
+        // 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<EditText>(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<EditText>(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