Fix UI bugs

This commit is contained in:
Nullptr 2022-03-01 17:29:37 +08:00
parent 1096ba6658
commit a44cd8ba3b
10 changed files with 208 additions and 148 deletions

View File

@ -5,7 +5,7 @@ buildscript {
google() google()
mavenCentral() mavenCentral()
} }
val agpVersion by extra("7.1.1") val agpVersion by extra("7.1.2")
dependencies { dependencies {
classpath("com.android.tools.build:gradle:$agpVersion") classpath("com.android.tools.build:gradle:$agpVersion")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")

View File

@ -11,8 +11,6 @@ val verName: String by rootProject.extra
val androidSourceCompatibility: JavaVersion by rootProject.extra val androidSourceCompatibility: JavaVersion by rootProject.extra
val androidTargetCompatibility: JavaVersion by rootProject.extra val androidTargetCompatibility: JavaVersion by rootProject.extra
val composeVersion = "1.2.0-alpha03"
plugins { plugins {
id("com.android.application") id("com.android.application")
id("kotlin-parcelize") id("kotlin-parcelize")
@ -52,7 +50,7 @@ android {
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = composeVersion kotlinCompilerExtensionVersion = "1.2.0-alpha03"
} }
sourceSets["main"].assets.srcDirs(rootProject.projectDir.resolve("out/assets")) sourceSets["main"].assets.srcDirs(rootProject.projectDir.resolve("out/assets"))
@ -85,14 +83,14 @@ dependencies {
implementation(projects.patch) implementation(projects.patch)
implementation("androidx.core:core-ktx:1.7.0") implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.activity:activity-compose:1.5.0-alpha02") implementation("androidx.activity:activity-compose:1.5.0-alpha03")
implementation("androidx.compose.material:material-icons-extended:1.1.0") implementation("androidx.compose.material:material-icons-extended:1.1.1")
implementation("androidx.compose.material3:material3:1.0.0-alpha05") implementation("androidx.compose.material3:material3:1.0.0-alpha06")
implementation("androidx.compose.runtime:runtime-livedata:$composeVersion") implementation("androidx.compose.runtime:runtime-livedata:1.1.1")
implementation("androidx.compose.ui:ui:$composeVersion") implementation("androidx.compose.ui:ui:1.1.1")
implementation("androidx.compose.ui:ui-tooling:$composeVersion") implementation("androidx.compose.ui:ui-tooling:1.1.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha02") implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha03")
implementation("androidx.navigation:navigation-compose:2.5.0-alpha02") implementation("androidx.navigation:navigation-compose:2.5.0-alpha03")
implementation("androidx.preference:preference:1.2.0") implementation("androidx.preference:preference:1.2.0")
implementation("com.google.accompanist:accompanist-drawablepainter:0.24.2-alpha") implementation("com.google.accompanist:accompanist-drawablepainter:0.24.2-alpha")
implementation("com.google.accompanist:accompanist-navigation-animation:0.24.2-alpha") implementation("com.google.accompanist:accompanist-navigation-animation:0.24.2-alpha")

View File

@ -10,7 +10,6 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
@ -34,23 +33,13 @@ class MainActivity : ComponentActivity() {
LSPTheme { LSPTheme {
CompositionLocalProvider(LocalNavController provides navController) { CompositionLocalProvider(LocalNavController provides navController) {
Scaffold( Scaffold(
topBar = {
navController.currentBackStackEntry?.let {
CompositionLocalProvider(LocalViewModelStoreOwner provides it) {
currentPage?.topBar?.invoke()
}
}
},
bottomBar = { bottomBar = {
MainNavigationBar(mainPage) { MainNavigationBar(mainPage) {
mainPage = it mainPage = it
navController.navigate(it.name) navController.navigate(it.name) {
} currentRoute?.let { route ->
}, popUpTo(route) { inclusive = true }
floatingActionButton = { }
navController.currentBackStackEntry?.let {
CompositionLocalProvider(LocalViewModelStoreOwner provides it) {
currentPage?.fab?.invoke()
} }
} }
} }

View File

@ -1,18 +1,25 @@
package org.lsposed.lspatch.ui.page package org.lsposed.lspatch.ui.page
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.SmallTopAppBar
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import org.lsposed.lspatch.R import org.lsposed.lspatch.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun HomePage() { fun HomePage() {
Scaffold(
topBar = { TopBar() }
) { innerPadding ->
}
} }
@Composable @Composable
fun HomeTopBar() { private fun TopBar() {
SmallTopAppBar( SmallTopAppBar(
title = { Text(stringResource(R.string.app_name)) } title = { Text(stringResource(R.string.app_name)) }
) )

View File

@ -2,31 +2,36 @@ package org.lsposed.lspatch.ui.page
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.*
import androidx.compose.material3.Icon
import androidx.compose.material3.SmallTopAppBar
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import org.lsposed.lspatch.R import org.lsposed.lspatch.R
import org.lsposed.lspatch.ui.util.LocalNavController import org.lsposed.lspatch.ui.util.LocalNavController
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ManageTopBar() { fun ManagePage() {
val navController = LocalNavController.current
Scaffold(
topBar = { TopBar() },
floatingActionButton = {
Fab { navController.navigate(PageList.NewPatch.name) }
}
) { innerPadding ->
}
}
@Composable
private fun TopBar() {
SmallTopAppBar( SmallTopAppBar(
title = { Text(PageList.Manage.title) } title = { Text(PageList.Manage.title) }
) )
} }
@Composable @Composable
fun ManageFab() { private fun Fab(onClick: () -> Unit) {
val navController = LocalNavController.current FloatingActionButton(onClick = onClick) {
FloatingActionButton(onClick = { navController.navigate(PageList.NewPatch.name) }) {
Icon(Icons.Filled.Add, stringResource(R.string.add)) Icon(Icons.Filled.Add, stringResource(R.string.add))
} }
} }
@Composable
fun ManagePage() {
}

View File

@ -1,5 +1,8 @@
package org.lsposed.lspatch.ui.page package org.lsposed.lspatch.ui.page
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
@ -29,8 +32,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel import androidx.navigation.NavBackStackEntry
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus import com.google.accompanist.permissions.PermissionStatus
import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.rememberPermissionState
@ -49,34 +51,27 @@ import org.lsposed.lspatch.ui.viewmodel.AppInfo
import org.lsposed.patch.util.Logger import org.lsposed.patch.util.Logger
import java.io.File import java.io.File
enum class PatchState { private enum class PatchState {
SELECTING, CONFIGURING, SUBMITTING, PATCHING, FINISHED, ERROR SELECTING, CONFIGURING, SUBMITTING, PATCHING, FINISHED, ERROR
} }
class NewPatchPageViewModel : ViewModel() { @OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3Api::class)
var patchState by mutableStateOf(PatchState.SELECTING)
var patchOptions by mutableStateOf<Patcher.Options?>(null)
}
@Composable @Composable
fun NewPatchFab() { fun NewPatchPage(entry: NavBackStackEntry) {
val viewModel = viewModel<NewPatchPageViewModel>()
if (viewModel.patchState == PatchState.CONFIGURING) {
ExtendedFloatingActionButton(
text = { Text(stringResource(R.string.patch_start)) },
icon = { Icon(Icons.Outlined.AutoFixHigh, null) },
onClick = { viewModel.patchState = PatchState.SUBMITTING }
)
}
}
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun NewPatchPage() {
val viewModel = viewModel<NewPatchPageViewModel>()
val navController = LocalNavController.current val navController = LocalNavController.current
val patchApp by navController.currentBackStackEntry!!.observeState<AppInfo>("appInfo") val patchApp by entry.observeState<AppInfo>("appInfo")
if (viewModel.patchState == PatchState.SELECTING && patchApp != null) viewModel.patchState = PatchState.CONFIGURING val isCancelled by entry.observeState<Boolean>("isCancelled")
var patchState by rememberSaveable { mutableStateOf(PatchState.SELECTING) }
var patchOptions by rememberSaveable { mutableStateOf<Patcher.Options?>(null) }
if (patchState == PatchState.SELECTING) {
when {
isCancelled == true -> {
LaunchedEffect(entry) { navController.popBackStack() }
return
}
patchApp != null -> patchState = PatchState.CONFIGURING
}
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
val filePermissionState = rememberPermissionState(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) val filePermissionState = rememberPermissionState(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
@ -100,17 +95,79 @@ fun NewPatchPage() {
} }
} }
Log.d(TAG, "NewPatchPage: ${viewModel.patchState}") Log.d(TAG, "NewPatchPage: $patchState")
when (viewModel.patchState) { if (patchState == PatchState.SELECTING) {
PatchState.SELECTING -> navController.navigate(PageList.SelectApps.name + "/false") LaunchedEffect(entry) {
PatchState.CONFIGURING, PatchState.SUBMITTING -> PatchOptionsPage(patchApp!!) navController.navigate(PageList.SelectApps.name + "/false")
PatchState.PATCHING, PatchState.FINISHED, PatchState.ERROR -> DoPatchPage(viewModel.patchOptions!!) }
} else {
Scaffold(
topBar = { TopBar(patchApp!!) },
floatingActionButton = {
if (patchState == PatchState.CONFIGURING) {
ConfiguringFab { patchState = PatchState.SUBMITTING }
}
}
) { innerPadding ->
if (patchState == PatchState.CONFIGURING || patchState == PatchState.SUBMITTING) {
PatchOptionsBody(
modifier = Modifier.padding(innerPadding),
patchState = patchState,
patchApp = patchApp!!,
onSubmit = {
patchOptions = it
patchState = PatchState.PATCHING
}
)
} else {
DoPatchBody(
modifier = Modifier.padding(innerPadding),
patchState = patchState,
patchOptions = patchOptions!!,
onFinish = { patchState = PatchState.FINISHED },
onFail = { patchState = PatchState.ERROR }
)
}
}
} }
} }
@Composable @Composable
private fun PatchOptionsPage(patchApp: AppInfo) { private fun TopBar(patchApp: AppInfo) {
val viewModel = viewModel<NewPatchPageViewModel>() SmallTopAppBar(
title = {
Column {
Text(
text = patchApp.label,
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(horizontal = 24.dp)
)
Text(
text = patchApp.app.packageName,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(horizontal = 24.dp)
)
}
}
)
}
@Composable
private fun ConfiguringFab(onClick: () -> Unit) {
ExtendedFloatingActionButton(
text = { Text(stringResource(R.string.patch_start)) },
icon = { Icon(Icons.Outlined.AutoFixHigh, null) },
onClick = onClick
)
}
@Composable
private fun PatchOptionsBody(
modifier: Modifier,
patchState: PatchState,
patchApp: AppInfo,
onSubmit: (Patcher.Options) -> Unit
) {
val navController = LocalNavController.current val navController = LocalNavController.current
var useManager by rememberSaveable { mutableStateOf(true) } var useManager by rememberSaveable { mutableStateOf(true) }
var debuggable by rememberSaveable { mutableStateOf(false) } var debuggable by rememberSaveable { mutableStateOf(false) }
@ -122,9 +179,9 @@ private fun PatchOptionsPage(patchApp: AppInfo) {
val embeddedModules = navController.currentBackStackEntry!! val embeddedModules = navController.currentBackStackEntry!!
.savedStateHandle.getLiveData<SnapshotStateList<AppInfo>>("selected", SnapshotStateList()) .savedStateHandle.getLiveData<SnapshotStateList<AppInfo>>("selected", SnapshotStateList())
if (viewModel.patchState == PatchState.SUBMITTING) LaunchedEffect(patchApp) { if (patchState == PatchState.SUBMITTING) LaunchedEffect(patchApp) {
if (useManager) embeddedModules.value?.clear() if (useManager) embeddedModules.value?.clear()
viewModel.patchOptions = Patcher.Options( val options = Patcher.Options(
apkPaths = arrayOf(patchApp.app.sourceDir), // TODO: Split Apk apkPaths = arrayOf(patchApp.app.sourceDir), // TODO: Split Apk
debuggable = debuggable, debuggable = debuggable,
sigbypassLevel = sigBypassLevel, sigbypassLevel = sigBypassLevel,
@ -134,24 +191,10 @@ private fun PatchOptionsPage(patchApp: AppInfo) {
verbose = true, verbose = true,
embeddedModules = embeddedModules.value?.map { it.app.sourceDir } ?: emptyList() // TODO: Split Apk embeddedModules = embeddedModules.value?.map { it.app.sourceDir } ?: emptyList() // TODO: Split Apk
) )
viewModel.patchState = PatchState.PATCHING onSubmit(options)
} }
Column( Column(modifier.verticalScroll(rememberScrollState())) {
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(top = 24.dp)
) {
Text(
text = patchApp.label,
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(horizontal = 24.dp)
)
Text(
text = patchApp.app.packageName,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(horizontal = 24.dp)
)
Text( Text(
text = stringResource(R.string.patch_mode), text = stringResource(R.string.patch_mode),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
@ -219,9 +262,14 @@ private fun PatchOptionsPage(patchApp: AppInfo) {
} }
@Composable @Composable
private fun DoPatchPage(patcherOptions: Patcher.Options) { private fun DoPatchBody(
modifier: Modifier,
patchState: PatchState,
patchOptions: Patcher.Options,
onFinish: () -> Unit,
onFail: () -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
val viewModel = viewModel<NewPatchPageViewModel>()
val navController = LocalNavController.current val navController = LocalNavController.current
val logs = remember { mutableStateListOf<Pair<Int, String>>() } val logs = remember { mutableStateListOf<Pair<Int, String>>() }
val logger = remember { val logger = remember {
@ -245,27 +293,27 @@ private fun DoPatchPage(patcherOptions: Patcher.Options) {
} }
} }
LaunchedEffect(patcherOptions) { LaunchedEffect(patchOptions) {
try { try {
Patcher.patch(context, logger, patcherOptions) Patcher.patch(context, logger, patchOptions)
viewModel.patchState = PatchState.FINISHED onFinish()
} catch (t: Throwable) { } catch (t: Throwable) {
logger.e(t.message.orEmpty()) logger.e(t.message.orEmpty())
logger.e(t.stackTraceToString()) logger.e(t.stackTraceToString())
viewModel.patchState = PatchState.ERROR onFail()
} finally { } finally {
File(patcherOptions.outputPath).deleteRecursively() File(patchOptions.outputPath).deleteRecursively()
} }
} }
Column( Column(
Modifier modifier
.fillMaxSize() .fillMaxSize()
.padding(24.dp) .padding(24.dp)
.wrapContentHeight() .wrapContentHeight()
.animateContentSize(spring(stiffness = Spring.StiffnessLow)) .animateContentSize(spring(stiffness = Spring.StiffnessLow))
) { ) {
ShimmerAnimation(enabled = viewModel.patchState == PatchState.PATCHING) { ShimmerAnimation(enabled = patchState == PatchState.PATCHING) {
CompositionLocalProvider( CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace) LocalTextStyle provides MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace)
) { ) {
@ -296,7 +344,7 @@ private fun DoPatchPage(patcherOptions: Patcher.Options) {
} }
} }
if (viewModel.patchState == PatchState.FINISHED) { if (patchState == PatchState.FINISHED) {
Row(Modifier.padding(top = 12.dp)) { Row(Modifier.padding(top = 12.dp)) {
Button( Button(
onClick = { navController.popBackStack() }, onClick = { navController.popBackStack() },
@ -310,6 +358,23 @@ private fun DoPatchPage(patcherOptions: Patcher.Options) {
content = { Text(stringResource(R.string.patch_install)) } content = { Text(stringResource(R.string.patch_install)) }
) )
} }
} else if (patchState == PatchState.ERROR) {
Row(Modifier.padding(top = 12.dp)) {
Button(
onClick = { navController.popBackStack() },
modifier = Modifier.weight(1f),
content = { Text(stringResource(R.string.patch_return)) }
)
Spacer(Modifier.weight(0.2f))
Button(
onClick = {
val cm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
cm.setPrimaryClip(ClipData.newPlainText("LSPatch", logs.joinToString { it.second + "\n" }))
},
modifier = Modifier.weight(1f),
content = { Text(stringResource(R.string.patch_copy_error)) }
)
}
} }
} }
} }

View File

@ -16,46 +16,35 @@ enum class PageList(
val iconSelected: ImageVector? = null, val iconSelected: ImageVector? = null,
val iconNotSelected: ImageVector? = null, val iconNotSelected: ImageVector? = null,
val arguments: List<NamedNavArgument> = emptyList(), val arguments: List<NamedNavArgument> = emptyList(),
val topBar: @Composable () -> Unit,
val fab: @Composable () -> Unit = {},
val body: @Composable NavBackStackEntry.() -> Unit val body: @Composable NavBackStackEntry.() -> Unit
) { ) {
Home( Home(
iconSelected = Icons.Filled.Home, iconSelected = Icons.Filled.Home,
iconNotSelected = Icons.Outlined.Home, iconNotSelected = Icons.Outlined.Home,
topBar = { HomeTopBar() },
body = { HomePage() } body = { HomePage() }
), ),
Manage( Manage(
iconSelected = Icons.Filled.Dashboard, iconSelected = Icons.Filled.Dashboard,
iconNotSelected = Icons.Outlined.Dashboard, iconNotSelected = Icons.Outlined.Dashboard,
topBar = { ManageTopBar() }, body = { ManagePage() }
fab = { ManageFab() },
body = {}
), ),
Repo( Repo(
iconSelected = Icons.Filled.GetApp, iconSelected = Icons.Filled.GetApp,
iconNotSelected = Icons.Outlined.GetApp, iconNotSelected = Icons.Outlined.GetApp,
topBar = {},
body = {} body = {}
), ),
Settings( Settings(
iconSelected = Icons.Filled.Settings, iconSelected = Icons.Filled.Settings,
iconNotSelected = Icons.Outlined.Settings, iconNotSelected = Icons.Outlined.Settings,
topBar = {},
body = {} body = {}
), ),
NewPatch( NewPatch(
topBar = {}, body = { NewPatchPage(this) }
fab = { NewPatchFab() },
body = { NewPatchPage() }
), ),
SelectApps( SelectApps(
arguments = listOf( arguments = listOf(
navArgument("multiSelect") { type = NavType.BoolType } navArgument("multiSelect") { type = NavType.BoolType }
), ),
topBar = { SelectAppsTopBar() },
fab = { SelectAppsFab() },
body = { SelectAppsPage(this) } body = { SelectAppsPage(this) }
); );

View File

@ -1,15 +1,14 @@
package org.lsposed.lspatch.ui.page package org.lsposed.lspatch.ui.page
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Done import androidx.compose.material.icons.outlined.Done
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.*
import androidx.compose.material3.Icon
import androidx.compose.material3.SmallTopAppBar
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -29,40 +28,53 @@ import org.lsposed.lspatch.ui.util.setState
import org.lsposed.lspatch.ui.viewmodel.AppInfo import org.lsposed.lspatch.ui.viewmodel.AppInfo
import org.lsposed.lspatch.ui.viewmodel.SelectAppsViewModel import org.lsposed.lspatch.ui.viewmodel.SelectAppsViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SelectAppsTopBar() { fun SelectAppsPage(entry: NavBackStackEntry) {
val navController = LocalNavController.current
val multiSelect = entry.arguments?.get("multiSelect") as? Boolean
?: throw IllegalArgumentException("multiSelect is null")
BackHandler {
navController.previousBackStackEntry!!.setState("isCancelled", true)
navController.popBackStack()
}
Scaffold(
topBar = { TopBar() },
floatingActionButton = {
if (multiSelect) MultiSelectFab()
}
) { innerPadding ->
if (multiSelect) {
MultiSelect(
modifier = Modifier.padding(innerPadding),
filter = { it.app.metaData?.get("xposedminversion") != null }
)
} else {
SingleSelect(modifier = Modifier.padding(innerPadding))
}
}
}
@Composable
private fun TopBar() {
SmallTopAppBar( SmallTopAppBar(
title = { Text(stringResource(R.string.page_select_apps)) } title = { Text(stringResource(R.string.page_select_apps)) }
) )
} }
@Composable @Composable
fun SelectAppsFab() { private fun MultiSelectFab() {
val navController = LocalNavController.current val navController = LocalNavController.current
val viewModel = viewModel<SelectAppsViewModel>() FloatingActionButton(onClick = { navController.popBackStack() }) {
val multiSelect = navController.currentBackStackEntry?.arguments?.get("multiSelect") as? Boolean Icon(Icons.Outlined.Done, stringResource(R.string.add))
?: throw IllegalArgumentException("multiSelect is null")
if (multiSelect) {
FloatingActionButton(onClick = { viewModel.done = true }) {
Icon(Icons.Outlined.Done, stringResource(R.string.add))
}
}
}
@Composable
fun SelectAppsPage(entry: NavBackStackEntry) {
val multiSelect = entry.arguments?.get("multiSelect") as? Boolean
?: throw IllegalArgumentException("multiSelect is null")
if (multiSelect) {
MultiSelect(filter = { it.app.metaData?.get("xposedminversion") != null })
} else {
SingleSelect()
} }
} }
@Composable @Composable
private fun SingleSelect( private fun SingleSelect(
modifier: Modifier,
filter: (AppInfo) -> Boolean = { it.app.flags and ApplicationInfo.FLAG_SYSTEM == 0 } filter: (AppInfo) -> Boolean = { it.app.flags and ApplicationInfo.FLAG_SYSTEM == 0 }
) { ) {
val context = LocalContext.current val context = LocalContext.current
@ -75,7 +87,7 @@ private fun SingleSelect(
SwipeRefresh( SwipeRefresh(
state = rememberSwipeRefreshState(viewModel.isRefreshing), state = rememberSwipeRefreshState(viewModel.isRefreshing),
onRefresh = { viewModel.filterAppList(context, true, filter) }, onRefresh = { viewModel.filterAppList(context, true, filter) },
modifier = Modifier.fillMaxSize() modifier = modifier.fillMaxSize()
) { ) {
LazyColumn { LazyColumn {
items(viewModel.filteredList) { items(viewModel.filteredList) {
@ -95,6 +107,7 @@ private fun SingleSelect(
@Composable @Composable
private fun MultiSelect( private fun MultiSelect(
modifier: Modifier,
filter: (AppInfo) -> Boolean = { true } filter: (AppInfo) -> Boolean = { true }
) { ) {
val context = LocalContext.current val context = LocalContext.current
@ -105,16 +118,11 @@ private fun MultiSelect(
LaunchedEffect(viewModel) { LaunchedEffect(viewModel) {
viewModel.filterAppList(context, false, filter) viewModel.filterAppList(context, false, filter)
} }
if (viewModel.done) {
LaunchedEffect(viewModel) {
navController.popBackStack()
}
}
SwipeRefresh( SwipeRefresh(
state = rememberSwipeRefreshState(viewModel.isRefreshing), state = rememberSwipeRefreshState(viewModel.isRefreshing),
onRefresh = { viewModel.filterAppList(context, true, filter) }, onRefresh = { viewModel.filterAppList(context, true, filter) },
modifier = Modifier.fillMaxSize() modifier = modifier.fillMaxSize()
) { ) {
LazyColumn { LazyColumn {
items(viewModel.filteredList) { items(viewModel.filteredList) {

View File

@ -29,8 +29,6 @@ class SelectAppsViewModel : ViewModel() {
Log.d(TAG, "SelectAppsViewModel ${toString().substringAfterLast('@')} construct") Log.d(TAG, "SelectAppsViewModel ${toString().substringAfterLast('@')} construct")
} }
var done by mutableStateOf(false)
var isRefreshing by mutableStateOf(false) var isRefreshing by mutableStateOf(false)
private set private set

View File

@ -29,6 +29,7 @@
<string name="patch_start">Start Patch</string> <string name="patch_start">Start Patch</string>
<string name="patch_return">Return</string> <string name="patch_return">Return</string>
<string name="patch_install">Install</string> <string name="patch_install">Install</string>
<string name="patch_copy_error">Copy error</string>
<!-- Select Apps Page --> <!-- Select Apps Page -->
<string name="page_select_apps">Select Apps</string> <string name="page_select_apps">Select Apps</string>