From 3eee9bde244e6cb0f0cd0a387c02c1509ccfa69b Mon Sep 17 00:00:00 2001 From: "a.emogurov" Date: Tue, 26 May 2026 09:44:26 +0400 Subject: [PATCH] feature: ability of adding outer actions --- .../debug/core/DebugPanelInstance.kt | 2 +- .../noop/plugin/aboutapp/AboutAppAction.kt | 24 ++++++++++ .../noop/plugin/aboutapp/AboutAppPlugin.kt | 3 +- .../debug/plugin/aboutapp/AboutAppPlugin.kt | 26 +++++++---- .../aboutapp/AboutAppPluginContainer.kt | 14 +++++- .../plugin/aboutapp/model/AboutAppAction.kt | 44 +++++++++++++++++++ .../plugin/aboutapp/ui/AboutAppScreen.kt | 44 +++++++++++++++++++ .../plugin/aboutapp/ui/AboutAppViewModel.kt | 16 ++++++- .../plugin/aboutapp/ui/AboutAppViewState.kt | 4 +- .../src/main/res/values/strings.xml | 1 + 10 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppAction.kt create mode 100644 plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/model/AboutAppAction.kt diff --git a/panel-core/src/main/kotlin/com/redmadrobot/debug/core/DebugPanelInstance.kt b/panel-core/src/main/kotlin/com/redmadrobot/debug/core/DebugPanelInstance.kt index afa5a59d..e89c5658 100644 --- a/panel-core/src/main/kotlin/com/redmadrobot/debug/core/DebugPanelInstance.kt +++ b/panel-core/src/main/kotlin/com/redmadrobot/debug/core/DebugPanelInstance.kt @@ -21,7 +21,7 @@ internal class DebugPanelInstance( private var pluginManager: PluginManager? = null private val eventLiveData: MutableLiveData = MutableLiveData() private val eventSharedFlow: MutableSharedFlow = MutableSharedFlow( - extraBufferCapacity = 1, + replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST, ) private val themeDataStore by lazy { diff --git a/panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppAction.kt b/panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppAction.kt new file mode 100644 index 00000000..ed62673f --- /dev/null +++ b/panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppAction.kt @@ -0,0 +1,24 @@ +package com.redmadrobot.debug.plugin.aboutapp + +import android.content.Context +import java.util.UUID + +/** + * No-op declaration of [AboutAppAction] for release builds. + */ +@Suppress("UnusedPrivateProperty") +public sealed interface AboutAppAction { + public val id: String + public val title: String + + public class Direct( + override val title: String, + public val onClick: (Context) -> Unit, + override val id: String = UUID.randomUUID().toString(), + ) : AboutAppAction + + public class Event( + override val title: String, + override val id: String = UUID.randomUUID().toString(), + ) : AboutAppAction +} diff --git a/panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppPlugin.kt b/panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppPlugin.kt index 2f7671bc..025773c2 100644 --- a/panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppPlugin.kt +++ b/panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppPlugin.kt @@ -7,5 +7,6 @@ package com.redmadrobot.debug.plugin.aboutapp */ @Suppress("UnusedPrivateProperty") public class AboutAppPlugin( - private val appInfoList: List + private val appInfoList: List, + private val actions: List = emptyList(), ) diff --git a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPlugin.kt b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPlugin.kt index 4e1aa4a9..4a363b0b 100644 --- a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPlugin.kt +++ b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPlugin.kt @@ -4,31 +4,41 @@ import androidx.compose.runtime.Composable import com.redmadrobot.debug.core.internal.CommonContainer import com.redmadrobot.debug.core.internal.PluginDependencyContainer import com.redmadrobot.debug.core.plugin.Plugin +import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppAction import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo import com.redmadrobot.debug.plugin.aboutapp.ui.AboutAppScreen /** * Plugin that displays information about the application in the debug panel. * - * Accepts an arbitrary list of "title -- value" pairs - * which will be shown as a list. + * Accepts an arbitrary list of "title -- value" pairs which will be shown as a list, + * plus an optional list of custom action buttons rendered below the info section. * * @param appInfoList list of information entries to display. - * Must contain at least one element. - * @throws IllegalStateException if [appInfoList] is empty + * @param actions list of custom action buttons rendered below the info section. + * @throws IllegalStateException if both [appInfoList] and [actions] are empty * * @see AboutAppInfo + * @see AboutAppAction */ public class AboutAppPlugin( - private val appInfoList: List + private val appInfoList: List, + private val actions: List = emptyList(), ) : Plugin() { init { - appInfoList.firstOrNull() - ?: error("AboutAppPlugin can't be initialized. At least one information block must be set.") + check(appInfoList.isNotEmpty() || actions.isNotEmpty()) { + "AboutAppPlugin can't be initialized. " + + "At least one information block or one action must be set." + } } override fun getPluginContainer(commonContainer: CommonContainer): PluginDependencyContainer { - return AboutAppPluginContainer(appInfoList = appInfoList, commonContainer = commonContainer) + return AboutAppPluginContainer( + appInfoList = appInfoList, + actions = actions, + commonContainer = commonContainer, + pushEvent = ::pushEvent, + ) } override fun getName(): String = NAME diff --git a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPluginContainer.kt b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPluginContainer.kt index 0fc72148..5489b600 100644 --- a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPluginContainer.kt +++ b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPluginContainer.kt @@ -1,18 +1,28 @@ package com.redmadrobot.debug.plugin.aboutapp +import com.redmadrobot.debug.core.DebugEvent import com.redmadrobot.debug.core.internal.CommonContainer import com.redmadrobot.debug.core.internal.PluginDependencyContainer +import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppAction import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo import com.redmadrobot.debug.plugin.aboutapp.ui.AboutAppViewModel import com.redmadrobot.debug.plugin.aboutapp.utils.ClipboardProvider internal class AboutAppPluginContainer( private val appInfoList: List, - private val commonContainer: CommonContainer + private val actions: List, + private val commonContainer: CommonContainer, + private val pushEvent: (DebugEvent) -> Unit, ) : PluginDependencyContainer { fun createAboutAppViewModel(): AboutAppViewModel { val clipboardProvider = ClipboardProvider(context = commonContainer.context) - return AboutAppViewModel(appInfoList = appInfoList, clipboardProvider = clipboardProvider) + return AboutAppViewModel( + appInfoList = appInfoList, + actions = actions, + context = commonContainer.context, + clipboardProvider = clipboardProvider, + pushEvent = pushEvent, + ) } } diff --git a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/model/AboutAppAction.kt b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/model/AboutAppAction.kt new file mode 100644 index 00000000..f9f48b13 --- /dev/null +++ b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/model/AboutAppAction.kt @@ -0,0 +1,44 @@ +package com.redmadrobot.debug.plugin.aboutapp.model + +import android.content.Context +import com.redmadrobot.debug.core.DebugEvent +import com.redmadrobot.debug.plugin.aboutapp.AboutAppPlugin +import java.util.UUID + +/** + * Custom action button displayed in [AboutAppPlugin] under the application info section. + * + * Library consumers should use [Event]: clicks are published on the debug panel event bus as the provided [DebugEvent]. + * + * [Direct] is intended for the library's own built-in actions that can be handled with application context only (e.g., copy a token, clear cache). + * It is not part of the recommended public usage — prefer [Event] in application code. + */ +public sealed interface AboutAppAction { + /** + * Stable identifier used as a `LazyColumn` key. Auto-generated by default; + * override only if you need a deterministic key (e.g., for UI tests). + */ + public val id: String + + /** Button label. */ + public val title: String + + /** + * Library-internal action whose [onClick] handler is invoked directly inside the plugin with the application context. + * Reserved for built-in actions shipped with the library. Application code should use [Event] instead. + */ + public class Direct( + override val title: String, + public val onClick: (Context) -> Unit, + override val id: String = UUID.randomUUID().toString(), + ) : AboutAppAction + + /** + * Action that, when clicked, publishes [debugEvent] on the debug panel event bus. + */ + public class Event( + override val title: String, + public val debugEvent: DebugEvent, + override val id: String = UUID.randomUUID().toString(), + ) : AboutAppAction +} diff --git a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppScreen.kt b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppScreen.kt index 4bfea8d4..575c33c1 100644 --- a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppScreen.kt +++ b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppScreen.kt @@ -33,6 +33,8 @@ import com.redmadrobot.debug.plugin.aboutapp.R import com.redmadrobot.debug.uikit.theme.DebugPanelShapes import com.redmadrobot.debug.uikit.theme.DebugPanelTheme +private const val ITEM_ACTIONS_HEADER_KEY = "actions_header" + @Composable internal fun AboutAppScreen( viewModel: AboutAppViewModel = provideViewModel { @@ -75,6 +77,18 @@ internal fun AboutAppScreen( }, ) } + + if (state.actions.isNotEmpty()) { + item(key = ITEM_ACTIONS_HEADER_KEY) { + AboutAppActionsHeader() + } + items(items = state.actions, key = { it.id }) { action -> + AboutAppAction( + title = action.title, + onActionClick = { viewModel.onActionClicked(action) } + ) + } + } } } } @@ -112,3 +126,33 @@ private fun InfoRow( ) } } + +@Composable +private fun AboutAppActionsHeader(modifier: Modifier = Modifier) { + Text( + text = stringResource(id = R.string.about_app_actions_title), + style = DebugPanelTheme.typography.labelLarge, + color = DebugPanelTheme.colors.content.tertiary, + modifier = modifier + .fillMaxWidth() + .padding(top = 20.dp, bottom = 8.dp) + .padding(horizontal = 12.dp), + ) +} + +@Composable +private fun AboutAppAction( + title: String, + onActionClick: () -> Unit, + modifier: Modifier = Modifier +) { + Text( + modifier = modifier + .padding(horizontal = 12.dp, vertical = 8.dp) + .clickable { onActionClick.invoke() }, + text = title, + overflow = TextOverflow.Ellipsis, + style = DebugPanelTheme.typography.bodyLarge, + color = DebugPanelTheme.colors.content.accent, + ) +} diff --git a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewModel.kt b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewModel.kt index 91809ef8..f4c20ac7 100644 --- a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewModel.kt +++ b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewModel.kt @@ -1,8 +1,10 @@ package com.redmadrobot.debug.plugin.aboutapp.ui +import android.content.Context import androidx.lifecycle.viewModelScope import com.redmadrobot.debug.core.DebugEvent import com.redmadrobot.debug.core.internal.PluginViewModel +import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppAction import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo import com.redmadrobot.debug.plugin.aboutapp.utils.ClipboardProvider import kotlinx.coroutines.flow.MutableSharedFlow @@ -15,9 +17,14 @@ import kotlinx.coroutines.launch internal class AboutAppViewModel( appInfoList: List, + actions: List, + private val context: Context, private val clipboardProvider: ClipboardProvider, + private val pushEvent: (DebugEvent) -> Unit, ) : PluginViewModel() { - private val _state = MutableStateFlow(value = AboutAppViewState(appInfoList = appInfoList)) + private val _state = MutableStateFlow( + value = AboutAppViewState(appInfoList = appInfoList, actions = actions), + ) val state: StateFlow = _state.asStateFlow() private val _events = MutableSharedFlow() @@ -29,6 +36,13 @@ internal class AboutAppViewModel( _events.emit(AppInfoSelectionEvent(message = message, item = item)) } } + + fun onActionClicked(action: AboutAppAction) { + when (action) { + is AboutAppAction.Direct -> action.onClick(context) + is AboutAppAction.Event -> pushEvent(action.debugEvent) + } + } } internal data class AppInfoSelectionEvent( diff --git a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewState.kt b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewState.kt index 163fbce3..aff478bb 100644 --- a/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewState.kt +++ b/plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewState.kt @@ -1,9 +1,11 @@ package com.redmadrobot.debug.plugin.aboutapp.ui import androidx.compose.runtime.Immutable +import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppAction import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo @Immutable internal data class AboutAppViewState( - val appInfoList: List + val appInfoList: List, + val actions: List, ) diff --git a/plugins/plugin-about-app/src/main/res/values/strings.xml b/plugins/plugin-about-app/src/main/res/values/strings.xml index 83199879..ae169aac 100644 --- a/plugins/plugin-about-app/src/main/res/values/strings.xml +++ b/plugins/plugin-about-app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ «%s» is copied + Debug actions