动作可以是功能性的, 例如跳转到某个界面、 保存编辑器更改到文件中等. 也可以是带界面的, 例如新建文件(需要通过对话框输入文件名)、删除文件(需要用户通过对话框确认删除)等.
自定义动作
如果想要自定义动作的话需要通过插件实现, 如果您还不会开发插件可以先查看 插件开发
编写动作分组继承 AbstractActionGroup, 并实现其中的 getId
和 getName
方法
package com.m8test.plugins.common.action
import com.hjq.toast.Toaster
import com.m8test.action.impl.AbstractActionGroup
import com.m8test.action.impl.DefaultFunctionalAction
import com.m8test.plugin.api.PluggableApkPlugin
/**
* Description TODO
*
* @date 2025/01/02 14:35:21
* @author M8Test,
[email protected], https://m8test.com
*/
class TestActionGroup(plugin: PluggableApkPlugin) : AbstractActionGroup() {
init {
registerAction(ComposableActions.getWrapContentDialogAction(plugin))
registerAction(ComposableActions.getWrapContentBottomSheetAction(plugin))
registerAction(ComposableActions.getWrapContentFloatyAction(plugin))
registerAction(ComposableActions.getFullscreenDialogAction(plugin))
registerAction(ComposableActions.getFullscreenBottomSheetAction(plugin))
registerAction(ComposableActions.getFullscreenFloatyAction(plugin))
registerAction(ViewActions.getWrapContentDialogAction(plugin))
registerAction(ViewActions.getWrapContentBottomSheetAction(plugin))
registerAction(ViewActions.getWrapContentFloatyAction(plugin))
registerAction(ViewActions.getFullscreenDialogAction(plugin))
registerAction(ViewActions.getFullscreenBottomSheetAction(plugin))
registerAction(ViewActions.getFullscreenFloatyAction(plugin))
registerAction(
DefaultFunctionalAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "FunctionalAction",
id = "FunctionalAction",
onPerform = { Toaster.show("FunctionalAction") })
)
}
override fun getId(): String {
return "TestActionGroup"
}
override fun getName(): String {
return "TestActionGroup"
}
}
在 AbstractComposablePluggableApkPlugin 中的 onInstall
方法注册动作分组, 以及在 onUninstall
方法取消注册动作分组, 并在其中注册需要的动作, 并通过 ActionGroupLazyColumn
和 ActionGroupLazyRow
显示动作分组.
package com.m8test.plugins.common.action
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.currentRecomposeScope
import androidx.compose.ui.Modifier
import com.google.accompanist.imageloading.rememberDrawablePainter
import com.m8test.action.impl.ability.DefaultActionAbilityFactory
import com.m8test.compose.widget.BackTopBarScaffold
import com.m8test.plugin.api.ApkPluginProvider
import com.m8test.plugin.impl.AbstractComposablePluggableApkPlugin
import com.m8test.util.ActionUtils
import com.m8test.util.ActionUtils.ActionGroupLazyColumn
import com.m8test.util.ActionUtils.ActionGroupLazyRow
/**
* Description TODO
*
* @date 2025/01/01 12:14:31
* @author M8Test,
[email protected], https://m8test.com
*/
class ActionPlugin(apkPluginProvider: ApkPluginProvider) :
AbstractComposablePluggableApkPlugin(apkPluginProvider) {
private val actionGroup = TestActionGroup(this)
override fun onInstall() {
super.onInstall()
ActionUtils.registerGroup(this, actionGroup)
}
override fun onUninstall() {
super.onUninstall()
ActionUtils.unregisterGroup(this, actionGroup)
}
@Composable
override fun Content() {
val recomposeScope = currentRecomposeScope
val onBackPressedDispatcher =
LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
BackTopBarScaffold(
onBackPress = { onBackPressedDispatcher?.onBackPressed() },
titleText = getResources().getString(R.string.text_settings)
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
val factory = DefaultActionAbilityFactory(
ability = Unit,
onInvalidate = { recomposeScope.invalidate() })
ActionGroupLazyRow(
actionGroup = actionGroup,
factory = { factory },
itemContent = { action, isEnabled, onClick ->
IconButton(enabled = isEnabled, onClick = onClick) {
Icon(
painter = rememberDrawablePainter(
drawable = action.getResources()
.getDrawable(action.getIconId(), null)
),
contentDescription = null
)
}
}
)
ActionGroupLazyColumn(
actionGroup = actionGroup,
factory = { factory },
itemContent = { action, isEnabled, onClick ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(enabled = isEnabled, onClick = onClick)
) {
Icon(
painter = rememberDrawablePainter(
drawable = action.getResources()
.getDrawable(action.getIconId(), null)
),
contentDescription = null
)
Text(text = action.getName())
}
}
)
}
}
}
}
编写测试用的动作, 下面是 jetpack compose 实现的动作
package com.m8test.plugins.common.action
import androidx.compose.material3.Text
import com.m8test.action.api.VisualAction
import com.m8test.action.impl.DefaultComposableAction
import com.m8test.plugin.api.PluggableApkPlugin
/**
* Description TODO
*
* @date 2025/01/01 12:26:18
* @author M8Test,
[email protected], https://m8test.com
*/
object ComposableActions {
fun getWrapContentDialogAction(plugin: PluggableApkPlugin) = DefaultComposableAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "非全屏对话框(Composable)",
id = "ComposableWrapContentDialog",
location = VisualAction.Location.WrapContentDialog,
content = { ability ->
Text(text = "非全屏对话框(Composable)")
}
)
fun getWrapContentBottomSheetAction(plugin: PluggableApkPlugin) = DefaultComposableAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "非全屏底部弹窗(Composable)",
id = "ComposableWrapContentBottomSheet",
location = VisualAction.Location.WrapContentBottomSheet,
content = { ability ->
Text(text = "非全屏底部弹窗(Composable)")
})
fun getFullscreenDialogAction(plugin: PluggableApkPlugin) = DefaultComposableAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "全屏对话框(Composable)",
id = "ComposableFullscreenDialog",
location = VisualAction.Location.FullscreenDialog,
content = { ability ->
Text(text = "全屏对话框(Composable)")
}
)
fun getFullscreenBottomSheetAction(plugin: PluggableApkPlugin) = DefaultComposableAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "全屏底部弹窗(Composable)",
id = "ComposableFullscreenBottomSheet",
location = VisualAction.Location.FullscreenBottomSheet,
content = {
Text(text = "全屏底部弹窗(Composable)")
}
)
fun getWrapContentFloatyAction(plugin: PluggableApkPlugin) = DefaultComposableAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "非全屏悬浮窗口(Composable)",
id = "ComposableWrapContentFloaty",
location = VisualAction.Location.WrapContentFloaty,
content = { ability ->
Text(text = "非全屏悬浮窗口(Composable)")
}
)
fun getFullscreenFloatyAction(plugin: PluggableApkPlugin) = DefaultComposableAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "全屏悬浮窗口(Composable)",
id = "ComposableFullscreenFloaty",
location = VisualAction.Location.FullscreenFloaty,
content = { ability ->
Text(text = "全屏悬浮窗口(Composable)")
}
)
}
继续编写测试用的动作, 下面是 View 实现的动作
package com.m8test.plugins.common.action
import android.widget.TextView
import com.m8test.action.api.VisualAction
import com.m8test.action.impl.DefaultViewAction
import com.m8test.plugin.api.PluggableApkPlugin
/**
* Description TODO
*
* @date 2025/01/01 12:26:18
* @author M8Test,
[email protected], https://m8test.com
*/
object ViewActions {
fun getWrapContentDialogAction(plugin: PluggableApkPlugin) = DefaultViewAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "非全屏对话框(View)",
id = "ViewWrapContentDialog",
location = VisualAction.Location.WrapContentDialog,
viewCreator = { ability ->
TextView(ability.getContext()).apply { text = "非全屏对话框(View)" }
}
)
fun getWrapContentBottomSheetAction(plugin: PluggableApkPlugin) = DefaultViewAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "非全屏底部弹窗(View)",
id = "ViewWrapContentBottomSheet",
location = VisualAction.Location.WrapContentBottomSheet,
viewCreator = { ability ->
TextView(ability.getContext()).apply { text = "非全屏底部弹窗(View)" }
}
)
fun getFullscreenDialogAction(plugin: PluggableApkPlugin) = DefaultViewAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "全屏对话框(View)",
id = "ViewFullscreenDialog",
location = VisualAction.Location.FullscreenDialog,
viewCreator = { ability ->
TextView(ability.getContext()).apply { text = "全屏对话框(View)" }
}
)
fun getFullscreenBottomSheetAction(plugin: PluggableApkPlugin) = DefaultViewAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "全屏底部弹窗(View)",
id = "ViewFullscreenBottomSheet",
location = VisualAction.Location.FullscreenBottomSheet,
viewCreator = { ability ->
TextView(ability.getContext()).apply { text = "全屏底部弹窗(View)" }
}
)
fun getWrapContentFloatyAction(plugin: PluggableApkPlugin) = DefaultViewAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "非全屏悬浮窗口(View)",
id = "ViewWrapContentFloaty",
location = VisualAction.Location.WrapContentFloaty,
viewCreator = { ability ->
TextView(ability.getContext()).apply { text = "非全屏悬浮窗口(View)" }
}
)
fun getFullscreenFloatyAction(plugin: PluggableApkPlugin) = DefaultViewAction(
resources = plugin.getResources(),
iconId = R.drawable.ic_launcher,
name = "全屏悬浮窗口(View)",
id = "ViewFullscreenFloaty",
location = VisualAction.Location.FullscreenFloaty,
viewCreator = { ability ->
TextView(ability.getContext()).apply { text = "全屏悬浮窗口(View)" }
}
)
}