Skip to content

Commit fa03bfb

Browse files
committed
feature: ability of adding outer actions
1 parent 50d317c commit fa03bfb

10 files changed

Lines changed: 164 additions & 14 deletions

File tree

panel-core/src/main/kotlin/com/redmadrobot/debug/core/DebugPanelInstance.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ internal class DebugPanelInstance(
2121
private var pluginManager: PluginManager? = null
2222
private val eventLiveData: MutableLiveData<DebugEvent> = MutableLiveData()
2323
private val eventSharedFlow: MutableSharedFlow<DebugEvent> = MutableSharedFlow(
24-
extraBufferCapacity = 1,
24+
replay = 1,
2525
onBufferOverflow = BufferOverflow.DROP_OLDEST,
2626
)
2727
private val themeDataStore by lazy {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.redmadrobot.debug.plugin.aboutapp
2+
3+
import android.content.Context
4+
import java.util.UUID
5+
6+
/**
7+
* No-op declaration of [AboutAppAction] for release builds.
8+
*/
9+
@Suppress("UnusedPrivateProperty")
10+
public sealed interface AboutAppAction {
11+
public val id: String
12+
public val title: String
13+
14+
public class Direct(
15+
override val title: String,
16+
public val onClick: (Context) -> Unit,
17+
override val id: String = UUID.randomUUID().toString(),
18+
) : AboutAppAction
19+
20+
public class Event(
21+
override val title: String,
22+
override val id: String = UUID.randomUUID().toString(),
23+
) : AboutAppAction
24+
}

panel-no-op/src/main/kotlin/com/redmadrobot/debug/noop/plugin/aboutapp/AboutAppPlugin.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ package com.redmadrobot.debug.plugin.aboutapp
77
*/
88
@Suppress("UnusedPrivateProperty")
99
public class AboutAppPlugin(
10-
private val appInfoList: List<AboutAppInfo>
10+
private val appInfoList: List<AboutAppInfo>,
11+
private val actions: List<AboutAppAction> = emptyList(),
1112
)

plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/AboutAppPlugin.kt

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,41 @@ import androidx.compose.runtime.Composable
44
import com.redmadrobot.debug.core.internal.CommonContainer
55
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
66
import com.redmadrobot.debug.core.plugin.Plugin
7+
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppAction
78
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo
89
import com.redmadrobot.debug.plugin.aboutapp.ui.AboutAppScreen
910

1011
/**
1112
* Plugin that displays information about the application in the debug panel.
1213
*
13-
* Accepts an arbitrary list of "title -- value" pairs
14-
* which will be shown as a list.
14+
* Accepts an arbitrary list of "title -- value" pairs which will be shown as a list,
15+
* plus an optional list of custom action buttons rendered below the info section.
1516
*
1617
* @param appInfoList list of information entries to display.
17-
* Must contain at least one element.
18-
* @throws IllegalStateException if [appInfoList] is empty
18+
* @param actions list of custom action buttons rendered below the info section.
19+
* @throws IllegalStateException if both [appInfoList] and [actions] are empty
1920
*
2021
* @see AboutAppInfo
22+
* @see AboutAppAction
2123
*/
2224
public class AboutAppPlugin(
23-
private val appInfoList: List<AboutAppInfo>
25+
private val appInfoList: List<AboutAppInfo>,
26+
private val actions: List<AboutAppAction> = emptyList(),
2427
) : Plugin() {
2528
init {
26-
appInfoList.firstOrNull()
27-
?: error("AboutAppPlugin can't be initialized. At least one information block must be set.")
29+
check(appInfoList.isNotEmpty() || actions.isNotEmpty()) {
30+
"AboutAppPlugin can't be initialized. " +
31+
"At least one information block or one action must be set."
32+
}
2833
}
2934

3035
override fun getPluginContainer(commonContainer: CommonContainer): PluginDependencyContainer {
31-
return AboutAppPluginContainer(appInfoList = appInfoList, commonContainer = commonContainer)
36+
return AboutAppPluginContainer(
37+
appInfoList = appInfoList,
38+
actions = actions,
39+
commonContainer = commonContainer,
40+
pushEvent = ::pushEvent,
41+
)
3242
}
3343

3444
override fun getName(): String = NAME
Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
package com.redmadrobot.debug.plugin.aboutapp
22

3+
import com.redmadrobot.debug.core.DebugEvent
34
import com.redmadrobot.debug.core.internal.CommonContainer
45
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
6+
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppAction
57
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo
68
import com.redmadrobot.debug.plugin.aboutapp.ui.AboutAppViewModel
79
import com.redmadrobot.debug.plugin.aboutapp.utils.ClipboardProvider
810

911
internal class AboutAppPluginContainer(
1012
private val appInfoList: List<AboutAppInfo>,
11-
private val commonContainer: CommonContainer
13+
private val actions: List<AboutAppAction>,
14+
private val commonContainer: CommonContainer,
15+
private val pushEvent: (DebugEvent) -> Unit,
1216
) : PluginDependencyContainer {
1317
fun createAboutAppViewModel(): AboutAppViewModel {
1418
val clipboardProvider = ClipboardProvider(context = commonContainer.context)
1519

16-
return AboutAppViewModel(appInfoList = appInfoList, clipboardProvider = clipboardProvider)
20+
return AboutAppViewModel(
21+
appInfoList = appInfoList,
22+
actions = actions,
23+
context = commonContainer.context,
24+
clipboardProvider = clipboardProvider,
25+
pushEvent = pushEvent,
26+
)
1727
}
1828
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.redmadrobot.debug.plugin.aboutapp.model
2+
3+
import android.content.Context
4+
import com.redmadrobot.debug.core.DebugEvent
5+
import com.redmadrobot.debug.plugin.aboutapp.AboutAppPlugin
6+
import java.util.UUID
7+
8+
/**
9+
* Custom action button displayed in [AboutAppPlugin] under the application info section.
10+
*
11+
* Library consumers should use [Event]: clicks are published on the debug panel event bus as the provided [DebugEvent].
12+
*
13+
* [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).
14+
* It is not part of the recommended public usage — prefer [Event] in application code.
15+
*/
16+
public sealed interface AboutAppAction {
17+
/**
18+
* Stable identifier used as a `LazyColumn` key. Auto-generated by default;
19+
* override only if you need a deterministic key (e.g., for UI tests).
20+
*/
21+
public val id: String
22+
23+
/** Button label. */
24+
public val title: String
25+
26+
/**
27+
* Library-internal action whose [onClick] handler is invoked directly inside the plugin with the application context.
28+
* Reserved for built-in actions shipped with the library. Application code should use [Event] instead.
29+
*/
30+
public class Direct(
31+
override val title: String,
32+
public val onClick: (Context) -> Unit,
33+
override val id: String = UUID.randomUUID().toString(),
34+
) : AboutAppAction
35+
36+
/**
37+
* Action that, when clicked, publishes [debugEvent] on the debug panel event bus.
38+
*/
39+
public class Event(
40+
override val title: String,
41+
public val debugEvent: DebugEvent,
42+
override val id: String = UUID.randomUUID().toString(),
43+
) : AboutAppAction
44+
}

plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppScreen.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import com.redmadrobot.debug.plugin.aboutapp.R
3333
import com.redmadrobot.debug.uikit.theme.DebugPanelShapes
3434
import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
3535

36+
private const val ITEM_ACTIONS_HEADER_KEY = "actions_header"
37+
3638
@Composable
3739
internal fun AboutAppScreen(
3840
viewModel: AboutAppViewModel = provideViewModel {
@@ -75,6 +77,18 @@ internal fun AboutAppScreen(
7577
},
7678
)
7779
}
80+
81+
if (state.actions.isNotEmpty()) {
82+
item(key = ITEM_ACTIONS_HEADER_KEY) {
83+
AboutAppActionsHeader()
84+
}
85+
items(items = state.actions, key = { it.id }) { action ->
86+
AboutAppAction(
87+
title = action.title,
88+
onActionClick = { viewModel.onActionClicked(action) }
89+
)
90+
}
91+
}
7892
}
7993
}
8094
}
@@ -112,3 +126,33 @@ private fun InfoRow(
112126
)
113127
}
114128
}
129+
130+
@Composable
131+
private fun AboutAppActionsHeader(modifier: Modifier = Modifier) {
132+
Text(
133+
text = stringResource(id = R.string.about_app_actions_title),
134+
style = DebugPanelTheme.typography.labelLarge,
135+
color = DebugPanelTheme.colors.content.tertiary,
136+
modifier = modifier
137+
.fillMaxWidth()
138+
.padding(top = 20.dp, bottom = 8.dp)
139+
.padding(horizontal = 12.dp),
140+
)
141+
}
142+
143+
@Composable
144+
private fun AboutAppAction(
145+
title: String,
146+
onActionClick: () -> Unit,
147+
modifier: Modifier = Modifier
148+
) {
149+
Text(
150+
modifier = modifier
151+
.padding(horizontal = 12.dp, vertical = 8.dp)
152+
.clickable { onActionClick.invoke() },
153+
text = title,
154+
overflow = TextOverflow.Ellipsis,
155+
style = DebugPanelTheme.typography.bodyLarge,
156+
color = DebugPanelTheme.colors.content.accent,
157+
)
158+
}

plugins/plugin-about-app/src/main/kotlin/com/redmadrobot/debug/plugin/aboutapp/ui/AboutAppViewModel.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.redmadrobot.debug.plugin.aboutapp.ui
22

3+
import android.content.Context
34
import androidx.lifecycle.viewModelScope
45
import com.redmadrobot.debug.core.DebugEvent
56
import com.redmadrobot.debug.core.internal.PluginViewModel
7+
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppAction
68
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo
79
import com.redmadrobot.debug.plugin.aboutapp.utils.ClipboardProvider
810
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -15,9 +17,14 @@ import kotlinx.coroutines.launch
1517

1618
internal class AboutAppViewModel(
1719
appInfoList: List<AboutAppInfo>,
20+
actions: List<AboutAppAction>,
21+
private val context: Context,
1822
private val clipboardProvider: ClipboardProvider,
23+
private val pushEvent: (DebugEvent) -> Unit,
1924
) : PluginViewModel() {
20-
private val _state = MutableStateFlow(value = AboutAppViewState(appInfoList = appInfoList))
25+
private val _state = MutableStateFlow(
26+
value = AboutAppViewState(appInfoList = appInfoList, actions = actions),
27+
)
2128
val state: StateFlow<AboutAppViewState> = _state.asStateFlow()
2229

2330
private val _events = MutableSharedFlow<AppInfoSelectionEvent>()
@@ -29,6 +36,13 @@ internal class AboutAppViewModel(
2936
_events.emit(AppInfoSelectionEvent(message = message, item = item))
3037
}
3138
}
39+
40+
fun onActionClicked(action: AboutAppAction) {
41+
when (action) {
42+
is AboutAppAction.Direct -> action.onClick(context)
43+
is AboutAppAction.Event -> pushEvent(action.debugEvent)
44+
}
45+
}
3246
}
3347

3448
internal data class AppInfoSelectionEvent(
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.redmadrobot.debug.plugin.aboutapp.ui
22

33
import androidx.compose.runtime.Immutable
4+
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppAction
45
import com.redmadrobot.debug.plugin.aboutapp.model.AboutAppInfo
56

67
@Immutable
78
internal data class AboutAppViewState(
8-
val appInfoList: List<AboutAppInfo>
9+
val appInfoList: List<AboutAppInfo>,
10+
val actions: List<AboutAppAction>,
911
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33
<string name="about_app_copied">«%s» is copied</string>
4+
<string name="about_app_actions_title">Debug actions</string>
45
</resources>

0 commit comments

Comments
 (0)