From 5d8958d2bcdfbec411115048b0af732676372b37 Mon Sep 17 00:00:00 2001 From: Tornaco Date: Fri, 8 Mar 2024 15:18:45 +0800 Subject: [PATCH] [app] add launchOtherAppRules --- .../app/activity/ActivityStackSupervisor.java | 16 +++ android/app/src/main/AndroidManifest.xml | 3 + .../LaunchOtherAppListActivity.java | 5 + .../launchother/LaunchOtherAppRuleActivity.kt | 123 ++++++++++++++++++ .../LaunchOtherAppRuleViewModel.kt | 60 +++++++++ .../res/menu/module_launch_other_app_list.xml | 5 + 6 files changed, 212 insertions(+) create mode 100644 android/app/src/main/java/now/fortuitous/thanos/launchother/LaunchOtherAppRuleActivity.kt create mode 100644 android/app/src/main/java/now/fortuitous/thanos/launchother/LaunchOtherAppRuleViewModel.kt diff --git a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/activity/ActivityStackSupervisor.java b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/activity/ActivityStackSupervisor.java index 44a15b48e..6414e0412 100644 --- a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/activity/ActivityStackSupervisor.java +++ b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/app/activity/ActivityStackSupervisor.java @@ -1,6 +1,7 @@ package github.tornaco.android.thanos.core.app.activity; import android.content.ComponentName; +import android.os.RemoteException; import java.util.List; @@ -175,4 +176,19 @@ public boolean isLaunchOtherAppBlockerEnabled() { public void setLaunchOtherAppBlockerEnabled(boolean enable) { supervisor.setLaunchOtherAppBlockerEnabled(enable); } + + @SneakyThrows + public void addLaunchOtherAppRule(String rule) throws RemoteException { + supervisor.addLaunchOtherAppRule(rule); + } + + @SneakyThrows + public void deleteLaunchOtherAppRule(String rule) throws RemoteException { + supervisor.deleteLaunchOtherAppRule(rule); + } + + @SneakyThrows + public String[] getAllLaunchOtherAppRules() throws RemoteException { + return supervisor.getAllLaunchOtherAppRules(); + } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 0ff887947..15de3a53a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -253,6 +253,9 @@ + selectAll(ActivityStackSupervisor.LaunchOtherAppPkgSetting.IGNORE)).show(); return true; } + if (R.id.action_rule == item.getItemId()) { + LaunchOtherAppRuleActivity.start(thisActivity()); + return true; + } return super.onOptionsItemSelected(item); } diff --git a/android/app/src/main/java/now/fortuitous/thanos/launchother/LaunchOtherAppRuleActivity.kt b/android/app/src/main/java/now/fortuitous/thanos/launchother/LaunchOtherAppRuleActivity.kt new file mode 100644 index 000000000..531aab919 --- /dev/null +++ b/android/app/src/main/java/now/fortuitous/thanos/launchother/LaunchOtherAppRuleActivity.kt @@ -0,0 +1,123 @@ +package now.fortuitous.thanos.launchother + +import android.content.Context +import android.content.Intent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import com.google.accompanist.swiperefresh.SwipeRefresh +import com.google.accompanist.swiperefresh.SwipeRefreshIndicator +import com.google.accompanist.swiperefresh.rememberSwipeRefreshState +import dagger.hilt.android.AndroidEntryPoint +import github.tornaco.android.thanos.module.compose.common.ComposeThemeActivity +import github.tornaco.android.thanos.module.compose.common.DisposableEffectWithLifeCycle +import github.tornaco.android.thanos.module.compose.common.theme.TypographyDefaults +import github.tornaco.android.thanos.module.compose.common.widget.ListItem +import github.tornaco.android.thanos.module.compose.common.widget.TextInputDialog +import github.tornaco.android.thanos.module.compose.common.widget.ThanoxSmallAppBarScaffold +import github.tornaco.android.thanos.module.compose.common.widget.rememberTextInputState + +@AndroidEntryPoint +class LaunchOtherAppRuleActivity : ComposeThemeActivity() { + companion object { + @JvmStatic + fun start(context: Context) { + val starter = Intent(context, LaunchOtherAppRuleActivity::class.java) + context.startActivity(starter) + } + } + + @Composable + override fun Content() { + val viewModel = hiltViewModel() + val state by viewModel.state.collectAsState() + + val inputDialog = rememberTextInputState( + title = stringResource(id = github.tornaco.android.thanos.R.string.menu_title_rules), + message = "" + ) { + viewModel.add(it) + } + TextInputDialog(state = inputDialog) + + ThanoxSmallAppBarScaffold( + title = { + Text( + text = stringResource(id = github.tornaco.android.thanos.R.string.menu_title_rules), + style = TypographyDefaults.appBarTitleTextStyle() + ) + }, + onBackPressed = { + thisActivity().finish() + }, + actions = { + IconButton(onClick = { + inputDialog.show() + }) { + Icon( + painter = painterResource(id = github.tornaco.android.thanos.R.drawable.ic_add_fill), + contentDescription = "Add" + ) + } + } + ) { paddings -> + + DisposableEffectWithLifeCycle(onResume = { + viewModel.refresh() + }) + + LaunchedEffect(viewModel) { + viewModel.refresh() + } + + SwipeRefresh( + state = rememberSwipeRefreshState(state.isLoading), + onRefresh = { viewModel.refresh() }, + indicatorPadding = paddings, + clipIndicatorToPadding = false, + indicator = { state, refreshTriggerDistance -> + SwipeRefreshIndicator( + state = state, + refreshTriggerDistance = refreshTriggerDistance, + scale = true, + arrowEnabled = false, + contentColor = MaterialTheme.colorScheme.primary + ) + } + ) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(paddings) + ) { + items(state.ruleItems) { + ListItem(modifier = Modifier, title = it.rule, + action1 = { + IconButton(onClick = { + viewModel.remove(it.rule) + }) { + Icon( + painter = painterResource(id = github.tornaco.android.thanos.R.drawable.ic_delete_bin_2_fill), + contentDescription = "Delete" + ) + } + }) + } + } + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/now/fortuitous/thanos/launchother/LaunchOtherAppRuleViewModel.kt b/android/app/src/main/java/now/fortuitous/thanos/launchother/LaunchOtherAppRuleViewModel.kt new file mode 100644 index 000000000..4b7163948 --- /dev/null +++ b/android/app/src/main/java/now/fortuitous/thanos/launchother/LaunchOtherAppRuleViewModel.kt @@ -0,0 +1,60 @@ +package now.fortuitous.thanos.launchother + +import android.annotation.SuppressLint +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import github.tornaco.android.thanos.core.app.ThanosManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import javax.inject.Inject + +data class RuleItem(val rule: String) + +data class OpsState( + val isLoading: Boolean, + val ruleItems: List +) + +@SuppressLint("StaticFieldLeak") +@HiltViewModel +class LaunchOtherAppRuleViewModel @Inject constructor(@ApplicationContext private val context: Context) : + ViewModel() { + + private val _state = + MutableStateFlow( + OpsState( + isLoading = true, + ruleItems = emptyList() + ) + ) + val state = _state.asStateFlow() + + private val thanos by lazy { ThanosManager.from(context) } + private val supervisor by lazy { thanos.activityStackSupervisor } + + fun refresh() { + _state.value = _state.value.copy(isLoading = true) + viewModelScope.launch { + val rules = withContext(Dispatchers.IO) { + supervisor.allLaunchOtherAppRules.map { RuleItem(it) } + } + _state.value = _state.value.copy(ruleItems = rules, isLoading = false) + } + } + + fun add(rule: String) { + supervisor.addLaunchOtherAppRule(rule) + refresh() + } + + fun remove(rule: String) { + supervisor.deleteLaunchOtherAppRule(rule) + refresh() + } +} \ No newline at end of file diff --git a/android/app/src/main/res/menu/module_launch_other_app_list.xml b/android/app/src/main/res/menu/module_launch_other_app_list.xml index 1304540b7..3f39f2ddd 100755 --- a/android/app/src/main/res/menu/module_launch_other_app_list.xml +++ b/android/app/src/main/res/menu/module_launch_other_app_list.xml @@ -23,4 +23,9 @@ android:title="@string/module_ops_mode_ignore_all" app:showAsAction="never" /> + +