diff --git a/app/src/main/java/com/bintianqi/owndroid/ApiReceiver.kt b/app/src/main/java/com/bintianqi/owndroid/ApiReceiver.kt index 550df66..eca67d7 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ApiReceiver.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ApiReceiver.kt @@ -66,7 +66,6 @@ class ApiReceiver: BroadcastReceiver() { } else -> { log += "\nInvalid action" - false } } } catch(e: Exception) { diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 31742c1..446d856 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -23,7 +23,6 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -114,6 +113,8 @@ import com.bintianqi.owndroid.dpm.DisableAccountManagement import com.bintianqi.owndroid.dpm.DisableAccountManagementScreen import com.bintianqi.owndroid.dpm.DisableMeteredData import com.bintianqi.owndroid.dpm.DisableUserControl +import com.bintianqi.owndroid.dpm.EditAppGroup +import com.bintianqi.owndroid.dpm.EditAppGroupScreen import com.bintianqi.owndroid.dpm.EnableSystemApp import com.bintianqi.owndroid.dpm.EnableSystemAppScreen import com.bintianqi.owndroid.dpm.FrpPolicy @@ -134,6 +135,8 @@ import com.bintianqi.owndroid.dpm.LockScreenInfo import com.bintianqi.owndroid.dpm.LockScreenInfoScreen import com.bintianqi.owndroid.dpm.LockTaskMode import com.bintianqi.owndroid.dpm.LockTaskModeScreen +import com.bintianqi.owndroid.dpm.ManageAppGroups +import com.bintianqi.owndroid.dpm.ManageAppGroupsScreen import com.bintianqi.owndroid.dpm.MtePolicy import com.bintianqi.owndroid.dpm.MtePolicyScreen import com.bintianqi.owndroid.dpm.NearbyStreamingPolicy @@ -284,6 +287,9 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { fun choosePackage() { navController.navigate(ApplicationsList(false)) } + fun navigateToAppGroups() { + navController.navigate(ManageAppGroups) + } LaunchedEffect(Unit) { if(!Privilege.status.value.activated) { navController.navigate(WorkModes(false)) { @@ -522,20 +528,20 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { composable { PackageFunctionScreen(R.string.suspend, vm.suspendedPackages, vm::getSuspendedPackaged, vm::setPackageSuspended, ::navigateUp, vm.chosenPackage, ::choosePackage, - R.string.info_suspend_app) + ::navigateToAppGroups, vm.appGroups, R.string.info_suspend_app) } composable { PackageFunctionScreen(R.string.hide, vm.hiddenPackages, vm::getHiddenPackages, - vm::setPackageHidden, ::navigateUp, vm.chosenPackage, ::choosePackage) + vm::setPackageHidden, ::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups) } composable { PackageFunctionScreenWithoutResult(R.string.block_uninstall, vm.ubPackages, - vm::getUbPackages, vm::setPackageUb, ::navigateUp, vm.chosenPackage, ::choosePackage) + vm::getUbPackages, vm::setPackageUb, ::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups) } composable { PackageFunctionScreenWithoutResult(R.string.disable_user_control, vm.ucdPackages, vm::getUcdPackages, vm::setPackageUcd, ::navigateUp, vm.chosenPackage, - ::choosePackage, R.string.info_disable_user_control) + ::choosePackage, ::navigateToAppGroups, vm.appGroups, R.string.info_disable_user_control) } composable { PermissionsManagerScreen(vm.packagePermissions, vm::getPackagePermissions, @@ -543,7 +549,8 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { } composable { PackageFunctionScreen(R.string.disable_metered_data, vm.mddPackages, - vm::getMddPackages, vm::setPackageMdd, ::navigateUp, vm.chosenPackage, ::choosePackage) + vm::getMddPackages, vm::setPackageMdd, ::navigateUp, vm.chosenPackage, + ::choosePackage, ::navigateToAppGroups, vm.appGroups) } composable { ClearAppStorageScreen(vm.chosenPackage, ::choosePackage, vm::clearAppData, ::navigateUp) @@ -554,7 +561,8 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { composable { PackageFunctionScreenWithoutResult(R.string.keep_uninstalled_packages, vm.kuPackages, vm::getKuPackages, vm::setPackageKu, ::navigateUp, vm.chosenPackage, - ::choosePackage, R.string.info_keep_uninstalled_apps) + ::choosePackage, ::navigateToAppGroups, vm.appGroups, + R.string.info_keep_uninstalled_apps) } composable { InstallExistingAppScreen(vm.chosenPackage, ::choosePackage, @@ -562,11 +570,13 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { } composable { PackageFunctionScreenWithoutResult(R.string.cross_profile_apps, vm.cpPackages, - vm::getCpPackages, vm::setPackageCp, ::navigateUp, vm.chosenPackage, ::choosePackage) + vm::getCpPackages, vm::setPackageCp, ::navigateUp, vm.chosenPackage, + ::choosePackage, ::navigateToAppGroups, vm.appGroups) } composable { PackageFunctionScreen(R.string.cross_profile_widget, vm.cpwProviders, - vm::getCpwProviders, vm::setCpwProvider, ::navigateUp, vm.chosenPackage, ::choosePackage) + vm::getCpwProviders, vm::setCpwProvider, ::navigateUp, vm.chosenPackage, + ::choosePackage, ::navigateToAppGroups, vm.appGroups) } composable { CredentialManagerPolicyScreen(vm.chosenPackage, ::choosePackage, @@ -588,6 +598,19 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { composable { SetDefaultDialerScreen(vm.chosenPackage, ::choosePackage, vm::setDefaultDialer, ::navigateUp) } + composable { + ManageAppGroupsScreen( + vm.appGroups, + { id, name, apps -> navController.navigate(EditAppGroup(id, name, apps)) }, + ::navigateUp + ) + } + composable { + EditAppGroupScreen( + it.toRoute(), vm::getAppInfo, ::navigateUp, vm::setAppGroup, + vm::deleteAppGroup, ::choosePackage, vm.chosenPackage + ) + } composable { UserRestrictionScreen(vm::getUserRestrictions, ::navigateUp, ::navigate) diff --git a/app/src/main/java/com/bintianqi/owndroid/MyDbHelper.kt b/app/src/main/java/com/bintianqi/owndroid/MyDbHelper.kt index 3504c09..dfa0805 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MyDbHelper.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MyDbHelper.kt @@ -4,11 +4,12 @@ import android.content.Context import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper -class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 3) { +class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 4) { override fun onCreate(db: SQLiteDatabase) { db.execSQL(DHIZUKU_CLIENTS_TABLE) db.execSQL(SECURITY_LOGS_TABLE) db.execSQL(NETWORK_LOGS_TABLE) + db.execSQL(APP_GROUPS_TABLE) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { if (oldVersion < 2) { @@ -17,6 +18,9 @@ class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 3) { if (oldVersion < 3) { db.execSQL(NETWORK_LOGS_TABLE) } + if (oldVersion < 4) { + db.execSQL(APP_GROUPS_TABLE) + } } companion object { const val DHIZUKU_CLIENTS_TABLE = "CREATE TABLE dhizuku_clients (uid INTEGER PRIMARY KEY," + @@ -26,5 +30,8 @@ class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 3) { const val NETWORK_LOGS_TABLE = "CREATE TABLE network_logs (id INTEGER, package INTEGER," + "time INTEGER, type TEXT, host TEXT, count INTEGER, addresses TEXT," + "address TEXT, port INTEGER)" + const val APP_GROUPS_TABLE = "CREATE TABLE app_groups(" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "name TEXT, apps TEXT)" } } \ No newline at end of file diff --git a/app/src/main/java/com/bintianqi/owndroid/MyRepository.kt b/app/src/main/java/com/bintianqi/owndroid/MyRepository.kt index 8e6af20..62f7c57 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MyRepository.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MyRepository.kt @@ -9,6 +9,7 @@ import androidx.annotation.RequiresApi import androidx.core.database.getIntOrNull import androidx.core.database.getLongOrNull import androidx.core.database.getStringOrNull +import com.bintianqi.owndroid.dpm.AppGroup import com.bintianqi.owndroid.dpm.NetworkLog import com.bintianqi.owndroid.dpm.SecurityEvent import com.bintianqi.owndroid.dpm.SecurityEventWithData @@ -224,4 +225,27 @@ class MyRepository(val dbHelper: MyDbHelper) { fun deleteNetworkLogs() { dbHelper.writableDatabase.execSQL("DELETE FROM network_logs") } + + fun getAppGroups(): List { + val list = mutableListOf() + dbHelper.readableDatabase.rawQuery("SELECT * FROM app_groups", null).use { + while (it.moveToNext()) { + list += AppGroup(it.getInt(0), it.getString(1), it.getString(2).split(',')) + } + } + return list + } + fun setAppGroup(id: Int?, name: String, apps: List) { + val cv = ContentValues() + cv.put("name", name) + cv.put("apps", apps.joinToString(",")) + if (id == null) { + dbHelper.writableDatabase.insert("app_groups", null, cv) + } else { + dbHelper.writableDatabase.update("app_groups", cv, "id = ?", arrayOf(id.toString())) + } + } + fun deleteAppGroup(id: Int) { + dbHelper.writableDatabase.delete("app_groups", "id = ?", arrayOf(id.toString())) + } } \ No newline at end of file diff --git a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt index 86a8051..5bc3510 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt @@ -59,6 +59,7 @@ import com.bintianqi.owndroid.dpm.ApnAuthType import com.bintianqi.owndroid.dpm.ApnConfig import com.bintianqi.owndroid.dpm.ApnMvnoType import com.bintianqi.owndroid.dpm.ApnProtocol +import com.bintianqi.owndroid.dpm.AppGroup import com.bintianqi.owndroid.dpm.AppStatus import com.bintianqi.owndroid.dpm.CaCertInfo import com.bintianqi.owndroid.dpm.CreateUserResult @@ -509,6 +510,24 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } } + val appGroups = MutableStateFlow(emptyList()) + init { + getAppGroups() + } + fun getAppGroups() { + appGroups.value = myRepo.getAppGroups() + } + fun setAppGroup(id: Int?, name: String, apps: List) { + myRepo.setAppGroup(id, name, apps) + getAppGroups() + } + fun deleteAppGroup(id: Int) { + myRepo.deleteAppGroup(id) + appGroups.update { group -> + group.filter { it.id != id } + } + } + @RequiresApi(24) fun reboot() { DPM.reboot(DAR) diff --git a/app/src/main/java/com/bintianqi/owndroid/Privilege.kt b/app/src/main/java/com/bintianqi/owndroid/Privilege.kt index c68fc80..5b2f284 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Privilege.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Privilege.kt @@ -23,8 +23,8 @@ object Privilege { return } } - } catch(_: Exception) { - false + } catch(e: Exception) { + e.printStackTrace() } dhizukuErrorStatus.value = 2 } diff --git a/app/src/main/java/com/bintianqi/owndroid/Utils.kt b/app/src/main/java/com/bintianqi/owndroid/Utils.kt index 197244d..700658d 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Utils.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Utils.kt @@ -135,7 +135,7 @@ class SerializableSaver(val serializer: KSerializer) : Saver { override fun restore(value: String): T? { return Json.decodeFromString(serializer, value) } - override fun SaverScope.save(value: T): String? { + override fun SaverScope.save(value: T): String { return Json.encodeToString(serializer, value) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt index dfb1691..1920f3f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -20,6 +21,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed @@ -30,12 +32,20 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.List +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.CheckCircle +import androidx.compose.material.icons.outlined.Delete import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LargeTopAppBar @@ -46,12 +56,15 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -89,6 +102,7 @@ import com.bintianqi.owndroid.ui.SwitchItem import com.google.accompanist.drawablepainter.rememberDrawablePainter import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.serialization.Serializable val String.isValidPackageName @@ -729,49 +743,238 @@ fun SetDefaultDialerScreen( fun PackageFunctionScreenWithoutResult( title: Int, packagesState: MutableStateFlow>, onGet: () -> Unit, onSet: (String, Boolean) -> Unit, onNavigateUp: () -> Unit, - chosenPackage: Channel, onChoosePackage: () -> Unit, notes: Int? = null + chosenPackage: Channel, onChoosePackage: () -> Unit, + navigateToGroups: () -> Unit, appGroups: StateFlow>, notes: Int? = null ) { PackageFunctionScreen( title, packagesState, onGet, { name, status -> onSet(name, status); null }, - onNavigateUp, chosenPackage, onChoosePackage, notes + onNavigateUp, chosenPackage, onChoosePackage, navigateToGroups, appGroups, notes ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun PackageFunctionScreen( title: Int, packagesState: MutableStateFlow>, onGet: () -> Unit, onSet: (String, Boolean) -> Boolean?, onNavigateUp: () -> Unit, - chosenPackage: Channel, onChoosePackage: () -> Unit, notes: Int? = null + chosenPackage: Channel, onChoosePackage: () -> Unit, + navigateToGroups: () -> Unit, appGroups: StateFlow>, notes: Int? = null ) { + val groups by appGroups.collectAsStateWithLifecycle() val packages by packagesState.collectAsStateWithLifecycle() var packageName by rememberSaveable { mutableStateOf("") } + var selectedGroup by remember { mutableStateOf(null) } LaunchedEffect(Unit) { onGet() packageName = chosenPackage.receive() } - MyLazyScaffold(title, onNavigateUp) { - items(packages, { it.name }) { - ApplicationItem(it) { - onSet(it.name, false) - } - } - item { - PackageNameTextField(packageName, onChoosePackage, - Modifier.padding(HorizontalPadding, 8.dp)) { packageName = it } - Button( - { - if (onSet(packageName, true) != false) { - println("reset") - packageName = "" + Scaffold( + topBar = { + TopAppBar( + { Text(stringResource(title)) }, + navigationIcon = { NavIcon(onNavigateUp) }, + actions = { + var expand by remember { mutableStateOf(false) } + Box { + IconButton({ + expand = true + }) { + Icon(Icons.Default.MoreVert, null) + } + DropdownMenu(expand, { expand = false }) { + groups.forEach { + DropdownMenuItem( + { Text("(${it.apps.size}) ${it.name}") }, + { + selectedGroup = it + expand = false + } + ) + } + if (groups.isNotEmpty()) HorizontalDivider() + DropdownMenuItem( + { Text(stringResource(R.string.manage_app_groups)) }, + { + navigateToGroups() + expand = false + } + ) + } } - }, - Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding).padding(bottom = 10.dp), - packageName.isValidPackageName - ) { - Text(stringResource(R.string.add)) + } + ) + } + ) { paddingValues -> + LazyColumn(Modifier.padding(paddingValues)) { + items(packages, { it.name }) { + ApplicationItem(it) { + onSet(it.name, false) + } + } + item { + PackageNameTextField(packageName, onChoosePackage, + Modifier.padding(HorizontalPadding, 8.dp)) { packageName = it } + Button( + { + if (onSet(packageName, true) != false) { + packageName = "" + } + }, + Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding).padding(bottom = 10.dp), + packageName.isValidPackageName + ) { + Text(stringResource(R.string.add)) + } + if (notes != null) Notes(notes, HorizontalPadding) + Spacer(Modifier.height(BottomPadding)) } - if (notes != null) Notes(notes, HorizontalPadding) - Spacer(Modifier.height(BottomPadding)) } } -} \ No newline at end of file + if (selectedGroup != null) AlertDialog( + text = { + Column { + Button({ + selectedGroup!!.apps.forEach { + onSet(it, true) + } + selectedGroup = null + }) { + Text(stringResource(R.string.add_to_list)) + } + Button({ + selectedGroup!!.apps.forEach { + onSet(it, false) + } + selectedGroup = null + }) { + Text(stringResource(R.string.remove_from_list)) + } + } + }, + confirmButton = { + TextButton({ selectedGroup = null }) { + Text(stringResource(R.string.cancel)) + } + }, + onDismissRequest = { selectedGroup = null } + ) +} + +class AppGroup(val id: Int, val name: String, val apps: List) + +@Serializable object ManageAppGroups + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ManageAppGroupsScreen( + appGroups: StateFlow>, + navigateToEditScreen: (Int?, String, List) -> Unit, navigateUp: () -> Unit +) { + val groups by appGroups.collectAsStateWithLifecycle() + Scaffold( + topBar = { + TopAppBar( + { Text(stringResource(R.string.app_group)) }, + navigationIcon = { NavIcon(navigateUp) } + ) + }, + floatingActionButton = { + FloatingActionButton({ + navigateToEditScreen(null, "", emptyList()) + }) { + Icon(Icons.Default.Add, null) + } + } + ) { paddingValues -> + LazyColumn(Modifier.padding(paddingValues)) { + items(groups, { it.id }) { + Column( + Modifier.fillMaxWidth().clickable { + navigateToEditScreen(it.id, it.name, it.apps) + }.padding(HorizontalPadding, 8.dp) + ) { + Text(it.name) + Text( + it.apps.size.toString() + " apps", Modifier.alpha(0.7F), + style = typography.bodyMedium + ) + } + } + } + } +} + +@Serializable class EditAppGroup(val id: Int?, val name: String, val apps: List) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun EditAppGroupScreen( + params: EditAppGroup, getAppInfo: (String) -> AppInfo, navigateUp: () -> Unit, + setGroup: (Int?, String, List) -> Unit, deleteGroup: (Int) -> Unit, + onChoosePackage: () -> Unit, chosenPackage: Channel +) { + var name by rememberSaveable { mutableStateOf(params.name) } + val list = rememberSaveable { mutableStateListOf(*params.apps.toTypedArray()) } + val appInfoList = list.map { getAppInfo(it) } + var packageName by rememberSaveable { mutableStateOf("") } + LaunchedEffect(Unit) { + packageName = chosenPackage.receive() + } + Scaffold( + topBar = { + TopAppBar( + { Text(stringResource(R.string.edit_app_group)) }, + navigationIcon = { + NavIcon(navigateUp) + }, + actions = { + if (params.id != null) IconButton({ + deleteGroup(params.id) + navigateUp() + }) { + Icon(Icons.Outlined.Delete, null) + } + IconButton( + { + setGroup(params.id, name, list) + navigateUp() + }, + enabled = name.isNotBlank() && list.isNotEmpty() + ) { + Icon(Icons.Default.Check, null) + } + } + ) + }, + contentWindowInsets = adaptiveInsets() + ) { paddingValues -> + LazyColumn(Modifier.padding(paddingValues)) { + item { + OutlinedTextField( + name, { name = it }, Modifier.fillMaxWidth().padding(HorizontalPadding, 8.dp), + label = { Text(stringResource(R.string.name)) } + ) + } + items(appInfoList, { it.name }) { + ApplicationItem(it) { + list -= it.name + } + } + item { + PackageNameTextField(packageName, onChoosePackage, + Modifier.padding(HorizontalPadding, 8.dp)) { packageName = it } + Button( + { + list += packageName + packageName = "" + }, + Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding).padding(bottom = 10.dp), + packageName.isValidPackageName + ) { + Text(stringResource(R.string.add)) + } + Spacer(Modifier.height(BottomPadding)) + } + } + } +} diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 236ebe7..258abcd 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -112,7 +112,6 @@ Не удалось инициализировать Dhizuku Разрешение Dhizuku не предоставлено - Режим Dhizuku отключен Система diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 664810e..33d5f12 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -118,7 +118,6 @@ Dhizuku Başlatılamadı Dhizuku İzni Verilmedi - Dhizuku Modu Devre Dışı Sistem diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index ad9d6cc..18c10cc 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -115,7 +115,6 @@ Dhizuku初始化失败 Dhizuku未授权 - Dhizuku模式已禁用 系统 @@ -370,6 +369,11 @@ 启用系统应用 卸载后保留 搜索 + 应用组 + 管理组 + 编辑组 + 添加到列表 + 从列表中移除 用户限制 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 452520e..e3baaaf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -122,7 +122,6 @@ Dhizuku Failed to initialize Dhizuku Dhizuku permission not granted - Dhizuku mode disabled Shizuku @@ -404,6 +403,11 @@ Install existing app Keep after uninstall Search + App group + Manage groups + Edit group + Add to list + Remove from list User restriction diff --git a/gradle.properties b/gradle.properties index 643d242..7d47327 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,3 +7,4 @@ kotlin.code.style=official org.gradle.parallel=true org.gradle.caching=true org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx1536M" +org.gradle.configuration-cache=true