Add option to dump crash logs

To tachi-chan: Thank you for your service o7
pull/7308/head
Jays2Kings 4 years ago
parent c26c38ca39
commit 093018fd81

@ -244,6 +244,8 @@ class NotificationReceiver : BroadcastReceiver() {
// Called to launch send intent. // Called to launch send intent.
private const val ACTION_SHARE_BACKUP = "$ID.$NAME.SEND_BACKUP" private const val ACTION_SHARE_BACKUP = "$ID.$NAME.SEND_BACKUP"
private const val ACTION_SHARE_CRASH_LOG = "$ID.$NAME.SEND_CRASH_LOG"
// Called to cancel library update. // Called to cancel library update.
private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE" private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE"
@ -563,6 +565,23 @@ class NotificationReceiver : BroadcastReceiver() {
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
} }
/**
* Returns [PendingIntent] that starts a share activity for a crash log dump file.
*
* @param context context of application
* @param uri uri of file
* @param notificationId id of notification
* @return [PendingIntent]
*/
internal fun shareCrashLogPendingBroadcast(context: Context, uri: Uri, notificationId: Int): PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_SHARE_CRASH_LOG
putExtra(EXTRA_URI, uri)
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
}
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
/** /**
* Returns [PendingIntent] that cancels a backup restore job. * Returns [PendingIntent] that cancels a backup restore job.
* *

@ -55,6 +55,12 @@ object Notifications {
const val ID_BACKUP_PROGRESS = -502 const val ID_BACKUP_PROGRESS = -502
const val ID_BACKUP_COMPLETE = -503 const val ID_BACKUP_COMPLETE = -503
/**
* Notification channel used for crash log file sharing.
*/
const val CHANNEL_CRASH_LOGS = "crash_logs_channel"
const val ID_CRASH_LOGS = -601
/** /**
* Creates the notification channels introduced in Android Oreo. * Creates the notification channels introduced in Android Oreo.
* *
@ -113,7 +119,12 @@ object Notifications {
group = GROUP_BACKUP_RESTORE group = GROUP_BACKUP_RESTORE
setShowBadge(false) setShowBadge(false)
setSound(null, null) setSound(null, null)
} },
NotificationChannel(
CHANNEL_CRASH_LOGS,
context.getString(R.string.crash_logs),
NotificationManager.IMPORTANCE_HIGH
)
) )
context.notificationManager.createNotificationChannels(channels) context.notificationManager.createNotificationChannels(channels)
} }

@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.CoroutineStart
@ -59,6 +60,37 @@ class SettingsAdvancedController : SettingsController() {
summaryRes = R.string.helps_fix_bugs summaryRes = R.string.helps_fix_bugs
defaultValue = true defaultValue = true
} }
preference {
key = "dump_crash_logs"
titleRes = R.string.dump_crash_logs
summaryRes = R.string.saves_error_logs
onClick {
CrashLogUtil(context).dumpLogs()
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = context.getSystemService(Context.POWER_SERVICE) as? PowerManager?
if (pm != null) preference {
key = "disable_batt_opt"
titleRes = R.string.disable_battery_optimization
summaryRes = R.string.disable_if_issues_with_updating
onClick {
val packageName: String = context.packageName
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
val intent = Intent().apply {
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
data = "package:$packageName".toUri()
}
startActivity(intent)
} else {
context.toast(R.string.battery_optimization_disabled)
}
}
}
}
preferenceCategory { preferenceCategory {
titleRes = R.string.data_management titleRes = R.string.data_management
preference { preference {
@ -158,30 +190,6 @@ class SettingsAdvancedController : SettingsController() {
onClick { LibraryUpdateService.start(context, target = Target.TRACKING) } onClick { LibraryUpdateService.start(context, target = Target.TRACKING) }
} }
} }
preferenceCategory {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val pm = context.getSystemService(Context.POWER_SERVICE) as? PowerManager?
if (pm != null) preference {
key = "disable_batt_opt"
titleRes = R.string.disable_battery_optimization
summaryRes = R.string.disable_if_issues_with_updating
onClick {
val packageName: String = context.packageName
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
val intent = Intent().apply {
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
data = "package:$packageName".toUri()
}
startActivity(intent)
} else {
context.toast(R.string.battery_optimization_disabled)
}
}
}
}
}
} }
class CleanupDownloadsDialogController() : DialogController() { class CleanupDownloadsDialogController() : DialogController() {

@ -0,0 +1,56 @@
package eu.kanade.tachiyomi.util
import android.content.Context
import android.net.Uri
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notificationManager
import eu.kanade.tachiyomi.util.system.toast
import java.io.IOException
class CrashLogUtil(private val context: Context) {
private val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_CRASH_LOGS) {
setSmallIcon(R.drawable.ic_tachi)
}
fun dumpLogs() {
try {
val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt")
Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}")
showNotification(file.getUriCompat(context))
} catch (e: IOException) {
context.toast("Failed to get logs")
}
}
private fun showNotification(uri: Uri) {
context.notificationManager.cancel(Notifications.ID_CRASH_LOGS)
with(notificationBuilder) {
setContentTitle(context.getString(R.string.crash_log_saved))
// Clear old actions if they exist
clearActions()
addAction(
R.drawable.ic_bug_report_24dp,
context.getString(R.string.open_log),
NotificationReceiver.openErrorLogPendingActivity(context, uri)
)
addAction(
R.drawable.ic_share_24dp,
context.getString(R.string.share),
NotificationReceiver.shareCrashLogPendingBroadcast(context, uri, Notifications.ID_CRASH_LOGS)
)
context.notificationManager.notify(Notifications.ID_CRASH_LOGS, build())
}
}
}

@ -601,6 +601,10 @@
<!-- Advanced section --> <!-- Advanced section -->
<string name="clear_chapter_cache">Clear chapter cache</string> <string name="clear_chapter_cache">Clear chapter cache</string>
<string name="data_management">Data Management</string> <string name="data_management">Data Management</string>
<string name="crash_logs">Crash logs</string>
<string name="dump_crash_logs">Dump crash logs</string>
<string name="saves_error_logs">Saves error logs to a file for sharing with the developers</string>
<string name="crash_log_saved">Crash logs saved</string>
<string name="network">Network</string> <string name="network">Network</string>
<string name="dns_over_https" translatable="false">DNS over HTTPS (Cloudflare)</string> <string name="dns_over_https" translatable="false">DNS over HTTPS (Cloudflare)</string>
<string name="requires_app_restart">Requires app restart to take effect</string> <string name="requires_app_restart">Requires app restart to take effect</string>

Loading…
Cancel
Save