Type safe preferences & Change topbar style
This commit is contained in:
parent
220b6db35c
commit
cb1ba36514
|
|
@ -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"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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)) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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)) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
@ -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()) }
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue