From ac96c49b605b09afd6b3e7c048b6a92bac76373f Mon Sep 17 00:00:00 2001 From: Koitharu Date: Wed, 2 Oct 2024 15:13:37 +0300 Subject: [PATCH] Strict mode notificaiton for debug build --- .../kotlin/org/koitharu/kotatsu/KotatsuApp.kt | 36 +++++++++-- .../koitharu/kotatsu/StrictModeNotifier.kt | 64 +++++++++++++++++++ app/src/debug/res/values/strings.xml | 3 +- 3 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 app/src/debug/kotlin/org/koitharu/kotatsu/StrictModeNotifier.kt diff --git a/app/src/debug/kotlin/org/koitharu/kotatsu/KotatsuApp.kt b/app/src/debug/kotlin/org/koitharu/kotatsu/KotatsuApp.kt index eec41000c..187a7b3d8 100644 --- a/app/src/debug/kotlin/org/koitharu/kotatsu/KotatsuApp.kt +++ b/app/src/debug/kotlin/org/koitharu/kotatsu/KotatsuApp.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu import android.content.Context +import android.os.Build import android.os.StrictMode import androidx.fragment.app.strictmode.FragmentStrictMode import org.koitharu.kotatsu.core.BaseApp @@ -18,22 +19,42 @@ class KotatsuApp : BaseApp() { } private fun enableStrictMode() { + val notifier = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + StrictModeNotifier(this) + } else { + null + } StrictMode.setThreadPolicy( StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() - .build(), + .run { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && notifier != null) { + penaltyListener(notifier.executor, notifier) + } else { + this + } + }.build(), ) StrictMode.setVmPolicy( StrictMode.VmPolicy.Builder() - .detectAll() + .detectActivityLeaks() + .detectLeakedSqlLiteObjects() + .detectLeakedClosableObjects() + .detectLeakedRegistrationObjects() .setClassInstanceLimit(LocalMangaRepository::class.java, 1) .setClassInstanceLimit(PagesCache::class.java, 1) .setClassInstanceLimit(MangaLoaderContext::class.java, 1) .setClassInstanceLimit(PageLoader::class.java, 1) .setClassInstanceLimit(ReaderViewModel::class.java, 1) .penaltyLog() - .build(), + .run { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && notifier != null) { + penaltyListener(notifier.executor, notifier) + } else { + this + } + }.build(), ) FragmentStrictMode.defaultPolicy = FragmentStrictMode.Policy.Builder() .penaltyDeath() @@ -42,6 +63,13 @@ class KotatsuApp : BaseApp() { .detectRetainInstanceUsage() .detectSetUserVisibleHint() .detectFragmentTagUsage() - .build() + .penaltyLog() + .run { + if (notifier != null) { + penaltyListener(notifier) + } else { + this + } + }.build() } } diff --git a/app/src/debug/kotlin/org/koitharu/kotatsu/StrictModeNotifier.kt b/app/src/debug/kotlin/org/koitharu/kotatsu/StrictModeNotifier.kt new file mode 100644 index 000000000..8847cb601 --- /dev/null +++ b/app/src/debug/kotlin/org/koitharu/kotatsu/StrictModeNotifier.kt @@ -0,0 +1,64 @@ +package org.koitharu.kotatsu + +import android.app.Notification +import android.app.Notification.BigTextStyle +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.os.Build +import android.os.StrictMode +import android.os.strictmode.Violation +import androidx.annotation.RequiresApi +import androidx.core.content.getSystemService +import androidx.fragment.app.strictmode.FragmentStrictMode +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asExecutor +import org.koitharu.kotatsu.core.ErrorReporterReceiver +import kotlin.math.absoluteValue +import androidx.fragment.app.strictmode.Violation as FragmentViolation + +@RequiresApi(Build.VERSION_CODES.P) +class StrictModeNotifier( + private val context: Context, +) : StrictMode.OnVmViolationListener, StrictMode.OnThreadViolationListener, FragmentStrictMode.OnViolationListener { + + val executor = Dispatchers.Default.asExecutor() + + private val notificationManager by lazy { + val nm = checkNotNull(context.getSystemService()) + val channel = NotificationChannel( + CHANNEL_ID, + context.getString(R.string.strict_mode), + NotificationManager.IMPORTANCE_LOW, + ) + nm.createNotificationChannel(channel) + nm + } + + override fun onVmViolation(v: Violation) = showNotification(v) + + override fun onThreadViolation(v: Violation) = showNotification(v) + + override fun onViolation(violation: FragmentViolation) = showNotification(violation) + + private fun showNotification(violation: Throwable) = Notification.Builder(context, CHANNEL_ID) + .setSmallIcon(android.R.drawable.stat_notify_error) + .setContentTitle(context.getString(R.string.strict_mode)) + .setContentText(violation.message) + .setStyle( + BigTextStyle() + .setBigContentTitle(context.getString(R.string.strict_mode)) + .setSummaryText(violation.message) + .bigText(violation.stackTraceToString()), + ).setShowWhen(true) + .setContentIntent(ErrorReporterReceiver.getPendingIntent(context, violation)) + .setAutoCancel(true) + .setGroup(CHANNEL_ID) + .build() + .let { notificationManager.notify(CHANNEL_ID, violation.hashCode().absoluteValue, it) } + + private companion object { + + const val CHANNEL_ID = "strict_mode" + } +} diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml index 5a9c5bfd8..dfda02b0e 100644 --- a/app/src/debug/res/values/strings.xml +++ b/app/src/debug/res/values/strings.xml @@ -1,3 +1,4 @@ Kotatsu Dev - \ No newline at end of file + Strict mode +