Anywhere dropdown menu
This commit is contained in:
parent
8498da904c
commit
75e004e4c7
|
|
@ -0,0 +1,64 @@
|
||||||
|
package org.lsposed.lspatch.ui.component
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.LocalIndication
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.PressInteraction
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.unit.DpOffset
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun AnywhereDropdown(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
expanded: Boolean,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onLongClick: (() -> Unit)? = null,
|
||||||
|
surface: @Composable () -> Unit,
|
||||||
|
content: @Composable ColumnScope.() -> Unit
|
||||||
|
) {
|
||||||
|
val indication = LocalIndication.current
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
val state by interactionSource.interactions.collectAsState(null)
|
||||||
|
var offset by remember { mutableStateOf(Offset.Zero) }
|
||||||
|
val dpOffset = with(LocalDensity.current) {
|
||||||
|
DpOffset(offset.x.toDp(), offset.y.toDp())
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(state) {
|
||||||
|
if (state is PressInteraction.Release) {
|
||||||
|
val i = state as PressInteraction.Release
|
||||||
|
offset = i.press.pressPosition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.combinedClickable(
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = indication,
|
||||||
|
enabled = enabled,
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onLongClick
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
surface()
|
||||||
|
Box {
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
offset = dpOffset,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,8 +2,6 @@ package org.lsposed.lspatch.ui.component
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.combinedClickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowForwardIos
|
import androidx.compose.material.icons.filled.ArrowForwardIos
|
||||||
|
|
@ -19,15 +17,13 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||||
import org.lsposed.lspatch.ui.theme.LSPTheme
|
import org.lsposed.lspatch.ui.theme.LSPTheme
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppItem(
|
fun AppItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
icon: Drawable,
|
icon: Drawable,
|
||||||
label: String,
|
label: String,
|
||||||
packageName: String,
|
packageName: String,
|
||||||
onClick: () -> Unit,
|
|
||||||
onLongClick: (() -> Unit)? = null,
|
|
||||||
checked: Boolean? = null,
|
checked: Boolean? = null,
|
||||||
rightIcon: (@Composable () -> Unit)? = null,
|
rightIcon: (@Composable () -> Unit)? = null,
|
||||||
additionalContent: (@Composable ColumnScope.() -> Unit)? = null,
|
additionalContent: (@Composable ColumnScope.() -> Unit)? = null,
|
||||||
|
|
@ -37,10 +33,6 @@ fun AppItem(
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.combinedClickable(
|
|
||||||
onClick = onClick,
|
|
||||||
onLongClick = onLongClick
|
|
||||||
)
|
|
||||||
.padding(20.dp)
|
.padding(20.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -68,7 +60,7 @@ fun AppItem(
|
||||||
if (checked != null) {
|
if (checked != null) {
|
||||||
Checkbox(
|
Checkbox(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
onCheckedChange = { onClick() },
|
onCheckedChange = null,
|
||||||
modifier = Modifier.padding(start = 20.dp)
|
modifier = Modifier.padding(start = 20.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +82,6 @@ private fun AppItemPreview() {
|
||||||
icon = shape,
|
icon = shape,
|
||||||
label = "Sample App",
|
label = "Sample App",
|
||||||
packageName = "org.lsposed.sample",
|
packageName = "org.lsposed.sample",
|
||||||
onClick = {},
|
|
||||||
rightIcon = { Icon(Icons.Filled.ArrowForwardIos, null) }
|
rightIcon = { Icon(Icons.Filled.ArrowForwardIos, null) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package org.lsposed.lspatch.ui.component.settings
|
package org.lsposed.lspatch.ui.component.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
|
@ -17,14 +18,13 @@ fun SettingsCheckBox(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
onClick: () -> Unit,
|
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
title: String,
|
title: String,
|
||||||
desc: String? = null,
|
desc: String? = null,
|
||||||
extraContent: (@Composable ColumnScope.() -> Unit)? = null
|
extraContent: (@Composable ColumnScope.() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
SettingsSlot(modifier, enabled, onClick, icon, title, desc, extraContent) {
|
SettingsSlot(modifier, enabled, icon, title, desc, extraContent) {
|
||||||
Checkbox(checked = checked, onCheckedChange = { onClick() })
|
Checkbox(checked = checked, onCheckedChange = null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,14 +35,14 @@ private fun SettingsCheckBoxPreview() {
|
||||||
var checked2 by remember { mutableStateOf(false) }
|
var checked2 by remember { mutableStateOf(false) }
|
||||||
Column {
|
Column {
|
||||||
SettingsCheckBox(
|
SettingsCheckBox(
|
||||||
|
modifier = Modifier.clickable { checked1 = !checked1 },
|
||||||
checked = checked1,
|
checked = checked1,
|
||||||
onClick = { checked1 = !checked1 },
|
|
||||||
title = "Title",
|
title = "Title",
|
||||||
desc = "Description"
|
desc = "Description"
|
||||||
)
|
)
|
||||||
SettingsCheckBox(
|
SettingsCheckBox(
|
||||||
|
modifier = Modifier.clickable { checked2 = !checked2 },
|
||||||
checked = checked2,
|
checked = checked2,
|
||||||
onClick = { checked2 = !checked2 },
|
|
||||||
icon = Icons.Outlined.Api,
|
icon = Icons.Outlined.Api,
|
||||||
title = "Title",
|
title = "Title",
|
||||||
desc = "Description"
|
desc = "Description"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package org.lsposed.lspatch.ui.component.settings
|
package org.lsposed.lspatch.ui.component.settings
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
|
@ -16,22 +15,16 @@ import androidx.compose.ui.unit.dp
|
||||||
fun SettingsSlot(
|
fun SettingsSlot(
|
||||||
modifier: Modifier,
|
modifier: Modifier,
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
onClick: () -> Unit,
|
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
title: String,
|
title: String,
|
||||||
desc: String?,
|
desc: String?,
|
||||||
extraContent: (@Composable ColumnScope.() -> Unit)? = null,
|
extraContent: (@Composable ColumnScope.() -> Unit)? = null,
|
||||||
action: (@Composable RowScope.() -> Unit)?,
|
action: (@Composable RowScope.() -> Unit)?,
|
||||||
) {
|
) {
|
||||||
val enabledModifier =
|
|
||||||
if (enabled) Modifier
|
|
||||||
.alpha(1f)
|
|
||||||
.clickable(onClick = onClick)
|
|
||||||
else Modifier.alpha(0.5f)
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.then(enabledModifier)
|
.alpha(if (enabled) 1f else 0.5f)
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
|
@ -71,9 +64,8 @@ fun SettingsSlot(
|
||||||
fun SettingsItem(
|
fun SettingsItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
onClick: () -> Unit,
|
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
title: String,
|
title: String,
|
||||||
desc: String? = null,
|
desc: String? = null,
|
||||||
extraContent: (@Composable ColumnScope.() -> Unit)? = null
|
extraContent: (@Composable ColumnScope.() -> Unit)? = null
|
||||||
) = SettingsSlot(modifier, enabled, onClick, icon, title, desc, extraContent, null)
|
) = SettingsSlot(modifier, enabled, icon, title, desc, extraContent, null)
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,13 @@ fun SettingsSwitch(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
onClick: () -> Unit,
|
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
title: String,
|
title: String,
|
||||||
desc: String? = null,
|
desc: String? = null,
|
||||||
extraContent: (@Composable ColumnScope.() -> Unit)? = null
|
extraContent: (@Composable ColumnScope.() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
SettingsSlot(modifier.clickable(onClick = onClick), enabled, onClick, icon, title, desc, extraContent) {
|
SettingsSlot(modifier, enabled, icon, title, desc, extraContent) {
|
||||||
Switch(checked = checked, onCheckedChange = { onClick() })
|
Switch(checked = checked, onCheckedChange = null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,14 +33,14 @@ private fun SettingsCheckBoxPreview() {
|
||||||
var checked2 by remember { mutableStateOf(false) }
|
var checked2 by remember { mutableStateOf(false) }
|
||||||
Column {
|
Column {
|
||||||
SettingsSwitch(
|
SettingsSwitch(
|
||||||
|
modifier = Modifier.clickable { checked1 = !checked1 },
|
||||||
checked = checked1,
|
checked = checked1,
|
||||||
onClick = { checked1 = !checked1 },
|
|
||||||
title = "Title",
|
title = "Title",
|
||||||
desc = "Description"
|
desc = "Description"
|
||||||
)
|
)
|
||||||
SettingsSwitch(
|
SettingsSwitch(
|
||||||
|
modifier = Modifier.clickable { checked2 = !checked2 },
|
||||||
checked = checked2,
|
checked = checked2,
|
||||||
onClick = { checked2 = !checked2 },
|
|
||||||
icon = Icons.Outlined.Api,
|
icon = Icons.Outlined.Api,
|
||||||
title = "Title",
|
title = "Title",
|
||||||
desc = "Description"
|
desc = "Description"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.Spring
|
import androidx.compose.animation.core.Spring
|
||||||
import androidx.compose.animation.core.spring
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
|
@ -39,6 +40,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.lsposed.lspatch.R
|
import org.lsposed.lspatch.R
|
||||||
import org.lsposed.lspatch.lspApp
|
import org.lsposed.lspatch.lspApp
|
||||||
|
import org.lsposed.lspatch.ui.component.AnywhereDropdown
|
||||||
import org.lsposed.lspatch.ui.component.SelectionColumn
|
import org.lsposed.lspatch.ui.component.SelectionColumn
|
||||||
import org.lsposed.lspatch.ui.component.ShimmerAnimation
|
import org.lsposed.lspatch.ui.component.ShimmerAnimation
|
||||||
import org.lsposed.lspatch.ui.component.settings.SettingsCheckBox
|
import org.lsposed.lspatch.ui.component.settings.SettingsCheckBox
|
||||||
|
|
@ -219,28 +221,36 @@ private fun PatchOptionsBody(modifier: Modifier, onAddEmbed: () -> Unit) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SettingsCheckBox(
|
SettingsCheckBox(
|
||||||
modifier = Modifier.padding(top = 6.dp),
|
modifier = Modifier
|
||||||
|
.padding(top = 6.dp)
|
||||||
|
.clickable { viewModel.debuggable = !viewModel.debuggable },
|
||||||
checked = viewModel.debuggable,
|
checked = viewModel.debuggable,
|
||||||
onClick = { viewModel.debuggable = !viewModel.debuggable },
|
|
||||||
icon = Icons.Outlined.BugReport,
|
icon = Icons.Outlined.BugReport,
|
||||||
title = stringResource(R.string.patch_debuggable)
|
title = stringResource(R.string.patch_debuggable)
|
||||||
)
|
)
|
||||||
SettingsCheckBox(
|
SettingsCheckBox(
|
||||||
|
modifier = Modifier.clickable { viewModel.overrideVersionCode = !viewModel.overrideVersionCode },
|
||||||
checked = viewModel.overrideVersionCode,
|
checked = viewModel.overrideVersionCode,
|
||||||
onClick = { viewModel.overrideVersionCode = !viewModel.overrideVersionCode },
|
|
||||||
icon = Icons.Outlined.Layers,
|
icon = Icons.Outlined.Layers,
|
||||||
title = stringResource(R.string.patch_override_version_code),
|
title = stringResource(R.string.patch_override_version_code),
|
||||||
desc = stringResource(R.string.patch_override_version_code_desc)
|
desc = stringResource(R.string.patch_override_version_code_desc)
|
||||||
)
|
)
|
||||||
Box {
|
var signExpanded by remember { mutableStateOf(false) }
|
||||||
var expanded by remember { mutableStateOf(false) }
|
AnywhereDropdown(
|
||||||
|
expanded = signExpanded,
|
||||||
|
onDismissRequest = { signExpanded = false },
|
||||||
|
onClick = { signExpanded = true },
|
||||||
|
surface = {
|
||||||
SettingsItem(
|
SettingsItem(
|
||||||
onClick = { expanded = true },
|
|
||||||
icon = Icons.Outlined.Edit,
|
icon = Icons.Outlined.Edit,
|
||||||
title = stringResource(R.string.patch_sign),
|
title = stringResource(R.string.patch_sign),
|
||||||
desc = viewModel.sign.mapIndexedNotNull { index, on -> if (on) "V" + (index + 1) else null }.joinToString(" + ").ifEmpty { "None" }
|
desc = viewModel.sign
|
||||||
|
.mapIndexedNotNull { index, on -> if (on) "V" + (index + 1) else null }
|
||||||
|
.joinToString(" + ")
|
||||||
|
.ifEmpty { "None" }
|
||||||
)
|
)
|
||||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
}
|
||||||
|
) {
|
||||||
repeat(2) { index ->
|
repeat(2) { index ->
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
|
|
@ -253,16 +263,19 @@ private fun PatchOptionsBody(modifier: Modifier, onAddEmbed: () -> Unit) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
var bypassExpanded by remember { mutableStateOf(false) }
|
||||||
Box {
|
AnywhereDropdown(
|
||||||
var expanded by remember { mutableStateOf(false) }
|
expanded = bypassExpanded,
|
||||||
|
onDismissRequest = { bypassExpanded = false },
|
||||||
|
onClick = { bypassExpanded = true },
|
||||||
|
surface = {
|
||||||
SettingsItem(
|
SettingsItem(
|
||||||
onClick = { expanded = true },
|
|
||||||
icon = Icons.Outlined.RemoveModerator,
|
icon = Icons.Outlined.RemoveModerator,
|
||||||
title = stringResource(R.string.patch_sigbypass),
|
title = stringResource(R.string.patch_sigbypass),
|
||||||
desc = sigBypassLvStr(viewModel.sigBypassLevel)
|
desc = sigBypassLvStr(viewModel.sigBypassLevel)
|
||||||
)
|
)
|
||||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
}
|
||||||
|
) {
|
||||||
repeat(3) {
|
repeat(3) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
|
|
@ -273,14 +286,13 @@ private fun PatchOptionsBody(modifier: Modifier, onAddEmbed: () -> Unit) {
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.sigBypassLevel = it
|
viewModel.sigBypassLevel = it
|
||||||
expanded = false
|
bypassExpanded = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DoPatchBody(modifier: Modifier, navigator: DestinationsNavigator) {
|
private fun DoPatchBody(modifier: Modifier, navigator: DestinationsNavigator) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.core.Spring
|
import androidx.compose.animation.core.Spring
|
||||||
import androidx.compose.animation.core.spring
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
|
@ -124,11 +125,12 @@ private fun SingleSelect(onSelect: (AppInfo) -> Unit) {
|
||||||
key = { it.app.packageName }
|
key = { it.app.packageName }
|
||||||
) {
|
) {
|
||||||
AppItem(
|
AppItem(
|
||||||
modifier = Modifier.animateItemPlacement(spring(stiffness = Spring.StiffnessLow)),
|
modifier = Modifier
|
||||||
|
.animateItemPlacement(spring(stiffness = Spring.StiffnessLow))
|
||||||
|
.clickable { onSelect(it) },
|
||||||
icon = LSPPackageManager.getIcon(it),
|
icon = LSPPackageManager.getIcon(it),
|
||||||
label = it.label,
|
label = it.label,
|
||||||
packageName = it.app.packageName,
|
packageName = it.app.packageName
|
||||||
onClick = { onSelect(it) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -145,14 +147,15 @@ private fun MultiSelect() {
|
||||||
) {
|
) {
|
||||||
val checked = viewModel.multiSelected.contains(it)
|
val checked = viewModel.multiSelected.contains(it)
|
||||||
AppItem(
|
AppItem(
|
||||||
modifier = Modifier.animateItemPlacement(spring(stiffness = Spring.StiffnessLow)),
|
modifier = Modifier
|
||||||
icon = LSPPackageManager.getIcon(it),
|
.animateItemPlacement(spring(stiffness = Spring.StiffnessLow))
|
||||||
label = it.label,
|
.clickable {
|
||||||
packageName = it.app.packageName,
|
|
||||||
onClick = {
|
|
||||||
if (checked) viewModel.multiSelected.remove(it)
|
if (checked) viewModel.multiSelected.remove(it)
|
||||||
else viewModel.multiSelected.add(it)
|
else viewModel.multiSelected.add(it)
|
||||||
},
|
},
|
||||||
|
icon = LSPPackageManager.getIcon(it),
|
||||||
|
label = it.label,
|
||||||
|
packageName = it.app.packageName,
|
||||||
checked = checked
|
checked = checked
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ package org.lsposed.lspatch.ui.page
|
||||||
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.PressInteraction
|
import androidx.compose.foundation.interaction.PressInteraction
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
|
@ -27,6 +27,7 @@ import kotlinx.coroutines.launch
|
||||||
import org.lsposed.lspatch.R
|
import org.lsposed.lspatch.R
|
||||||
import org.lsposed.lspatch.config.Configs
|
import org.lsposed.lspatch.config.Configs
|
||||||
import org.lsposed.lspatch.config.MyKeyStore
|
import org.lsposed.lspatch.config.MyKeyStore
|
||||||
|
import org.lsposed.lspatch.ui.component.AnywhereDropdown
|
||||||
import org.lsposed.lspatch.ui.component.CenterTopBar
|
import org.lsposed.lspatch.ui.component.CenterTopBar
|
||||||
import org.lsposed.lspatch.ui.component.settings.SettingsItem
|
import org.lsposed.lspatch.ui.component.settings.SettingsItem
|
||||||
import org.lsposed.lspatch.ui.component.settings.SettingsSwitch
|
import org.lsposed.lspatch.ui.component.settings.SettingsSwitch
|
||||||
|
|
@ -56,33 +57,36 @@ fun SettingsScreen() {
|
||||||
private fun KeyStore() {
|
private fun KeyStore() {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
var dropdownExpanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Box {
|
AnywhereDropdown(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false },
|
||||||
|
onClick = { expanded = true },
|
||||||
|
surface = {
|
||||||
SettingsItem(
|
SettingsItem(
|
||||||
onClick = { dropdownExpanded = !dropdownExpanded },
|
|
||||||
icon = Icons.Outlined.Ballot,
|
icon = Icons.Outlined.Ballot,
|
||||||
title = stringResource(R.string.settings_keystore),
|
title = stringResource(R.string.settings_keystore),
|
||||||
desc = stringResource(if (MyKeyStore.useDefault) R.string.settings_keystore_default else R.string.settings_keystore_custom)
|
desc = stringResource(if (MyKeyStore.useDefault) R.string.settings_keystore_default else R.string.settings_keystore_custom)
|
||||||
)
|
)
|
||||||
DropdownMenu(expanded = dropdownExpanded, onDismissRequest = { dropdownExpanded = false }) {
|
}
|
||||||
|
) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.settings_keystore_default)) },
|
text = { Text(stringResource(R.string.settings_keystore_default)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch { MyKeyStore.reset() }
|
scope.launch { MyKeyStore.reset() }
|
||||||
dropdownExpanded = false
|
expanded = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.settings_keystore_custom)) },
|
text = { Text(stringResource(R.string.settings_keystore_custom)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
dropdownExpanded = false
|
expanded = false
|
||||||
showDialog = true
|
showDialog = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (showDialog) {
|
if (showDialog) {
|
||||||
var wrongKeystore by rememberSaveable { mutableStateOf(false) }
|
var wrongKeystore by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
@ -106,7 +110,7 @@ private fun KeyStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = { dropdownExpanded = false; showDialog = false },
|
onDismissRequest = { expanded = false; showDialog = false },
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
content = { Text(stringResource(android.R.string.ok)) },
|
content = { Text(stringResource(android.R.string.ok)) },
|
||||||
|
|
@ -144,14 +148,14 @@ private fun KeyStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.launch { MyKeyStore.setCustom(password, alias, aliasPassword) }
|
scope.launch { MyKeyStore.setCustom(password, alias, aliasPassword) }
|
||||||
dropdownExpanded = false
|
expanded = false
|
||||||
showDialog = false
|
showDialog = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
content = { Text(stringResource(android.R.string.cancel)) },
|
content = { Text(stringResource(android.R.string.cancel)) },
|
||||||
onClick = { dropdownExpanded = false; showDialog = false }
|
onClick = { expanded = false; showDialog = false }
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
|
|
@ -228,8 +232,8 @@ private fun KeyStore() {
|
||||||
@Composable
|
@Composable
|
||||||
private fun DetailPatchLogs() {
|
private fun DetailPatchLogs() {
|
||||||
SettingsSwitch(
|
SettingsSwitch(
|
||||||
|
modifier = Modifier.clickable { Configs.detailPatchLogs = !Configs.detailPatchLogs },
|
||||||
checked = Configs.detailPatchLogs,
|
checked = Configs.detailPatchLogs,
|
||||||
onClick = { Configs.detailPatchLogs = !Configs.detailPatchLogs },
|
|
||||||
title = stringResource(R.string.settings_detail_patch_logs)
|
title = stringResource(R.string.settings_detail_patch_logs)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import org.lsposed.lspatch.config.Configs
|
||||||
import org.lsposed.lspatch.database.entity.Module
|
import org.lsposed.lspatch.database.entity.Module
|
||||||
import org.lsposed.lspatch.lspApp
|
import org.lsposed.lspatch.lspApp
|
||||||
import org.lsposed.lspatch.share.LSPConfig
|
import org.lsposed.lspatch.share.LSPConfig
|
||||||
|
import org.lsposed.lspatch.ui.component.AnywhereDropdown
|
||||||
import org.lsposed.lspatch.ui.component.AppItem
|
import org.lsposed.lspatch.ui.component.AppItem
|
||||||
import org.lsposed.lspatch.ui.component.LoadingDialog
|
import org.lsposed.lspatch.ui.component.LoadingDialog
|
||||||
import org.lsposed.lspatch.ui.page.SelectAppsResult
|
import org.lsposed.lspatch.ui.page.SelectAppsResult
|
||||||
|
|
@ -49,6 +50,7 @@ import org.lsposed.lspatch.ui.page.destinations.NewPatchScreenDestination
|
||||||
import org.lsposed.lspatch.ui.page.destinations.SelectAppsScreenDestination
|
import org.lsposed.lspatch.ui.page.destinations.SelectAppsScreenDestination
|
||||||
import org.lsposed.lspatch.ui.util.LocalSnackbarHost
|
import org.lsposed.lspatch.ui.util.LocalSnackbarHost
|
||||||
import org.lsposed.lspatch.ui.viewmodel.manage.AppManageViewModel
|
import org.lsposed.lspatch.ui.viewmodel.manage.AppManageViewModel
|
||||||
|
import org.lsposed.lspatch.ui.viewstate.ProcessingState
|
||||||
import org.lsposed.lspatch.util.LSPPackageManager
|
import org.lsposed.lspatch.util.LSPPackageManager
|
||||||
import org.lsposed.lspatch.util.ShizukuApi
|
import org.lsposed.lspatch.util.ShizukuApi
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
@ -93,13 +95,16 @@ fun AppManageBody(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewModel.processingUpdate) LoadingDialog()
|
when (viewModel.updateLoaderState) {
|
||||||
viewModel.updateLoaderResult?.let {
|
is ProcessingState.Idle -> Unit
|
||||||
|
is ProcessingState.Processing -> LoadingDialog()
|
||||||
|
is ProcessingState.Done -> {
|
||||||
|
val it = viewModel.updateLoaderState as ProcessingState.Done
|
||||||
val updateSuccessfully = stringResource(R.string.manage_update_loader_successfully)
|
val updateSuccessfully = stringResource(R.string.manage_update_loader_successfully)
|
||||||
val updateFailed = stringResource(R.string.manage_update_loader_failed)
|
val updateFailed = stringResource(R.string.manage_update_loader_failed)
|
||||||
val copyError = stringResource(R.string.copy_error)
|
val copyError = stringResource(R.string.copy_error)
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
it.onSuccess {
|
it.result.onSuccess {
|
||||||
snackbarHost.showSnackbar(updateSuccessfully)
|
snackbarHost.showSnackbar(updateSuccessfully)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
val result = snackbarHost.showSnackbar(updateFailed, copyError)
|
val result = snackbarHost.showSnackbar(updateFailed, copyError)
|
||||||
|
|
@ -111,6 +116,20 @@ fun AppManageBody(
|
||||||
viewModel.dispatch(AppManageViewModel.ViewAction.ClearUpdateLoaderResult)
|
viewModel.dispatch(AppManageViewModel.ViewAction.ClearUpdateLoaderResult)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
when (viewModel.optimizeState) {
|
||||||
|
is ProcessingState.Idle -> Unit
|
||||||
|
is ProcessingState.Processing -> LoadingDialog()
|
||||||
|
is ProcessingState.Done -> {
|
||||||
|
val it = viewModel.optimizeState as ProcessingState.Done
|
||||||
|
val optimizeSucceed = stringResource(R.string.manage_optimize_successfully)
|
||||||
|
val optimizeFailed = stringResource(R.string.manage_optimize_failed)
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
snackbarHost.showSnackbar(if (it.result) optimizeSucceed else optimizeFailed)
|
||||||
|
viewModel.dispatch(AppManageViewModel.ViewAction.ClearOptimizeResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(Modifier.fillMaxHeight()) {
|
LazyColumn(Modifier.fillMaxHeight()) {
|
||||||
items(
|
items(
|
||||||
|
|
@ -118,13 +137,15 @@ fun AppManageBody(
|
||||||
key = { it.first.app.packageName }
|
key = { it.first.app.packageName }
|
||||||
) {
|
) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
Box {
|
AnywhereDropdown(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false },
|
||||||
|
onClick = { expanded = true },
|
||||||
|
surface = {
|
||||||
AppItem(
|
AppItem(
|
||||||
icon = LSPPackageManager.getIcon(it.first),
|
icon = LSPPackageManager.getIcon(it.first),
|
||||||
label = it.first.label,
|
label = it.first.label,
|
||||||
packageName = it.first.app.packageName,
|
packageName = it.first.app.packageName,
|
||||||
onClick = { expanded = true },
|
|
||||||
onLongClick = { expanded = true },
|
|
||||||
additionalContent = {
|
additionalContent = {
|
||||||
Text(
|
Text(
|
||||||
text = buildAnnotatedString {
|
text = buildAnnotatedString {
|
||||||
|
|
@ -141,7 +162,8 @@ fun AppManageBody(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
}
|
||||||
|
) {
|
||||||
val shizukuUnavailable = stringResource(R.string.shizuku_unavailable)
|
val shizukuUnavailable = stringResource(R.string.shizuku_unavailable)
|
||||||
if (it.second.lspConfig.VERSION_CODE < LSPConfig.instance.VERSION_CODE || BuildConfig.DEBUG) {
|
if (it.second.lspConfig.VERSION_CODE < LSPConfig.instance.VERSION_CODE || BuildConfig.DEBUG) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
|
|
@ -174,8 +196,6 @@ fun AppManageBody(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val optimizeSucceed = stringResource(R.string.manage_optimize_successfully)
|
|
||||||
val optimizeFailed = stringResource(R.string.manage_optimize_failed)
|
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.manage_optimize)) },
|
text = { Text(stringResource(R.string.manage_optimize)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -184,8 +204,7 @@ fun AppManageBody(
|
||||||
if (!ShizukuApi.isPermissionGranted) {
|
if (!ShizukuApi.isPermissionGranted) {
|
||||||
snackbarHost.showSnackbar(shizukuUnavailable)
|
snackbarHost.showSnackbar(shizukuUnavailable)
|
||||||
} else {
|
} else {
|
||||||
val result = ShizukuApi.performDexOptMode(it.first.app.packageName)
|
viewModel.dispatch(AppManageViewModel.ViewAction.PerformOptimize(it.first))
|
||||||
snackbarHost.showSnackbar(if (result) optimizeSucceed else optimizeFailed)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +233,6 @@ fun AppManageBody(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppManageFab(navigator: DestinationsNavigator) {
|
fun AppManageFab(navigator: DestinationsNavigator) {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import org.lsposed.lspatch.R
|
import org.lsposed.lspatch.R
|
||||||
|
import org.lsposed.lspatch.ui.component.AnywhereDropdown
|
||||||
import org.lsposed.lspatch.ui.component.AppItem
|
import org.lsposed.lspatch.ui.component.AppItem
|
||||||
import org.lsposed.lspatch.ui.viewmodel.manage.ModuleManageViewModel
|
import org.lsposed.lspatch.ui.viewmodel.manage.ModuleManageViewModel
|
||||||
import org.lsposed.lspatch.util.LSPPackageManager
|
import org.lsposed.lspatch.util.LSPPackageManager
|
||||||
|
|
@ -44,13 +45,16 @@ fun ModuleManageBody() {
|
||||||
key = { it.first.app.packageName }
|
key = { it.first.app.packageName }
|
||||||
) {
|
) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
Box {
|
AnywhereDropdown(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false },
|
||||||
|
onClick = { /* TODO: Start module */ },
|
||||||
|
onLongClick = { expanded = true },
|
||||||
|
surface = {
|
||||||
AppItem(
|
AppItem(
|
||||||
icon = LSPPackageManager.getIcon(it.first),
|
icon = LSPPackageManager.getIcon(it.first),
|
||||||
label = it.first.label,
|
label = it.first.label,
|
||||||
packageName = it.first.app.packageName,
|
packageName = it.first.app.packageName,
|
||||||
onClick = { /* TODO: startAndSendModuleBinder */ },
|
|
||||||
onLongClick = { expanded = true },
|
|
||||||
additionalContent = {
|
additionalContent = {
|
||||||
Text(
|
Text(
|
||||||
text = it.second.description,
|
text = it.second.description,
|
||||||
|
|
@ -69,6 +73,9 @@ fun ModuleManageBody() {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
// TODO: Implement
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,15 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.lsposed.lspatch.Patcher
|
import org.lsposed.lspatch.Patcher
|
||||||
import org.lsposed.lspatch.lspApp
|
import org.lsposed.lspatch.lspApp
|
||||||
import org.lsposed.lspatch.share.Constants
|
import org.lsposed.lspatch.share.Constants
|
||||||
import org.lsposed.lspatch.share.PatchConfig
|
import org.lsposed.lspatch.share.PatchConfig
|
||||||
|
import org.lsposed.lspatch.ui.viewstate.ProcessingState
|
||||||
import org.lsposed.lspatch.util.LSPPackageManager
|
import org.lsposed.lspatch.util.LSPPackageManager
|
||||||
import org.lsposed.lspatch.util.LSPPackageManager.AppInfo
|
import org.lsposed.lspatch.util.LSPPackageManager.AppInfo
|
||||||
|
import org.lsposed.lspatch.util.ShizukuApi
|
||||||
import org.lsposed.patch.util.Logger
|
import org.lsposed.patch.util.Logger
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
@ -32,6 +33,8 @@ class AppManageViewModel : ViewModel() {
|
||||||
sealed class ViewAction {
|
sealed class ViewAction {
|
||||||
data class UpdateLoader(val appInfo: AppInfo, val config: PatchConfig) : ViewAction()
|
data class UpdateLoader(val appInfo: AppInfo, val config: PatchConfig) : ViewAction()
|
||||||
object ClearUpdateLoaderResult : ViewAction()
|
object ClearUpdateLoaderResult : ViewAction()
|
||||||
|
data class PerformOptimize(val appInfo: AppInfo) : ViewAction()
|
||||||
|
object ClearOptimizeResult : ViewAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
val appList: List<Pair<AppInfo, PatchConfig>> by derivedStateOf {
|
val appList: List<Pair<AppInfo, PatchConfig>> by derivedStateOf {
|
||||||
|
|
@ -46,9 +49,10 @@ class AppManageViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var processingUpdate by mutableStateOf(false)
|
var updateLoaderState: ProcessingState<Result<Unit>> by mutableStateOf(ProcessingState.Idle)
|
||||||
private set
|
private set
|
||||||
var updateLoaderResult: Result<Unit>? by mutableStateOf(null)
|
|
||||||
|
var optimizeState: ProcessingState<Boolean> by mutableStateOf(ProcessingState.Idle)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
private val logger = object : Logger() {
|
private val logger = object : Logger() {
|
||||||
|
|
@ -65,17 +69,20 @@ class AppManageViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dispatch(action: ViewAction) {
|
suspend fun dispatch(action: ViewAction) {
|
||||||
|
withContext(viewModelScope.coroutineContext) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is ViewAction.UpdateLoader -> updateLoader(action.appInfo, action.config)
|
is ViewAction.UpdateLoader -> updateLoader(action.appInfo, action.config)
|
||||||
is ViewAction.ClearUpdateLoaderResult -> updateLoaderResult = null
|
is ViewAction.ClearUpdateLoaderResult -> updateLoaderState = ProcessingState.Idle
|
||||||
|
is ViewAction.PerformOptimize -> performOptimize(action.appInfo)
|
||||||
|
is ViewAction.ClearOptimizeResult -> optimizeState = ProcessingState.Idle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateLoader(appInfo: AppInfo, config: PatchConfig) {
|
private suspend fun updateLoader(appInfo: AppInfo, config: PatchConfig) {
|
||||||
Log.i(TAG, "Update loader for ${appInfo.app.packageName}")
|
Log.i(TAG, "Update loader for ${appInfo.app.packageName}")
|
||||||
viewModelScope.launch {
|
updateLoaderState = ProcessingState.Processing
|
||||||
processingUpdate = true
|
|
||||||
val result = runCatching {
|
val result = runCatching {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
LSPPackageManager.cleanTmpApkDir()
|
LSPPackageManager.cleanTmpApkDir()
|
||||||
|
|
@ -101,8 +108,15 @@ class AppManageViewModel : ViewModel() {
|
||||||
if (status != PackageInstaller.STATUS_SUCCESS) throw RuntimeException(message)
|
if (status != PackageInstaller.STATUS_SUCCESS) throw RuntimeException(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processingUpdate = false
|
updateLoaderState = ProcessingState.Done(result)
|
||||||
updateLoaderResult = result
|
}
|
||||||
}
|
|
||||||
|
private suspend fun performOptimize(appInfo: AppInfo) {
|
||||||
|
Log.i(TAG, "Perform optimize for ${appInfo.app.packageName}")
|
||||||
|
optimizeState = ProcessingState.Processing
|
||||||
|
val result = withContext(Dispatchers.IO) {
|
||||||
|
ShizukuApi.performDexOptMode(appInfo.app.packageName)
|
||||||
|
}
|
||||||
|
optimizeState = ProcessingState.Done(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.lsposed.lspatch.ui.viewstate
|
||||||
|
|
||||||
|
sealed class ProcessingState<out T> {
|
||||||
|
object Idle : ProcessingState<Nothing>()
|
||||||
|
object Processing : ProcessingState<Nothing>()
|
||||||
|
data class Done<T>(val result: T) : ProcessingState<T>()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue