From 4fc372035a3e70f38d0905dbb671dc4054d735a9 Mon Sep 17 00:00:00 2001 From: Ida Delphine Date: Thu, 8 Oct 2026 11:04:36 -0700 Subject: [PATCH] add platform and compose debug actions --- .../newpipe/settings/ComposeDebugActions.kt | 53 +++++++++++++++++++ .../app/platform/AndroidDebugActions.kt | 44 +++++++++++++++ .../net/newpipe/app/platform/DebugActions.kt | 45 ++++++++++++++++ .../newpipe/app/platform/IOSDebugActions.kt | 14 +++++ .../newpipe/app/platform/JVMDebugActions.kt | 15 ++++++ 5 files changed, 171 insertions(+) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/ComposeDebugActions.kt create mode 100644 shared/src/androidMain/kotlin/net/newpipe/app/platform/AndroidDebugActions.kt create mode 100644 shared/src/iosMain/kotlin/net/newpipe/app/platform/IOSDebugActions.kt create mode 100644 shared/src/jvmMain/kotlin/net/newpipe/app/platform/JVMDebugActions.kt diff --git a/app/src/main/java/org/schabi/newpipe/settings/ComposeDebugActions.kt b/app/src/main/java/org/schabi/newpipe/settings/ComposeDebugActions.kt new file mode 100644 index 000000000..7c415e4ef --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/ComposeDebugActions.kt @@ -0,0 +1,53 @@ +package org.schabi.newpipe.settings + +import android.content.Context +import android.content.Intent +import net.newpipe.app.platform.DebugActions +import org.schabi.newpipe.error.ErrorInfo +import org.schabi.newpipe.error.ErrorUtil +import org.schabi.newpipe.error.UserAction +import org.schabi.newpipe.local.feed.notifications.NotificationWorker + +/** + * Host-side implementation of the debug actions used by the Compose debug settings screen. + */ +@Suppress("unused") +class ComposeDebugActions(private val context: Context) : DebugActions { + + private val leakCanary = loadLeakCanary() + + override val isLeakCanaryAvailable: Boolean + get() = leakCanary != null + + override fun showMemoryLeaks() { + // The injected context is the application context, so the activity needs its own task. + leakCanary?.newLeakDisplayActivityIntent + ?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ?.let(context::startActivity) + } + + override fun checkNewStreams() = NotificationWorker.runNow(context) + + override fun createErrorNotification() = ErrorUtil.createNotification(context, dummyErrorInfo()) + + override fun reportDummyError() = ErrorUtil.openActivity(context, dummyErrorInfo()) + + private fun dummyErrorInfo() = ErrorInfo(RuntimeException(DUMMY), UserAction.UI_ERROR, DUMMY) + + /** + * LeakCanary only ships in debug builds, so it is reached through the build-variant dependent + * bridge the legacy debug settings fragment already defines. + * + * TODO: move [DebugSettingsFragment.DebugSettingsBVDLeakCanaryAPI] out of the fragment when + * the legacy debug settings fragment is deleted. + */ + private fun loadLeakCanary(): DebugSettingsFragment.DebugSettingsBVDLeakCanaryAPI? = runCatching { + Class.forName(DebugSettingsFragment.DebugSettingsBVDLeakCanaryAPI.IMPL_CLASS) + .getDeclaredConstructor() + .newInstance() as DebugSettingsFragment.DebugSettingsBVDLeakCanaryAPI + }.getOrNull() + + private companion object { + const val DUMMY = "Dummy" + } +} diff --git a/shared/src/androidMain/kotlin/net/newpipe/app/platform/AndroidDebugActions.kt b/shared/src/androidMain/kotlin/net/newpipe/app/platform/AndroidDebugActions.kt new file mode 100644 index 000000000..9bc3d24c1 --- /dev/null +++ b/shared/src/androidMain/kotlin/net/newpipe/app/platform/AndroidDebugActions.kt @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.platform + +import android.content.Context +import co.touchlab.kermit.Logger +import org.koin.core.annotation.Singleton + +/** + * Forwards every debug action to a bridge supplied by the host app. + * + */ +@Singleton(binds = [DebugActions::class]) +class AndroidDebugActions(context: Context) : DebugActions { + + private val host: DebugActions = loadHost(context) + + override val isLeakCanaryAvailable: Boolean + get() = host.isLeakCanaryAvailable + + override fun showMemoryLeaks() = host.showMemoryLeaks() + + override fun checkNewStreams() = host.checkNewStreams() + + override fun createErrorNotification() = host.createErrorNotification() + + override fun reportDummyError() = host.reportDummyError() + + private fun loadHost(context: Context): DebugActions = runCatching { + Class.forName(HOST_CLASS) + .getDeclaredConstructor(Context::class.java) + .newInstance(context) as DebugActions + }.getOrElse { error -> + Logger.w(messageString = "No host debug actions found, falling back to no-op", throwable = error) + NoOpDebugActions + } + + private companion object { + const val HOST_CLASS = "org.schabi.newpipe.settings.ComposeDebugActions" + } +} diff --git a/shared/src/commonMain/kotlin/net/newpipe/app/platform/DebugActions.kt b/shared/src/commonMain/kotlin/net/newpipe/app/platform/DebugActions.kt index 38452c7f0..8f4e01739 100644 --- a/shared/src/commonMain/kotlin/net/newpipe/app/platform/DebugActions.kt +++ b/shared/src/commonMain/kotlin/net/newpipe/app/platform/DebugActions.kt @@ -1,2 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + package net.newpipe.app.platform +import co.touchlab.kermit.Logger + +/** + * Platform-specific actions triggered from the Debug settings screen. + * + */ +interface DebugActions { + /** Whether the running build ships LeakCanary; its two rows are disabled when it does not. */ + val isLeakCanaryAvailable: Boolean + + /** Opens the LeakCanary leak list. */ + fun showMemoryLeaks() + + /** Runs the new-stream notification check now, instead of waiting for its schedule. */ + fun checkNewStreams() + + /** Posts a dummy error notification, to verify error reporting end to end. */ + fun createErrorNotification() + + /** Opens the error report screen for a dummy error. */ + fun reportDummyError() +} + +/** + * Fallback for platforms with no debug tooling to drive, and for Android builds that ship without + * the host-side bridge. + */ +internal object NoOpDebugActions : DebugActions { + override val isLeakCanaryAvailable = false + + override fun showMemoryLeaks() = logUnsupported("showMemoryLeaks") + + override fun checkNewStreams() = logUnsupported("checkNewStreams") + + override fun createErrorNotification() = logUnsupported("createErrorNotification") + + override fun reportDummyError() = logUnsupported("reportDummyError") + + private fun logUnsupported(action: String) = Logger.w(messageString = "Debug action '$action' is unsupported on this platform") +} diff --git a/shared/src/iosMain/kotlin/net/newpipe/app/platform/IOSDebugActions.kt b/shared/src/iosMain/kotlin/net/newpipe/app/platform/IOSDebugActions.kt new file mode 100644 index 000000000..4912cd7a7 --- /dev/null +++ b/shared/src/iosMain/kotlin/net/newpipe/app/platform/IOSDebugActions.kt @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.platform + +import org.koin.core.annotation.Singleton + +/** + * iOS has no debug tooling to drive yet, and the Debug category is hidden there anyway + */ +@Singleton(binds = [DebugActions::class]) +class IOSDebugActions : DebugActions by NoOpDebugActions diff --git a/shared/src/jvmMain/kotlin/net/newpipe/app/platform/JVMDebugActions.kt b/shared/src/jvmMain/kotlin/net/newpipe/app/platform/JVMDebugActions.kt new file mode 100644 index 000000000..4dc7efe55 --- /dev/null +++ b/shared/src/jvmMain/kotlin/net/newpipe/app/platform/JVMDebugActions.kt @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2026 NewPipe e.V. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package net.newpipe.app.platform + +import org.koin.core.annotation.Singleton + +/** + * Desktop has no debug tooling to drive yet + */ + +@Singleton(binds = [DebugActions::class]) +class JVMDebugActions : DebugActions by NoOpDebugActions