diff --git a/Readme-en.md b/Readme-en.md index 944c2b8..14f20fe 100644 --- a/Readme-en.md +++ b/Readme-en.md @@ -16,6 +16,7 @@ Use Android Device owner privilege to manage your device. - System - Options: disable camera, disable screenshot, master volume mute, disable USB signal... + - Lock task mode - Manage CA certificates - _Wipe data_ - ... @@ -33,8 +34,9 @@ Use Android Device owner privilege to manage your device. - Applications - Suspend/hide app - Block app uninstallation - - Install/uninstall app - Grant/revoke permissions + - Clear app storage + - Install/uninstall app - ... - User restriction - Network: disable configuring mobile network, disable configuring Wi-Fi, disable SMS, disable outgoing calls... @@ -57,6 +59,8 @@ Use Android Device owner privilege to manage your device. ## Activate - Shizuku (recommended) +- Dhizuku +- Root - Execute command in adb shell: `dpm set-device-owner com.bintianqi.owndroid/.Receiver` ## FAQ @@ -72,7 +76,6 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the Solutions: - Freeze apps who hold those accounts. - Delete these accounts. -- Use LSPosed module [HookDPM](https://github.com/BinTianqi/HookDPM). #### Already several users on the device @@ -82,7 +85,6 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the Solutions: - Delete secondary users. -- Use LSPosed module [HookDPM](https://github.com/BinTianqi/HookDPM). > [!NOTE] > Some systems have features such as app cloning and children space, which are usually users. @@ -116,6 +118,7 @@ Solution: Use OwnDroid testkey version | ADD_USER_RESTRICTION | `restriction` | | | CLEAR_USER_RESTRICTION | `restriction` | | | LOCK | | | +| REBOOT | | 7 | [Available user restrictions](https://developer.android.com/reference/android/os/UserManager#constants_1) diff --git a/Readme-ja.md b/Readme-ja.md index b357570..aa2eec1 100644 --- a/Readme-ja.md +++ b/Readme-ja.md @@ -57,6 +57,8 @@ AndroidのDevice owner特権を使用してデバイスを管理します。 ## アクティベート - Shizuku (推奨) +- Dhizuku +- Root - adbシェルでコマンドを実行: `dpm set-device-owner com.bintianqi.owndroid/.Receiver` ## FAQ @@ -72,7 +74,6 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the 解決策: - これらのアカウントを保持しているアプリを凍結します。 - これらのアカウントを削除します。 -- LSPosedモジュール [HookDPM](https://github.com/BinTianqi/HookDPM) を使用します。 #### デバイスに既に複数のユーザーが存在する場合 @@ -82,7 +83,6 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the 解決策: - セカンダリユーザーを削除します。 -- LSPosedモジュール [HookDPM](https://github.com/BinTianqi/HookDPM) を使用します。 > [!NOTE] > 一部のシステムにはアプリのクローンや子供用スペースなどの機能があり、通常はユーザーとして扱われます。 @@ -108,14 +108,15 @@ java.lang.IllegalStateException: Unexpected @ProvisioningPreCondition ## API | ID | Extras | 最小Androidバージョン | -|------------------------|---------------|:---------------------:| -| HIDE | `package` | | -| UNHIDE | `package` | | -| SUSPEND | `package` | 7 | -| UNSUSPEND | `package` | 7 | -| ADD_USER_RESTRICTION | `restriction` | | -| CLEAR_USER_RESTRICTION | `restriction` | | -| LOCK | | | +|------------------------|---------------|:--------------:| +| HIDE | `package` | | +| UNHIDE | `package` | | +| SUSPEND | `package` | 7 | +| UNSUSPEND | `package` | 7 | +| ADD_USER_RESTRICTION | `restriction` | | +| CLEAR_USER_RESTRICTION | `restriction` | | +| LOCK | | | +| REBOOT | | 7 | [利用可能なユーザー制限](https://developer.android.com/reference/android/os/UserManager#constants_1) diff --git a/Readme.md b/Readme.md index c75f0c6..87b2515 100644 --- a/Readme.md +++ b/Readme.md @@ -16,6 +16,7 @@ - 系统 - 选项:禁用摄像头、禁止截屏、全局静音、禁用USB信号... + - 锁定任务模式 - 管理CA证书 - _清除数据_ - ... @@ -33,8 +34,9 @@ - 应用管理 - 挂起/隐藏应用 - 阻止应用卸载 - - 安装/卸载应用 - 授予/撤销权限 + - 清除应用存储 + - 安装/卸载应用 - ... - 用户限制 - 网络:禁止配置移动网络、禁止配置Wi-Fi、禁用短信、禁止拨出电话... @@ -57,6 +59,8 @@ ## 激活 - Shizuku (推荐) +- Dhizuku +- Root - 在ADB命令行中执行命令: `dpm set-device-owner com.bintianqi.owndroid/.Receiver` ## FAQ @@ -72,7 +76,6 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the 解决办法: - 冻结持有这些账号的app。 - 删除这些账号。 -- 使用LSPosed模块 [HookDPM](https://github.com/BinTianqi/HookDPM)。 #### 设备上有多个用户 @@ -82,7 +85,6 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the 解决办法: - 删除次级用户。 -- 使用LSPosed模块[HookDPM](https://github.com/BinTianqi/HookDPM)。 > [!NOTE] > 一些系统有应用克隆、儿童空间等功能,它们通常是用户。 @@ -116,6 +118,7 @@ java.lang.IllegalStateException: Unexpected @ProvisioningPreCondition | ADD_USER_RESTRICTION | `restriction` | | | CLEAR_USER_RESTRICTION | `restriction` | | | LOCK | | | +| REBOOT | | 7 | [可用的用户限制](https://developer.android.google.cn/reference/android/os/UserManager#constants_1) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4348f78..3c63d89 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -24,8 +24,8 @@ android { applicationId = "com.bintianqi.owndroid" minSdk = 21 targetSdk = 35 - versionCode = 38 - versionName = "6.6" + versionCode = 39 + versionName = "7.0" multiDexEnabled = false } diff --git a/app/src/main/java/com/bintianqi/owndroid/ApiReceiver.kt b/app/src/main/java/com/bintianqi/owndroid/ApiReceiver.kt index d1df5a5..0cf7fcd 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ApiReceiver.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ApiReceiver.kt @@ -22,14 +22,15 @@ class ApiReceiver: BroadcastReceiver() { if(!app.isNullOrEmpty()) log += "\npackage: $app" try { @SuppressWarnings("NewApi") - val ok = when(intent.action) { - "com.bintianqi.owndroid.action.HIDE" -> dpm.setApplicationHidden(receiver, app, true) - "com.bintianqi.owndroid.action.UNHIDE" -> dpm.setApplicationHidden(receiver, app, false) - "com.bintianqi.owndroid.action.SUSPEND" -> dpm.setPackagesSuspended(receiver, arrayOf(app), true).isEmpty() - "com.bintianqi.owndroid.action.UNSUSPEND" -> dpm.setPackagesSuspended(receiver, arrayOf(app), false).isEmpty() - "com.bintianqi.owndroid.action.ADD_USER_RESTRICTION" -> { dpm.addUserRestriction(receiver, restriction); true } - "com.bintianqi.owndroid.action.CLEAR_USER_RESTRICTION" -> { dpm.clearUserRestriction(receiver, restriction); true } - "com.bintianqi.owndroid.action.LOCK" -> { dpm.lockNow(); true } + val ok = when(intent.action?.removePrefix("com.bintianqi.owndroid.action.")) { + "HIDE" -> dpm.setApplicationHidden(receiver, app, true) + "UNHIDE" -> dpm.setApplicationHidden(receiver, app, false) + "SUSPEND" -> dpm.setPackagesSuspended(receiver, arrayOf(app), true).isEmpty() + "UNSUSPEND" -> dpm.setPackagesSuspended(receiver, arrayOf(app), false).isEmpty() + "ADD_USER_RESTRICTION" -> { dpm.addUserRestriction(receiver, restriction); true } + "CLEAR_USER_RESTRICTION" -> { dpm.clearUserRestriction(receiver, restriction); true } + "LOCK" -> { dpm.lockNow(); true } + "REBOOT" -> { dpm.reboot(receiver); true } else -> { log += "\nInvalid action" false diff --git a/app/src/main/java/com/bintianqi/owndroid/AppInstallerActivity.kt b/app/src/main/java/com/bintianqi/owndroid/AppInstallerActivity.kt index b94ad82..8efda6d 100644 --- a/app/src/main/java/com/bintianqi/owndroid/AppInstallerActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/AppInstallerActivity.kt @@ -63,8 +63,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog import androidx.core.content.ContextCompat +import androidx.core.net.toUri import androidx.fragment.app.FragmentActivity import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -80,7 +80,6 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.net.URLDecoder -import androidx.core.net.toUri class AppInstallerActivity:FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -181,7 +180,7 @@ private fun AppInstaller( ResultDialog(result, onResultDialogClose) } } - if(appLockDialog) Dialog({ appLockDialog = false }) { + if(appLockDialog) { AppLockDialog({ appLockDialog = false onStartInstall() diff --git a/app/src/main/java/com/bintianqi/owndroid/AppLock.kt b/app/src/main/java/com/bintianqi/owndroid/AppLock.kt index 2bc5739..f5e02fc 100644 --- a/app/src/main/java/com/bintianqi/owndroid/AppLock.kt +++ b/app/src/main/java/com/bintianqi/owndroid/AppLock.kt @@ -21,9 +21,16 @@ import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.Icon import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager @@ -32,14 +39,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import kotlinx.serialization.Serializable - -@Serializable object AppLock +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties @Composable -fun AppLockDialog(onSucceed: () -> Unit, onDismiss: () -> Unit) { +fun AppLockDialog(onSucceed: () -> Unit, onDismiss: () -> Unit) = Dialog(onDismiss, DialogProperties(true, false)) { val context = LocalContext.current val fm = LocalFocusManager.current + val fr = FocusRequester() val sp = SharedPrefs(context) var input by remember { mutableStateOf("") } var isError by remember { mutableStateOf(false) } @@ -51,12 +58,15 @@ fun AppLockDialog(onSucceed: () -> Unit, onDismiss: () -> Unit) { isError = true } } + LaunchedEffect(Unit) { + fr.requestFocus() + } BackHandler(onBack = onDismiss) Card(Modifier.pointerInput(Unit) { detectTapGestures(onTap = { fm.clearFocus() }) }, shape = RoundedCornerShape(16.dp)) { Column(Modifier.padding(12.dp)) { Row(verticalAlignment = Alignment.CenterVertically) { OutlinedTextField( - input, { input = it; isError = false }, Modifier.width(200.dp), + input, { input = it; isError = false }, Modifier.width(200.dp).focusRequester(fr), label = { Text(stringResource(R.string.password)) }, isError = isError, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Password, imeAction = if(input.length >= 4) ImeAction.Go else ImeAction.Done diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 14ff9d5..b9305d6 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -1,7 +1,6 @@ package com.bintianqi.owndroid import android.annotation.SuppressLint -import android.app.Activity import android.os.Build.VERSION import android.os.Bundle import android.widget.Toast @@ -38,6 +37,9 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll @@ -47,7 +49,6 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.DialogProperties import androidx.core.view.WindowCompat import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Lifecycle @@ -57,7 +58,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.compose.dialog import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute import com.bintianqi.owndroid.dpm.AddApnSetting @@ -257,9 +257,13 @@ class MainActivity : FragmentActivity() { val vm by viewModels() lifecycleScope.launch { delay(5000); setDefaultAffiliationID(context) } setContent { + var appLockDialog by rememberSaveable { mutableStateOf(false) } val theme by vm.theme.collectAsStateWithLifecycle() OwnDroidTheme(theme) { - Home(vm) + Home(vm) { appLockDialog = true } + if (appLockDialog) { + AppLockDialog({ appLockDialog = false }) { moveTaskToBack(true) } + } } } } @@ -282,7 +286,7 @@ class MainActivity : FragmentActivity() { @ExperimentalMaterial3Api @Composable -fun Home(vm: MyViewModel) { +fun Home(vm: MyViewModel, onLock: () -> Unit) { val navController = rememberNavController() val context = LocalContext.current val receiver = context.getReceiver() @@ -477,10 +481,6 @@ fun Home(vm: MyViewModel) { composable { ApiSettings(::navigateUp) } composable { NotificationsScreen(::navigateUp) } composable { AboutScreen(::navigateUp) } - - dialog(dialogProperties = DialogProperties(false, false)) { - AppLockDialog(::navigateUp) { (context as? Activity)?.moveTaskToBack(true) } - } } DisposableEffect(lifecycleOwner) { val sp = SharedPrefs(context) @@ -489,9 +489,7 @@ fun Home(vm: MyViewModel) { (event == Lifecycle.Event.ON_CREATE && !sp.lockPasswordHash.isNullOrEmpty()) || (event == Lifecycle.Event.ON_RESUME && sp.lockWhenLeaving) ) { - navController.navigate(AppLock) { - launchSingleTop = true - } + onLock() } } lifecycleOwner.lifecycle.addObserver(observer) diff --git a/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt b/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt index e189c97..6beaaaf 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt @@ -13,12 +13,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource -import androidx.compose.ui.window.Dialog +import androidx.core.content.edit import androidx.fragment.app.FragmentActivity import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import kotlin.system.exitProcess -import androidx.core.content.edit class ManageSpaceActivity: FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -30,9 +29,7 @@ class ManageSpaceActivity: FragmentActivity() { OwnDroidTheme(theme) { var appLockDialog by remember { mutableStateOf(!SharedPrefs(this).lockPasswordHash.isNullOrEmpty()) } if(appLockDialog) { - Dialog(::finish) { - AppLockDialog({ appLockDialog = false }, ::finish) - } + AppLockDialog({ appLockDialog = false }, ::finish) } else { AlertDialog( text = { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 01a311b..1219145 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -105,7 +105,7 @@ Account type Transfer Ownership Target component name - Lockscreen info + Lock screen info Support Messages Short message Long message diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3d2fa0e..4a49976 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,9 @@ [versions] -agp = "8.9.1" +agp = "8.9.2" kotlin = "2.1.20" -navigation-compose = "2.8.9" -composeBom = "2025.04.00" +navigation-compose = "2.9.0" +composeBom = "2025.05.00" accompanist-drawablepainter = "0.35.0-alpha" accompanist-permissions = "0.37.0" shizuku = "13.1.5" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3ac8cd1..5eaef7f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,8 +1,8 @@ #Fri Jan 12 20:22:20 CST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip -#distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip -#distributionUrl=https://mirrors.aliyun.com/gradle/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +#distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.14-bin.zip +#distributionUrl=https://mirrors.aliyun.com/gradle/distributions/v8.14.0/gradle-8.14-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists