Type safe preferences & Change topbar style

This commit is contained in:
Nullptr 2022-07-07 01:23:30 +08:00
parent 220b6db35c
commit cb1ba36514
No known key found for this signature in database
GPG Key ID: 0B9D02052FF536BD
18 changed files with 157 additions and 115 deletions

View File

@ -3,9 +3,4 @@ package org.lsposed.lspatch
object Constants { object Constants {
const val PATCH_FILE_SUFFIX = "-lspatched.apk" const val PATCH_FILE_SUFFIX = "-lspatched.apk"
const val PREFS_KEYSTORE_PASSWORD = "keystore_password"
const val PREFS_KEYSTORE_ALIAS = "keystore_alias"
const val PREFS_KEYSTORE_ALIAS_PASSWORD = "keystore_alias_password"
const val PREFS_STORAGE_DIRECTORY = "storage_directory"
} }

View File

@ -5,7 +5,7 @@ import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.lsposed.lspatch.Constants.PATCH_FILE_SUFFIX import org.lsposed.lspatch.Constants.PATCH_FILE_SUFFIX
import org.lsposed.lspatch.Constants.PREFS_STORAGE_DIRECTORY import org.lsposed.lspatch.config.Configs
import org.lsposed.lspatch.config.MyKeyStore import org.lsposed.lspatch.config.MyKeyStore
import org.lsposed.lspatch.share.PatchConfig import org.lsposed.lspatch.share.PatchConfig
import org.lsposed.patch.LSPatch import org.lsposed.patch.LSPatch
@ -15,7 +15,6 @@ import java.io.IOException
object Patcher { object Patcher {
class Options( class Options(
private val verbose: Boolean,
private val config: PatchConfig, private val config: PatchConfig,
private val apkPaths: List<String>, private val apkPaths: List<String>,
private val embeddedModules: List<String>? private val embeddedModules: List<String>?
@ -30,12 +29,12 @@ object Patcher {
add("--v2"); add(config.v2.toString()) add("--v2"); add(config.v2.toString())
if (config.useManager) add("--manager") if (config.useManager) add("--manager")
if (config.overrideVersionCode) add("-r") if (config.overrideVersionCode) add("-r")
if (verbose) add("-v") if (Configs.detailPatchLogs) add("-v")
embeddedModules?.forEach { embeddedModules?.forEach {
add("-m"); add(it) add("-m"); add(it)
} }
if (!MyKeyStore.useDefault) { if (!MyKeyStore.useDefault) {
addAll(arrayOf("-k", MyKeyStore.file.path, MyKeyStore.password, MyKeyStore.alias, MyKeyStore.aliasPassword)) addAll(arrayOf("-k", MyKeyStore.file.path, Configs.keyStorePassword, Configs.keyStoreAlias, Configs.keyStoreAliasPassword))
} }
}.toTypedArray() }.toTypedArray()
} }
@ -45,7 +44,7 @@ object Patcher {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
LSPatch(logger, *options.toStringArray()).doCommandLine() LSPatch(logger, *options.toStringArray()).doCommandLine()
val uri = lspApp.prefs.getString(PREFS_STORAGE_DIRECTORY, null)?.toUri() val uri = Configs.storageDirectory?.toUri()
?: throw IOException("Uri is null") ?: throw IOException("Uri is null")
val root = DocumentFile.fromTreeUri(lspApp, uri) val root = DocumentFile.fromTreeUri(lspApp, uri)
?: throw IOException("DocumentFile is null") ?: throw IOException("DocumentFile is null")

View File

@ -0,0 +1,35 @@
package org.lsposed.lspatch.config
import org.lsposed.lspatch.lspApp
import org.lsposed.lspatch.ui.util.delegateStateOf
import org.lsposed.lspatch.ui.util.getValue
import org.lsposed.lspatch.ui.util.setValue
object Configs {
private const val PREFS_KEYSTORE_PASSWORD = "keystore_password"
private const val PREFS_KEYSTORE_ALIAS = "keystore_alias"
private const val PREFS_KEYSTORE_ALIAS_PASSWORD = "keystore_alias_password"
private const val PREFS_STORAGE_DIRECTORY = "storage_directory"
private const val PREFS_DETAIL_PATCH_LOGS = "detail_patch_logs"
var keyStorePassword by delegateStateOf(lspApp.prefs.getString(PREFS_KEYSTORE_PASSWORD, "123456")!!) {
lspApp.prefs.edit().putString(PREFS_KEYSTORE_PASSWORD, it).apply()
}
var keyStoreAlias by delegateStateOf(lspApp.prefs.getString(PREFS_KEYSTORE_ALIAS, "key0")!!) {
lspApp.prefs.edit().putString(PREFS_KEYSTORE_ALIAS, it).apply()
}
var keyStoreAliasPassword by delegateStateOf(lspApp.prefs.getString(PREFS_KEYSTORE_ALIAS_PASSWORD, "123456")!!) {
lspApp.prefs.edit().putString(PREFS_KEYSTORE_ALIAS_PASSWORD, it).apply()
}
var storageDirectory by delegateStateOf(lspApp.prefs.getString(PREFS_STORAGE_DIRECTORY, null)) {
lspApp.prefs.edit().putString(PREFS_STORAGE_DIRECTORY, it).apply()
}
var detailPatchLogs by delegateStateOf(lspApp.prefs.getBoolean(PREFS_DETAIL_PATCH_LOGS, true)) {
lspApp.prefs.edit().putBoolean(PREFS_DETAIL_PATCH_LOGS, it).apply()
}
}

View File

@ -1,56 +1,38 @@
package org.lsposed.lspatch.config package org.lsposed.lspatch.config
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.lsposed.lspatch.Constants.PREFS_KEYSTORE_ALIAS
import org.lsposed.lspatch.Constants.PREFS_KEYSTORE_ALIAS_PASSWORD
import org.lsposed.lspatch.Constants.PREFS_KEYSTORE_PASSWORD
import org.lsposed.lspatch.lspApp import org.lsposed.lspatch.lspApp
import java.io.File import java.io.File
object MyKeyStore { object MyKeyStore {
val file = File("${lspApp.filesDir}/keystore.bks") val file = File("${lspApp.filesDir}/keystore.bks")
val tmpFile = File("${lspApp.filesDir}/keystore.bks.tmp") val tmpFile = File("${lspApp.filesDir}/keystore.bks.tmp")
val password: String var useDefault by mutableStateOf(!file.exists())
get() = lspApp.prefs.getString("keystore_password", "123456")!! private set
val alias: String
get() = lspApp.prefs.getString("keystore_alias", "key0")!!
val aliasPassword: String
get() = lspApp.prefs.getString("keystore_alias_password", "123456")!!
private var mUseDefault by mutableStateOf(!file.exists())
val useDefault by derivedStateOf { mUseDefault }
suspend fun reset() { suspend fun reset() {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
file.delete() file.delete()
lspApp.prefs.edit() Configs.keyStorePassword = "123456"
.putString(PREFS_KEYSTORE_PASSWORD, "123456") Configs.keyStoreAlias = "key0"
.putString(PREFS_KEYSTORE_ALIAS, "key0") Configs.keyStoreAliasPassword = "123456"
.putString(PREFS_KEYSTORE_ALIAS_PASSWORD, "123456") useDefault = true
.apply()
mUseDefault = true
} }
} }
suspend fun setCustom(password: String, alias: String, aliasPassword: String) { suspend fun setCustom(password: String, alias: String, aliasPassword: String) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
tmpFile.renameTo(file) tmpFile.renameTo(file)
lspApp.prefs.edit() Configs.keyStorePassword = password
.putString(PREFS_KEYSTORE_PASSWORD, password) Configs.keyStoreAlias = alias
.putString(PREFS_KEYSTORE_ALIAS, alias) Configs.keyStoreAliasPassword = aliasPassword
.putString(PREFS_KEYSTORE_ALIAS_PASSWORD, aliasPassword) useDefault = false
.apply()
mUseDefault = false
} }
} }
} }

View File

@ -0,0 +1,27 @@
package org.lsposed.lspatch.ui.component
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import org.lsposed.lspatch.ui.util.SampleStringProvider
@Preview
@Composable
fun CenterTopBar(@PreviewParameter(SampleStringProvider::class, 1) text: String) {
CenterAlignedTopAppBar(
title = {
Text(
text = text,
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Bold,
fontFamily = FontFamily.Monospace,
style = MaterialTheme.typography.titleMedium
)
}
)
}

View File

@ -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
@ -21,7 +22,7 @@ fun SettingsSwitch(
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.clickable(onClick = onClick), enabled, onClick, icon, title, desc, extraContent) {
Switch(checked = checked, onCheckedChange = { onClick() }) Switch(checked = checked, onCheckedChange = { onClick() })
} }
} }

View File

@ -30,6 +30,7 @@ import com.ramcosta.composedestinations.annotation.RootNavGraph
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.lsposed.lspatch.R import org.lsposed.lspatch.R
import org.lsposed.lspatch.share.LSPConfig import org.lsposed.lspatch.share.LSPConfig
import org.lsposed.lspatch.ui.component.CenterTopBar
import org.lsposed.lspatch.ui.util.HtmlText import org.lsposed.lspatch.ui.util.HtmlText
import org.lsposed.lspatch.ui.util.LocalSnackbarHost import org.lsposed.lspatch.ui.util.LocalSnackbarHost
import org.lsposed.lspatch.util.ShizukuApi import org.lsposed.lspatch.util.ShizukuApi
@ -40,7 +41,9 @@ import rikka.shizuku.Shizuku
@Destination @Destination
@Composable @Composable
fun HomeScreen() { fun HomeScreen() {
Scaffold(topBar = { TopBar() }) { innerPadding -> Scaffold(
topBar = { CenterTopBar(stringResource(R.string.app_name)) }
) { innerPadding ->
Column( Column(
modifier = Modifier modifier = Modifier
.padding(innerPadding) .padding(innerPadding)
@ -56,22 +59,6 @@ fun HomeScreen() {
} }
} }
@Preview
@Composable
private fun TopBar() {
CenterAlignedTopAppBar(
title = {
Text(
text = stringResource(R.string.app_name),
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Bold,
fontFamily = FontFamily.Monospace,
style = MaterialTheme.typography.titleMedium
)
}
)
}
private val listener: (Int, Int) -> Unit = { _, grantResult -> private val listener: (Int, Int) -> Unit = { _, grantResult ->
ShizukuApi.isPermissionGranted = grantResult == PackageManager.PERMISSION_GRANTED ShizukuApi.isPermissionGranted = grantResult == PackageManager.PERMISSION_GRANTED
} }

View File

@ -4,19 +4,21 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
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.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import org.lsposed.lspatch.ui.component.CenterTopBar
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Destination @Destination
@Composable @Composable
fun LogsScreen() { fun LogsScreen() {
Scaffold(topBar = { TopBar() }) { innerPadding -> Scaffold(
topBar = { CenterTopBar(stringResource(BottomBarDestination.Logs.label)) }
) { innerPadding ->
Text( Text(
modifier = Modifier modifier = Modifier
.padding(innerPadding) .padding(innerPadding)
@ -26,10 +28,3 @@ fun LogsScreen() {
) )
} }
} }
@Composable
private fun TopBar() {
SmallTopAppBar(
title = { Text(stringResource(BottomBarDestination.Logs.label)) }
)
}

View File

@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -18,6 +17,7 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.result.ResultRecipient
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.lsposed.lspatch.R import org.lsposed.lspatch.R
import org.lsposed.lspatch.ui.component.CenterTopBar
import org.lsposed.lspatch.ui.page.destinations.SelectAppsScreenDestination import org.lsposed.lspatch.ui.page.destinations.SelectAppsScreenDestination
import org.lsposed.lspatch.ui.page.manage.AppManageBody import org.lsposed.lspatch.ui.page.manage.AppManageBody
import org.lsposed.lspatch.ui.page.manage.AppManageFab import org.lsposed.lspatch.ui.page.manage.AppManageFab
@ -33,23 +33,21 @@ fun ManageScreen(
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val pagerState = rememberPagerState() val pagerState = rememberPagerState()
Scaffold( Scaffold(
topBar = { TopBar() }, topBar = { CenterTopBar(stringResource(BottomBarDestination.Manage.label)) },
floatingActionButton = { if (pagerState.currentPage == 0) AppManageFab(navigator) } floatingActionButton = { if (pagerState.currentPage == 0) AppManageFab(navigator) }
) { innerPadding -> ) { innerPadding ->
Box(Modifier.padding(innerPadding)) { Box(Modifier.padding(innerPadding)) {
Column { Column {
TabRow( TabRow(
selectedTabIndex = pagerState.currentPage, contentColor = MaterialTheme.colorScheme.secondary,
indicator = { tabPositions -> selectedTabIndex = pagerState.currentPage
TabRowDefaults.Indicator(Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]))
}
) { ) {
Tab( Tab(
selected = pagerState.currentPage == 0, selected = pagerState.currentPage == 0,
onClick = { scope.launch { pagerState.animateScrollToPage(0) } } onClick = { scope.launch { pagerState.animateScrollToPage(0) } }
) { ) {
Text( Text(
modifier = Modifier.padding(vertical = 12.dp), modifier = Modifier.padding(vertical = 16.dp),
text = stringResource(R.string.apps) text = stringResource(R.string.apps)
) )
} }
@ -58,7 +56,7 @@ fun ManageScreen(
onClick = { scope.launch { pagerState.animateScrollToPage(1) } } onClick = { scope.launch { pagerState.animateScrollToPage(1) } }
) { ) {
Text( Text(
modifier = Modifier.padding(vertical = 12.dp), modifier = Modifier.padding(vertical = 16.dp),
text = stringResource(R.string.modules) text = stringResource(R.string.modules)
) )
} }
@ -74,10 +72,3 @@ fun ManageScreen(
} }
} }
} }
@Composable
private fun TopBar() {
SmallTopAppBar(
title = { Text(stringResource(BottomBarDestination.Manage.label)) }
)
}

View File

@ -4,19 +4,21 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
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.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import org.lsposed.lspatch.ui.component.CenterTopBar
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Destination @Destination
@Composable @Composable
fun RepoScreen() { fun RepoScreen() {
Scaffold(topBar = { TopBar() }) { innerPadding -> Scaffold(
topBar = { CenterTopBar(stringResource(BottomBarDestination.Repo.label)) }
) { innerPadding ->
Text( Text(
modifier = Modifier modifier = Modifier
.padding(innerPadding) .padding(innerPadding)
@ -26,10 +28,3 @@ fun RepoScreen() {
) )
} }
} }
@Composable
private fun TopBar() {
SmallTopAppBar(
title = { Text(stringResource(BottomBarDestination.Repo.label)) }
)
}

View File

@ -25,8 +25,11 @@ import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import kotlinx.coroutines.launch 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.MyKeyStore import org.lsposed.lspatch.config.MyKeyStore
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 java.io.IOException import java.io.IOException
import java.security.GeneralSecurityException import java.security.GeneralSecurityException
import java.security.KeyStore import java.security.KeyStore
@ -36,7 +39,7 @@ import java.security.KeyStore
@Composable @Composable
fun SettingsScreen() { fun SettingsScreen() {
Scaffold( Scaffold(
topBar = { TopBar() } topBar = { CenterTopBar(stringResource(BottomBarDestination.Settings.label)) }
) { innerPadding -> ) { innerPadding ->
Column( Column(
modifier = Modifier modifier = Modifier
@ -44,17 +47,11 @@ fun SettingsScreen() {
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
KeyStore() KeyStore()
DetailPatchLogs()
} }
} }
} }
@Composable
private fun TopBar() {
SmallTopAppBar(
title = { Text(stringResource(R.string.screen_settings)) }
)
}
@Composable @Composable
private fun KeyStore() { private fun KeyStore() {
val context = LocalContext.current val context = LocalContext.current
@ -227,3 +224,12 @@ private fun KeyStore() {
) )
} }
} }
@Composable
private fun DetailPatchLogs() {
SettingsSwitch(
checked = Configs.detailPatchLogs,
onClick = { Configs.detailPatchLogs = !Configs.detailPatchLogs },
title = stringResource(R.string.settings_detail_patch_logs)
)
}

View File

@ -35,9 +35,9 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.NavResult
import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.result.ResultRecipient
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.lsposed.lspatch.Constants
import org.lsposed.lspatch.R import org.lsposed.lspatch.R
import org.lsposed.lspatch.config.ConfigManager import org.lsposed.lspatch.config.ConfigManager
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
@ -232,7 +232,7 @@ fun AppManageFab(navigator: DestinationsNavigator) {
val uri = it.data?.data ?: throw IOException("No data") val uri = it.data?.data ?: throw IOException("No data")
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, takeFlags) context.contentResolver.takePersistableUriPermission(uri, takeFlags)
lspApp.prefs.edit().putString(Constants.PREFS_STORAGE_DIRECTORY, uri.toString()).apply() Configs.storageDirectory = uri.toString()
Log.i(TAG, "Storage directory: ${uri.path}") Log.i(TAG, "Storage directory: ${uri.path}")
showNewPatchDialog = true showNewPatchDialog = true
} catch (e: Exception) { } catch (e: Exception) {
@ -325,7 +325,7 @@ fun AppManageFab(navigator: DestinationsNavigator) {
FloatingActionButton( FloatingActionButton(
content = { Icon(Icons.Filled.Add, stringResource(R.string.add)) }, content = { Icon(Icons.Filled.Add, stringResource(R.string.add)) },
onClick = { onClick = {
val uri = lspApp.prefs.getString(Constants.PREFS_STORAGE_DIRECTORY, null)?.toUri() val uri = Configs.storageDirectory?.toUri()
if (uri == null) { if (uri == null) {
shouldSelectDirectory = true shouldSelectDirectory = true
} else { } else {
@ -337,7 +337,7 @@ fun AppManageFab(navigator: DestinationsNavigator) {
showNewPatchDialog = true showNewPatchDialog = true
}.onFailure { }.onFailure {
Log.w(TAG, "Failed to take persistable permission for saved uri", it) Log.w(TAG, "Failed to take persistable permission for saved uri", it)
lspApp.prefs.edit().putString(Constants.PREFS_STORAGE_DIRECTORY, null).apply() Configs.storageDirectory = null
shouldSelectDirectory = true shouldSelectDirectory = true
} }
} }

View File

@ -0,0 +1,31 @@
package org.lsposed.lspatch.ui.util
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import kotlin.reflect.KProperty
class DelegateState<T>(initial: T, private val sideEffectSetter: (T) -> Unit) {
private var snapshot by mutableStateOf(initial)
var value: T
get() = snapshot
set(value) {
snapshot = value
sideEffectSetter(snapshot)
}
operator fun component1(): T = value
operator fun component2(): (T) -> Unit = { value = it }
}
fun <T> delegateStateOf(initial: T, sideEffectSetter: (T) -> Unit) = DelegateState(initial, sideEffectSetter)
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> DelegateState<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> DelegateState<T>.setValue(thisObj: Any?, property: KProperty<*>, value: T) {
this.value = value
}

View File

@ -0,0 +1,7 @@
package org.lsposed.lspatch.ui.util
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
class SampleStringProvider : PreviewParameterProvider<String> {
override val values: Sequence<String> = sequenceOf("Hello", "World")
}

View File

@ -89,7 +89,6 @@ class NewPatchViewModel : ViewModel() {
Log.d(TAG, "Submit patch") Log.d(TAG, "Submit patch")
if (useManager) embeddedModules = emptyList() if (useManager) embeddedModules = emptyList()
patchOptions = Patcher.Options( patchOptions = Patcher.Options(
verbose = true,
config = PatchConfig(useManager, debuggable, overrideVersionCode, sign[0], sign[1], sigBypassLevel, null, null), config = PatchConfig(useManager, debuggable, overrideVersionCode, sign[0], sign[1], sigBypassLevel, null, null),
apkPaths = listOf(patchApp.app.sourceDir) + (patchApp.app.splitSourceDirs ?: emptyArray()), apkPaths = listOf(patchApp.app.sourceDir) + (patchApp.app.splitSourceDirs ?: emptyArray()),
embeddedModules = embeddedModules.flatMap { listOf(it.app.sourceDir) + (it.app.splitSourceDirs ?: emptyArray()) } embeddedModules = embeddedModules.flatMap { listOf(it.app.sourceDir) + (it.app.splitSourceDirs ?: emptyArray()) }

View File

@ -96,14 +96,7 @@ class AppManageViewModel : ViewModel() {
} }
} }
} }
Patcher.patch( Patcher.patch(logger, Patcher.Options(config, patchPaths, embeddedModulePaths))
logger, Patcher.Options(
verbose = true,
config = config,
apkPaths = patchPaths,
embeddedModules = embeddedModulePaths
)
)
val (status, message) = LSPPackageManager.install() val (status, message) = LSPPackageManager.install()
if (status != PackageInstaller.STATUS_SUCCESS) throw RuntimeException(message) if (status != PackageInstaller.STATUS_SUCCESS) throw RuntimeException(message)
} }

View File

@ -18,8 +18,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.lsposed.lspatch.Constants.PATCH_FILE_SUFFIX import org.lsposed.lspatch.Constants.PATCH_FILE_SUFFIX
import org.lsposed.lspatch.Constants.PREFS_STORAGE_DIRECTORY
import org.lsposed.lspatch.config.ConfigManager import org.lsposed.lspatch.config.ConfigManager
import org.lsposed.lspatch.config.Configs
import org.lsposed.lspatch.lspApp import org.lsposed.lspatch.lspApp
import org.lsposed.patch.util.ManifestParser import org.lsposed.patch.util.ManifestParser
import java.io.File import java.io.File
@ -86,10 +86,8 @@ object LSPPackageManager {
flags = flags or 0x00000004 /* PackageManager.INSTALL_ALLOW_TEST */ or 0x00000002 /* PackageManager.INSTALL_REPLACE_EXISTING */ flags = flags or 0x00000004 /* PackageManager.INSTALL_ALLOW_TEST */ or 0x00000002 /* PackageManager.INSTALL_REPLACE_EXISTING */
HiddenApiBridge.PackageInstaller_SessionParams_installFlags(params, flags) HiddenApiBridge.PackageInstaller_SessionParams_installFlags(params, flags)
ShizukuApi.createPackageInstallerSession(params).use { session -> ShizukuApi.createPackageInstallerSession(params).use { session ->
val uri = lspApp.prefs.getString(PREFS_STORAGE_DIRECTORY, null)?.toUri() val uri = Configs.storageDirectory?.toUri() ?: throw IOException("Uri is null")
?: throw IOException("Uri is null") val root = DocumentFile.fromTreeUri(lspApp, uri) ?: throw IOException("DocumentFile is null")
val root = DocumentFile.fromTreeUri(lspApp, uri)
?: throw IOException("DocumentFile is null")
root.listFiles().forEach { file -> root.listFiles().forEach { file ->
if (file.name?.endsWith(PATCH_FILE_SUFFIX) != true) return@forEach if (file.name?.endsWith(PATCH_FILE_SUFFIX) != true) return@forEach
Log.d(TAG, "Add ${file.name}") Log.d(TAG, "Add ${file.name}")

View File

@ -84,4 +84,5 @@
<string name="settings_keystore_wrong_password">Wrong keystore password</string> <string name="settings_keystore_wrong_password">Wrong keystore password</string>
<string name="settings_keystore_wrong_alias">Wrong alias name</string> <string name="settings_keystore_wrong_alias">Wrong alias name</string>
<string name="settings_keystore_wrong_alias_password">Wrong alias password</string> <string name="settings_keystore_wrong_alias_password">Wrong alias password</string>
<string name="settings_detail_patch_logs">Detail patch logs</string>
</resources> </resources>