diff --git a/res/values/strings.xml b/res/values/strings.xml index 9cf1dec3..30f69c0d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -401,6 +401,9 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted Device name Dark theme + Export KDE Connect logs + Generate a file with execution information that can help troubleshoot issues. + More settings Per-device settings can be found under \'Plugin settings\' from within a device. Show persistent notification diff --git a/src/org/kde/kdeconnect/Helpers/CreateFileResultContract.kt b/src/org/kde/kdeconnect/Helpers/CreateFileResultContract.kt new file mode 100644 index 00000000..05cbbbfb --- /dev/null +++ b/src/org/kde/kdeconnect/Helpers/CreateFileResultContract.kt @@ -0,0 +1,27 @@ +package org.kde.kdeconnect.Helpers + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.activity.result.contract.ActivityResultContract + +data class CreateFileParams( + val fileMimeType: String, + val suggestedFileName: String, +) + +class CreateFileResultContract : ActivityResultContract() { + + override fun createIntent(context: Context, input: CreateFileParams): Intent = + Intent(Intent.ACTION_CREATE_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + setTypeAndNormalize(input.fileMimeType) + putExtra(Intent.EXTRA_TITLE, input.suggestedFileName) + } + + override fun parseResult(resultCode: Int, intent: Intent?): Uri? = when (resultCode) { + Activity.RESULT_OK -> intent?.data + else -> null + } +} \ No newline at end of file diff --git a/src/org/kde/kdeconnect/UserInterface/SettingsFragment.kt b/src/org/kde/kdeconnect/UserInterface/SettingsFragment.kt index a39be9c9..030a14b5 100644 --- a/src/org/kde/kdeconnect/UserInterface/SettingsFragment.kt +++ b/src/org/kde/kdeconnect/UserInterface/SettingsFragment.kt @@ -11,6 +11,7 @@ import android.content.Intent import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Color +import android.net.Uri import android.os.Build import android.os.Bundle import android.text.InputFilter @@ -20,6 +21,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.EditText +import androidx.activity.result.ActivityResultLauncher import androidx.core.content.ContextCompat import androidx.preference.EditTextPreference import androidx.preference.ListPreference @@ -27,14 +29,22 @@ import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import com.google.android.material.snackbar.Snackbar +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.apache.commons.io.IOUtils import org.kde.kdeconnect.BackgroundService +import org.kde.kdeconnect.Helpers.CreateFileParams +import org.kde.kdeconnect.Helpers.CreateFileResultContract import org.kde.kdeconnect.Helpers.DeviceHelper import org.kde.kdeconnect.Helpers.DeviceHelper.filterName import org.kde.kdeconnect.Helpers.DeviceHelper.getDeviceName import org.kde.kdeconnect.Helpers.NotificationHelper import org.kde.kdeconnect.UserInterface.ThemeUtil.applyTheme import org.kde.kdeconnect.extensions.setupBottomPadding +import org.kde.kdeconnect_tp.BuildConfig import org.kde.kdeconnect_tp.R +import java.io.InputStreamReader class SettingsFragment : PreferenceFragmentCompat() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -56,6 +66,7 @@ class SettingsFragment : PreferenceFragmentCompat() { devicesByIpPref(context), udpBroadcastPref(context), bluetoothSupportPref(context), + exportLogsPref(context), moreSettingsPref(context), ).forEach(screen::addPreference) @@ -217,6 +228,32 @@ class SettingsFragment : PreferenceFragmentCompat() { } } + private fun exportLogsPref(context: Context) = Preference(context).apply { + isPersistent = false + setTitle(R.string.settings_export_logs) + setSummary(R.string.settings_export_logs_text) + onPreferenceClickListener = Preference.OnPreferenceClickListener { + exportLogs.launch(CreateFileParams("text/plain", "kdeconnect-log.txt")) + true + } + } + + private val exportLogs: ActivityResultLauncher = registerForActivityResult( + CreateFileResultContract() + ) { uri: Uri? -> + val output = uri?.let { context?.contentResolver?.openOutputStream(uri) } ?: return@registerForActivityResult + CoroutineScope(Dispatchers.IO).launch { + val pid = android.os.Process.myPid() + val process = Runtime.getRuntime().exec(arrayOf("logcat", "-d", "--pid=$pid")) + val reader = InputStreamReader(process.inputStream) + output.use { + it.write("KDE Connect ${BuildConfig.VERSION_NAME}\n".toByteArray(Charsets.UTF_8)) + it.write("Android ${Build.VERSION.RELEASE} (${Build.MANUFACTURER} ${Build.MODEL})\n".toByteArray(Charsets.UTF_8)) + IOUtils.copy(reader, it, Charsets.UTF_8) + } + } + } + private fun moreSettingsPref(context: Context) = Preference(context).apply { isPersistent = false isSelectable = false