Minimum availability
This commit is contained in:
parent
f7be0567eb
commit
d593107e04
|
|
@ -13,6 +13,7 @@ val composeVersion = "1.2.0-alpha03"
|
|||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-parcelize")
|
||||
kotlin("android")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ private fun MainNavHost(navController: NavHostController, modifier: Modifier) {
|
|||
@Composable
|
||||
private fun MainNavigationBar(page: PageList, onClick: (PageList) -> Unit) {
|
||||
NavigationBar(tonalElevation = 8.dp) {
|
||||
arrayOf(PageList.Home, PageList.Patches, PageList.Repo, PageList.Settings).forEach {
|
||||
arrayOf(PageList.Home, PageList.Manage, PageList.Repo, PageList.Settings).forEach {
|
||||
NavigationBarItem(
|
||||
selected = page == it,
|
||||
onClick = { onClick(it) },
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ import android.graphics.drawable.GradientDrawable
|
|||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -18,8 +16,7 @@ import androidx.compose.ui.unit.dp
|
|||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import org.lsposed.lspatch.ui.theme.LSPTheme
|
||||
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AppItem(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -28,7 +25,8 @@ fun AppItem(
|
|||
packageName: String,
|
||||
additionalInfo: (@Composable () -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: (() -> Unit)? = null
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
checked: Boolean? = null
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
|
|
@ -39,19 +37,28 @@ fun AppItem(
|
|||
)
|
||||
.padding(20.dp)
|
||||
) {
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(20.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberDrawablePainter(icon),
|
||||
contentDescription = label,
|
||||
modifier = Modifier.size(32.dp),
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
Column(Modifier.padding(start = 20.dp)) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(text = label, style = MaterialTheme.typography.bodyMedium)
|
||||
Text(text = packageName, style = MaterialTheme.typography.bodySmall)
|
||||
additionalInfo?.invoke()
|
||||
}
|
||||
if (checked != null) {
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = { onClick() },
|
||||
modifier = Modifier.padding(start = 20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,15 @@ fun SettingsSlot(
|
|||
extraContent: (@Composable ColumnScope.() -> Unit)? = null,
|
||||
action: (@Composable RowScope.() -> Unit)?,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth() then (
|
||||
val enabledModifier =
|
||||
if (enabled) Modifier
|
||||
.alpha(1f)
|
||||
.clickable(onClick = onClick)
|
||||
else Modifier.alpha(0.5f)
|
||||
)
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.then(enabledModifier)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
|
@ -54,7 +55,9 @@ fun SettingsSlot(
|
|||
Text(
|
||||
text = desc,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.alpha(0.75f).padding(top = 4.dp)
|
||||
modifier = Modifier
|
||||
.alpha(0.75f)
|
||||
.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
extraContent?.invoke(this)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package org.lsposed.lspatch.ui.page
|
||||
|
||||
import androidx.compose.material3.SmallTopAppBar
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import org.lsposed.lspatch.R
|
||||
|
||||
@Composable
|
||||
fun HomePage() {
|
||||
|
|
@ -9,5 +13,7 @@ fun HomePage() {
|
|||
|
||||
@Composable
|
||||
fun HomeTopBar() {
|
||||
|
||||
SmallTopAppBar(
|
||||
title = { Text(stringResource(R.string.app_name)) }
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,21 +12,21 @@ import org.lsposed.lspatch.R
|
|||
import org.lsposed.lspatch.ui.util.LocalNavController
|
||||
|
||||
@Composable
|
||||
fun PatchesTopBar() {
|
||||
fun ManageTopBar() {
|
||||
SmallTopAppBar(
|
||||
title = { Text(PageList.Patches.title) }
|
||||
title = { Text(PageList.Manage.title) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PatchesFab() {
|
||||
fun ManageFab() {
|
||||
val navController = LocalNavController.current
|
||||
FloatingActionButton(onClick = { navController.navigate(PageList.NewPatch.name) }) {
|
||||
Icon(Icons.Filled.Add, stringResource(R.string.patches_add))
|
||||
Icon(Icons.Filled.Add, stringResource(R.string.add))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PatchesPage() {
|
||||
fun ManagePage() {
|
||||
|
||||
}
|
||||
|
|
@ -20,8 +20,8 @@ import androidx.compose.material.icons.outlined.BugReport
|
|||
import androidx.compose.material.icons.outlined.WorkOutline
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -40,6 +40,7 @@ import org.lsposed.lspatch.ui.component.settings.SettingsItem
|
|||
import org.lsposed.lspatch.ui.util.LocalNavController
|
||||
import org.lsposed.lspatch.ui.util.isScrolledToEnd
|
||||
import org.lsposed.lspatch.ui.util.lastItemIndex
|
||||
import org.lsposed.lspatch.ui.util.observeState
|
||||
import org.lsposed.lspatch.ui.viewmodel.AppInfo
|
||||
import org.lsposed.patch.util.Logger
|
||||
|
||||
|
|
@ -68,8 +69,7 @@ fun NewPatchFab() {
|
|||
fun NewPatchPage() {
|
||||
val viewModel = viewModel<NewPatchPageViewModel>()
|
||||
val navController = LocalNavController.current
|
||||
val patchApp by navController.currentBackStackEntry!!.savedStateHandle
|
||||
.getLiveData<AppInfo>("appInfo").observeAsState()
|
||||
val patchApp by navController.currentBackStackEntry!!.observeState<AppInfo>("appInfo")
|
||||
if (viewModel.patchState == PatchState.SELECTING && patchApp != null) viewModel.patchState = PatchState.CONFIGURING
|
||||
|
||||
Log.d(TAG, "NewPatchPage: ${viewModel.patchState}")
|
||||
|
|
@ -83,6 +83,7 @@ fun NewPatchPage() {
|
|||
@Composable
|
||||
private fun PatchOptionsPage(patchApp: AppInfo) {
|
||||
val viewModel = viewModel<NewPatchPageViewModel>()
|
||||
val navController = LocalNavController.current
|
||||
var useManager by rememberSaveable { mutableStateOf(true) }
|
||||
var debuggable by rememberSaveable { mutableStateOf(false) }
|
||||
var v1 by rememberSaveable { mutableStateOf(false) }
|
||||
|
|
@ -90,9 +91,12 @@ private fun PatchOptionsPage(patchApp: AppInfo) {
|
|||
var v3 by rememberSaveable { mutableStateOf(true) }
|
||||
val sigBypassLevel by rememberSaveable { mutableStateOf(2) }
|
||||
var overrideVersionCode by rememberSaveable { mutableStateOf(false) }
|
||||
val embeddedModules = navController.currentBackStackEntry!!
|
||||
.savedStateHandle.getLiveData<SnapshotStateList<AppInfo>>("selected", SnapshotStateList())
|
||||
|
||||
if (viewModel.patchState == PatchState.SUBMITTING) LaunchedEffect(patchApp) {
|
||||
val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
|
||||
if (useManager) embeddedModules.value?.clear()
|
||||
viewModel.patchOptions = Patcher.Options(
|
||||
apkPaths = arrayOf(patchApp.app.sourceDir), // TODO: Split Apk
|
||||
outputPath = downloadDir,
|
||||
|
|
@ -102,7 +106,7 @@ private fun PatchOptionsPage(patchApp: AppInfo) {
|
|||
useManager = useManager,
|
||||
overrideVersionCode = overrideVersionCode,
|
||||
verbose = true,
|
||||
embeddedModules = emptyList() // TODO: Embed modules
|
||||
embeddedModules = embeddedModules.value?.map { it.app.sourceDir } ?: emptyList() // TODO: Split Apk
|
||||
)
|
||||
viewModel.patchState = PatchState.PATCHING
|
||||
}
|
||||
|
|
@ -145,7 +149,7 @@ private fun PatchOptionsPage(patchApp: AppInfo) {
|
|||
desc = stringResource(R.string.patch_portable_desc),
|
||||
extraContent = {
|
||||
TextButton(
|
||||
onClick = { /* TODO */ }
|
||||
onClick = { navController.navigate(PageList.SelectApps.name + "/true") }
|
||||
) {
|
||||
Text(text = stringResource(R.string.patch_embed_modules), style = MaterialTheme.typography.bodyLarge)
|
||||
}
|
||||
|
|
@ -229,14 +233,14 @@ private fun DoPatchPage(patcherOptions: Patcher.Options) {
|
|||
val patching by remember { derivedStateOf { viewModel.patchState == PatchState.PATCHING } }
|
||||
ShimmerAnimation(enabled = patching) {
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace)
|
||||
LocalTextStyle provides MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace)
|
||||
) {
|
||||
val scrollState = rememberLazyListState()
|
||||
LazyColumn(
|
||||
state = scrollState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 300.dp)
|
||||
.heightIn(max = 320.dp)
|
||||
.clip(RoundedCornerShape(32.dp))
|
||||
.background(brush)
|
||||
.padding(horizontal = 24.dp, vertical = 18.dp)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
package org.lsposed.lspatch.ui.page
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.GetApp
|
||||
import androidx.compose.material.icons.filled.Healing
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.outlined.GetApp
|
||||
import androidx.compose.material.icons.outlined.Healing
|
||||
import androidx.compose.material.icons.outlined.Home
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -32,11 +26,11 @@ enum class PageList(
|
|||
topBar = { HomeTopBar() },
|
||||
body = { HomePage() }
|
||||
),
|
||||
Patches(
|
||||
iconSelected = Icons.Filled.Healing,
|
||||
iconNotSelected = Icons.Outlined.Healing,
|
||||
topBar = { PatchesTopBar() },
|
||||
fab = { PatchesFab() },
|
||||
Manage(
|
||||
iconSelected = Icons.Filled.Dashboard,
|
||||
iconNotSelected = Icons.Outlined.Dashboard,
|
||||
topBar = { ManageTopBar() },
|
||||
fab = { ManageFab() },
|
||||
body = {}
|
||||
),
|
||||
Repo(
|
||||
|
|
@ -60,17 +54,18 @@ enum class PageList(
|
|||
arguments = listOf(
|
||||
navArgument("multiSelect") { type = NavType.BoolType }
|
||||
),
|
||||
topBar = {},
|
||||
topBar = { SelectAppsTopBar() },
|
||||
fab = { SelectAppsFab() },
|
||||
body = { SelectAppsPage(this) }
|
||||
);
|
||||
|
||||
val title: String
|
||||
@Composable get() = when (this) {
|
||||
Home -> stringResource(R.string.app_name)
|
||||
Patches -> stringResource(R.string.page_patches)
|
||||
Manage -> stringResource(R.string.page_manage)
|
||||
Repo -> stringResource(R.string.page_repo)
|
||||
Settings -> stringResource(R.string.page_settings)
|
||||
NewPatch -> stringResource(R.string.page_new_patch)
|
||||
SelectApps -> stringResource(R.string.page_select_app)
|
||||
SelectApps -> stringResource(R.string.page_select_apps)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,60 +1,90 @@
|
|||
package org.lsposed.lspatch.ui.page
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Done
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.SmallTopAppBar
|
||||
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.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import org.lsposed.lspatch.R
|
||||
import org.lsposed.lspatch.ui.component.AppItem
|
||||
import org.lsposed.lspatch.ui.util.LocalNavController
|
||||
import org.lsposed.lspatch.ui.util.observeState
|
||||
import org.lsposed.lspatch.ui.util.setState
|
||||
import org.lsposed.lspatch.ui.viewmodel.AppInfo
|
||||
import org.lsposed.lspatch.ui.viewmodel.SelectAppViewModel
|
||||
import org.lsposed.lspatch.ui.viewmodel.SelectAppsViewModel
|
||||
|
||||
@Composable
|
||||
fun SelectAppsTopBar() {
|
||||
SmallTopAppBar(
|
||||
title = { Text(stringResource(R.string.page_select_apps)) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SelectAppsFab() {
|
||||
val navController = LocalNavController.current
|
||||
val viewModel = viewModel<SelectAppsViewModel>()
|
||||
val multiSelect = navController.currentBackStackEntry?.arguments?.get("multiSelect") as? Boolean
|
||||
?: 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) {
|
||||
TODO("MultiSelect")
|
||||
MultiSelect(filter = { it.app.metaData?.get("xposedminversion") != null })
|
||||
} else {
|
||||
SelectSingle()
|
||||
SingleSelect()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SelectSingle(
|
||||
filter: (AppInfo) -> Boolean = { true }
|
||||
private fun SingleSelect(
|
||||
filter: (AppInfo) -> Boolean = { it.app.flags and ApplicationInfo.FLAG_SYSTEM == 0 }
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val viewModel = viewModel<SelectAppViewModel>()
|
||||
val isRefreshing by viewModel.isRefreshing.collectAsState()
|
||||
if (SelectAppViewModel.appList.isEmpty())
|
||||
val viewModel = viewModel<SelectAppsViewModel>()
|
||||
LaunchedEffect(viewModel) {
|
||||
viewModel.loadAppList(context)
|
||||
viewModel.filterAppList(context, false, filter)
|
||||
}
|
||||
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefreshing),
|
||||
onRefresh = { viewModel.loadAppList(context) },
|
||||
state = rememberSwipeRefreshState(viewModel.isRefreshing),
|
||||
onRefresh = { viewModel.filterAppList(context, true, filter) },
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn {
|
||||
items(SelectAppViewModel.appList) {
|
||||
items(viewModel.filteredList) {
|
||||
AppItem(
|
||||
icon = it.icon,
|
||||
icon = viewModel.getIcon(it),
|
||||
label = it.label,
|
||||
packageName = it.app.packageName,
|
||||
onClick = {
|
||||
navController.previousBackStackEntry!!.savedStateHandle.getLiveData<AppInfo>("appInfo").value = it
|
||||
navController.previousBackStackEntry!!.setState("appInfo", it)
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
|
|
@ -62,3 +92,43 @@ private fun SelectSingle(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MultiSelect(
|
||||
filter: (AppInfo) -> Boolean = { true }
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val viewModel = viewModel<SelectAppsViewModel>()
|
||||
val selected by navController.previousBackStackEntry!!.observeState<SnapshotStateList<AppInfo>>("selected")
|
||||
|
||||
LaunchedEffect(viewModel) {
|
||||
viewModel.filterAppList(context, false, filter)
|
||||
}
|
||||
if (viewModel.done) {
|
||||
LaunchedEffect(viewModel) {
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(viewModel.isRefreshing),
|
||||
onRefresh = { viewModel.filterAppList(context, true, filter) },
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
LazyColumn {
|
||||
items(viewModel.filteredList) {
|
||||
val checked = selected!!.contains(it)
|
||||
AppItem(
|
||||
icon = viewModel.getIcon(it),
|
||||
label = it.label,
|
||||
packageName = it.app.packageName,
|
||||
onClick = {
|
||||
if (checked) selected!!.remove(it) else selected!!.add(it)
|
||||
},
|
||||
checked = checked
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package org.lsposed.lspatch.ui.util
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavHostController
|
||||
|
|
@ -17,6 +19,13 @@ val NavController.currentRoute: String?
|
|||
val NavController.startRoute: String?
|
||||
get() = graph.findStartDestination().route
|
||||
|
||||
fun <T> NavBackStackEntry.setState(key: String, value: T?) {
|
||||
savedStateHandle.getLiveData<T>(key).value = value
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> NavBackStackEntry.observeState(key: String, initial: T? = null) = savedStateHandle.getLiveData(key, initial).observeAsState()
|
||||
|
||||
@Composable
|
||||
fun NavController.isAtStartRoute(): Boolean = currentRoute == startRoute
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Parcelable
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -11,46 +12,54 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.lsposed.lspatch.TAG
|
||||
|
||||
class AppInfo(val app: ApplicationInfo, val icon: Drawable, val label: String)
|
||||
@Parcelize
|
||||
class AppInfo(val app: ApplicationInfo, val label: String) : Parcelable
|
||||
|
||||
class SelectAppViewModel : ViewModel() {
|
||||
private var appList = listOf<AppInfo>()
|
||||
private val appIcon = mutableMapOf<String, Drawable>()
|
||||
|
||||
class SelectAppsViewModel : ViewModel() {
|
||||
|
||||
init {
|
||||
Log.d(TAG, "SelectAppViewModel ${toString().substringAfterLast('@')} construct")
|
||||
Log.d(TAG, "SelectAppsViewModel ${toString().substringAfterLast('@')} construct")
|
||||
}
|
||||
|
||||
companion object {
|
||||
var appList by mutableStateOf(listOf<AppInfo>())
|
||||
var done by mutableStateOf(false)
|
||||
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
private set
|
||||
}
|
||||
|
||||
private val _isRefreshing = MutableStateFlow(false)
|
||||
val isRefreshing: StateFlow<Boolean>
|
||||
get() = _isRefreshing.asStateFlow()
|
||||
var filteredList by mutableStateOf(listOf<AppInfo>())
|
||||
private set
|
||||
|
||||
fun loadAppList(context: Context) {
|
||||
viewModelScope.launch {
|
||||
private suspend fun refreshAppList(context: Context) {
|
||||
Log.d(TAG, "Start refresh apps")
|
||||
_isRefreshing.emit(true)
|
||||
isRefreshing = true
|
||||
val pm = context.packageManager
|
||||
val collection = mutableListOf<AppInfo>()
|
||||
withContext(Dispatchers.IO) {
|
||||
pm.getInstalledApplications(PackageManager.GET_META_DATA).forEach {
|
||||
val icon = pm.getApplicationIcon(it)
|
||||
val label = pm.getApplicationLabel(it)
|
||||
collection.add(AppInfo(it, icon, label.toString()))
|
||||
appIcon[it.packageName] = pm.getApplicationIcon(it)
|
||||
collection.add(AppInfo(it, label.toString()))
|
||||
}
|
||||
}
|
||||
appList = collection
|
||||
_isRefreshing.emit(false)
|
||||
isRefreshing = false
|
||||
Log.d(TAG, "Refreshed ${appList.size} apps")
|
||||
}
|
||||
|
||||
fun filterAppList(context: Context, refresh: Boolean, filter: (AppInfo) -> Boolean) {
|
||||
viewModelScope.launch {
|
||||
if (appList.isEmpty() || refresh) refreshAppList(context)
|
||||
filteredList = appList.filter(filter)
|
||||
}
|
||||
}
|
||||
|
||||
fun getIcon(appInfo: AppInfo) = appIcon[appInfo.app.packageName]!!
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
<resources>
|
||||
<string name="app_name">LSPatch</string>
|
||||
<string name="add">Add</string>
|
||||
|
||||
<string name="page_repo">Repo</string>
|
||||
<string name="page_settings">Settings</string>
|
||||
<string name="page_select_app">Select App</string>
|
||||
|
||||
<!-- Patches Page -->
|
||||
<string name="page_patches">Patches</string>
|
||||
<string name="patches_add">Add</string>
|
||||
<!-- Manage Page -->
|
||||
<string name="page_manage">Manage</string>
|
||||
|
||||
<!-- New Patch Page -->
|
||||
<string name="page_new_patch">New Patch</string>
|
||||
|
|
@ -28,4 +27,7 @@
|
|||
<string name="patch_start">Start Patch</string>
|
||||
<string name="patch_return">Return</string>
|
||||
<string name="patch_install">Install</string>
|
||||
|
||||
<!-- Select Apps Page -->
|
||||
<string name="page_select_apps">Select Apps</string>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Reference in New Issue