You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
10 KiB
Kotlin
211 lines
10 KiB
Kotlin
package com.fox2code.mmm.settings
|
|
|
|
import android.content.Context
|
|
import android.content.DialogInterface
|
|
import android.content.Intent
|
|
import android.os.Build
|
|
import android.os.Bundle
|
|
import android.widget.Toast
|
|
import androidx.core.content.FileProvider
|
|
import androidx.preference.Preference
|
|
import androidx.preference.PreferenceFragmentCompat
|
|
import androidx.security.crypto.EncryptedSharedPreferences
|
|
import androidx.security.crypto.MasterKey
|
|
import com.fox2code.mmm.BuildConfig
|
|
import com.fox2code.mmm.Constants
|
|
import com.fox2code.mmm.MainApplication
|
|
import com.fox2code.mmm.R
|
|
import com.fox2code.mmm.installer.InstallerInitializer
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
import org.apache.commons.io.FileUtils
|
|
import timber.log.Timber
|
|
import java.io.BufferedReader
|
|
import java.io.File
|
|
import java.io.FileOutputStream
|
|
import java.io.IOException
|
|
import java.io.InputStreamReader
|
|
import java.util.Date
|
|
|
|
class DebugFragment : PreferenceFragmentCompat() {
|
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
val name = "mmmx"
|
|
val context: Context? = MainApplication.INSTANCE
|
|
val masterKey: MasterKey
|
|
val preferenceManager = preferenceManager
|
|
val dataStore: SharedPreferenceDataStore
|
|
try {
|
|
masterKey =
|
|
MasterKey.Builder(context!!).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
|
|
dataStore = SharedPreferenceDataStore(
|
|
EncryptedSharedPreferences.create(
|
|
context,
|
|
name,
|
|
masterKey,
|
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
)
|
|
)
|
|
preferenceManager!!.preferenceDataStore = dataStore
|
|
preferenceManager.sharedPreferencesName = "mmm"
|
|
} catch (e: Exception) {
|
|
Timber.e(e, "Failed to create encrypted shared preferences")
|
|
throw RuntimeException(getString(R.string.error_encrypted_shared_preferences))
|
|
}
|
|
setPreferencesFromResource(R.xml.debugging_preferences, rootKey)
|
|
|
|
|
|
if (!MainApplication.isDeveloper) {
|
|
findPreference<Preference>("pref_disable_low_quality_module_filter")!!.isVisible = false
|
|
// Find pref_clear_data and set it invisible
|
|
findPreference<Preference>("pref_clear_data")!!.isVisible = false
|
|
}
|
|
// hande clear cache
|
|
findPreference<Preference>("pref_clear_cache")!!.onPreferenceClickListener =
|
|
Preference.OnPreferenceClickListener { _: Preference? ->
|
|
// Clear cache
|
|
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.clear_cache_dialogue_title)
|
|
.setMessage(
|
|
R.string.clear_cache_dialogue_message
|
|
).setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
|
|
// Clear app cache
|
|
try {
|
|
// use apache commons IO to delete the cache
|
|
FileUtils.deleteDirectory(requireContext().cacheDir)
|
|
// create a new cache dir
|
|
FileUtils.forceMkdir(requireContext().cacheDir)
|
|
// create cache dirs for cronet and webview
|
|
FileUtils.forceMkdir(File(requireContext().cacheDir, "cronet"))
|
|
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/wasm"))
|
|
FileUtils.forceMkdir(File(MainApplication.INSTANCE!!.dataDir.toString() + "/cache/WebView/Default/HTTP Cache/Code Cache/js"))
|
|
Toast.makeText(
|
|
requireContext(), R.string.cache_cleared, Toast.LENGTH_SHORT
|
|
).show()
|
|
} catch (e: Exception) {
|
|
Timber.e(e)
|
|
Toast.makeText(
|
|
requireContext(), R.string.cache_clear_failed, Toast.LENGTH_SHORT
|
|
).show()
|
|
}
|
|
}.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> }.show()
|
|
true
|
|
}
|
|
if (!BuildConfig.DEBUG || InstallerInitializer.peekMagiskPath() == null) {
|
|
// Hide the pref_crash option if not in debug mode - stop users from purposely crashing the app
|
|
Timber.i(InstallerInitializer.peekMagiskPath())
|
|
findPreference<Preference?>("pref_test_crash")!!.isVisible = false
|
|
} else {
|
|
if (findPreference<Preference?>("pref_test_crash") != null && findPreference<Preference?>(
|
|
"pref_clear_data"
|
|
) != null
|
|
) {
|
|
findPreference<Preference>("pref_test_crash")!!.onPreferenceClickListener =
|
|
Preference.OnPreferenceClickListener { _: Preference? ->
|
|
throw RuntimeException("This is a test crash with a stupidly long description to show off the crash handler. Are we having fun yet?")
|
|
}
|
|
findPreference<Preference>("pref_clear_data")!!.onPreferenceClickListener =
|
|
Preference.OnPreferenceClickListener { _: Preference? ->
|
|
// Clear app data
|
|
MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.clear_data_dialogue_title)
|
|
.setMessage(
|
|
R.string.clear_data_dialogue_message
|
|
).setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
|
|
// Clear app data
|
|
MainApplication.INSTANCE!!.resetApp()
|
|
}.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int -> }
|
|
.show()
|
|
true
|
|
}
|
|
} else {
|
|
Timber.e(
|
|
"Something is null: %s, %s",
|
|
findPreference("pref_clear_data"),
|
|
findPreference("pref_test_crash")
|
|
)
|
|
}
|
|
}
|
|
if (InstallerInitializer.peekMagiskVersion() < Constants.MAGISK_VER_CODE_INSTALL_COMMAND || !MainApplication.isDeveloper || InstallerInitializer.isKsu) {
|
|
findPreference<Preference>("pref_use_magisk_install_command")!!.isVisible = false
|
|
}
|
|
|
|
|
|
// handle pref_save_logs which saves logs to our external storage and shares them
|
|
val saveLogs = findPreference<Preference>("pref_save_logs")
|
|
saveLogs!!.onPreferenceClickListener =
|
|
Preference.OnPreferenceClickListener setOnPreferenceClickListener@{ _: Preference? ->
|
|
// Save logs to external storage
|
|
val logsFile = File(requireContext().getExternalFilesDir(null), "logs.txt")
|
|
var fileOutputStream: FileOutputStream? = null
|
|
try {
|
|
logsFile.createNewFile()
|
|
fileOutputStream = FileOutputStream(logsFile)
|
|
// first, some device and app info: namely device oem and model, android version and build, app version and build
|
|
fileOutputStream.write(
|
|
String.format(
|
|
"Device: %s %s\nAndroid Version: %s\nROM: %s\nApp Version: %s (%s)\n\n",
|
|
Build.MANUFACTURER,
|
|
Build.MODEL,
|
|
Build.VERSION.RELEASE,
|
|
Build.FINGERPRINT,
|
|
BuildConfig.VERSION_NAME,
|
|
BuildConfig.VERSION_CODE
|
|
).toByteArray()
|
|
)
|
|
// next, the logs
|
|
// get our logs from logcat
|
|
val process = Runtime.getRuntime().exec("logcat -d")
|
|
val bufferedReader = BufferedReader(
|
|
InputStreamReader(process.inputStream)
|
|
)
|
|
var line: String?
|
|
val iterator: Iterator<String> = bufferedReader.lines().iterator()
|
|
while (iterator.hasNext()) {
|
|
line = iterator.next()
|
|
fileOutputStream.write(line.toByteArray())
|
|
fileOutputStream.write("\n".toByteArray())
|
|
}
|
|
fileOutputStream.flush()
|
|
Toast.makeText(
|
|
requireContext(), R.string.logs_saved, Toast.LENGTH_SHORT
|
|
).show()
|
|
} catch (e: IOException) {
|
|
e.printStackTrace()
|
|
Toast.makeText(
|
|
requireContext(), R.string.error_saving_logs, Toast.LENGTH_SHORT
|
|
).show()
|
|
return@setOnPreferenceClickListener true
|
|
} finally {
|
|
if (fileOutputStream != null) {
|
|
try {
|
|
fileOutputStream.close()
|
|
} catch (ignored: IOException) {
|
|
}
|
|
}
|
|
}
|
|
// save logs to our external storage - name is current date and time
|
|
try {
|
|
val extStorage = File(requireContext().getExternalFilesDir(null), "logs" + File.separator + "log-" + Date().toString() + ".txt")
|
|
FileUtils.copyFile(logsFile, extStorage)
|
|
} catch (e: IOException) {
|
|
e.printStackTrace()
|
|
Toast.makeText(
|
|
requireContext(), R.string.error_saving_logs, Toast.LENGTH_SHORT
|
|
).show()
|
|
return@setOnPreferenceClickListener true
|
|
}
|
|
// Share logs
|
|
val shareIntent = Intent()
|
|
// create a new intent and grantUriPermission to the file provider
|
|
shareIntent.action = Intent.ACTION_SEND
|
|
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
shareIntent.putExtra(
|
|
Intent.EXTRA_STREAM, FileProvider.getUriForFile(
|
|
requireContext(), BuildConfig.APPLICATION_ID + ".file-provider", logsFile
|
|
)
|
|
)
|
|
shareIntent.type = "text/plain"
|
|
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_logs)))
|
|
true
|
|
}
|
|
}
|
|
|
|
} |