diff --git a/Readme-en.md b/Readme-en.md index 14f20fe..5c2f7aa 100644 --- a/Readme-en.md +++ b/Readme-en.md @@ -2,7 +2,7 @@ # OwnDroid -Use Android Device owner privilege to manage your device. +Use Android's DevicePolicyManager API to manage your device. ## Download @@ -14,60 +14,28 @@ Use Android Device owner privilege to manage your device. ## Features -- System - - Options: disable camera, disable screenshot, master volume mute, disable USB signal... - - Lock task mode - - Manage CA certificates - - _Wipe data_ - - ... -- Network - - Add/modify/delete Wi-Fi - - Network stats - - Minimum Wi-Fi security level - - Always-on VPN - - Network logging - - ... +- System: Disable camera, disable screenshot, master volume mute, disable USB signal, Lock task mode, Manage CA certificates, Wipe data... +- Network: Add/modify/delete Wi-Fi, Network stats, Minimum Wi-Fi security level, Always-on VPN, Network logging... +- Applications: Suspend/hide app, Block app uninstallation, Grant/revoke permissions, Clear app storage, Install/uninstall app... +- User restriction: Disable SMS, disable outgoing call, disable bluetooth, disable NFC, disable USB file transfer, disable app installing, disable app uninstalling... +- User manager: User information, Start/switch/stop/delete user, Create user... +- Password and keyguard: Reset password, Require password complexity, Set screen timeout... + +## Working modes + +- Device owner (recommended) + + Activating methods: + - Shizuku + - Dhizuku + - Root + - ADB shell command `dpm set-device-owner com.bintianqi.owndroid/.Receiver` +- [Dhizuku](https://github.com/iamr0s/Dhizuku) - Work profile - - Create work profile - - Suspend personal apps - - ... -- Applications - - Suspend/hide app - - Block app uninstallation - - 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... - - Connection: disable bluetooth, disable configuring location, disable USB file transfer, disable printing... - - Applications: disable installing/uninstalling app... - - Users: disable adding/removing/switching user... - - Media: disable configuring brightness, disable adjusting volume... - - Other: disable modifying accounts, disable configuring locale, disable factory reset, disable debug features... -- User manager - - User information - - Start/switch/stop/delete user - - Create user - - ... -- Password and keyguard - - _Reset password_ - - Require password complexity - - Set screen timeout - - ... - -## Activate - -- Shizuku (recommended) -- Dhizuku -- Root -- Execute command in adb shell: `dpm set-device-owner com.bintianqi.owndroid/.Receiver` ## FAQ -### Activating - -#### Already some accounts on the device +### Already some accounts on the device ```text java.lang.IllegalStateException: Not allowed to set the device owner because there are already some accounts on the device @@ -77,7 +45,7 @@ Solutions: - Freeze apps who hold those accounts. - Delete these accounts. -#### Already several users on the device +### Already several users on the device ```text java.lang.IllegalStateException: Not allowed to set the device owner because there are already several users on the device @@ -89,7 +57,7 @@ Solutions: > [!NOTE] > Some systems have features such as app cloning and children space, which are usually users. -#### MIUI +### MIUI ```text java.lang.SecurityException: Neither user 2000 nor current process has android.permission.MANAGE_DEVICE_ADMINS. @@ -99,7 +67,7 @@ Solutions: - Enable `USB debugging (Security setting)` in developer options. - Execute activating command in root shell. -#### ColorOS +### ColorOS ```text java.lang.IllegalStateException: Unexpected @ProvisioningPreCondition diff --git a/Readme-ja.md b/Readme-ja.md index aa2eec1..861dec0 100644 --- a/Readme-ja.md +++ b/Readme-ja.md @@ -27,10 +27,6 @@ AndroidのDevice owner特権を使用してデバイスを管理します。 - 常時オンVPN - ネットワークログ - ... -- ワークプロファイル - - ワークプロファイルの作成 - - 個人アプリの一時停止 - - ... - アプリケーション - アプリの一時停止/非表示 - アンインストールのブロック @@ -63,9 +59,7 @@ AndroidのDevice owner特権を使用してデバイスを管理します。 ## FAQ -### アクティベート - -#### デバイスに既にアカウントが存在する場合 +### デバイスに既にアカウントが存在する場合 ```text java.lang.IllegalStateException: Not allowed to set the device owner because there are already some accounts on the device @@ -75,7 +69,7 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the - これらのアカウントを保持しているアプリを凍結します。 - これらのアカウントを削除します。 -#### デバイスに既に複数のユーザーが存在する場合 +### デバイスに既に複数のユーザーが存在する場合 ```text java.lang.IllegalStateException: Not allowed to set the device owner because there are already several users on the device @@ -87,7 +81,7 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the > [!NOTE] > 一部のシステムにはアプリのクローンや子供用スペースなどの機能があり、通常はユーザーとして扱われます。 -#### MIUI +### MIUI ```text java.lang.SecurityException: Neither user 2000 nor current process has android.permission.MANAGE_DEVICE_ADMINS. @@ -97,7 +91,7 @@ java.lang.SecurityException: Neither user 2000 nor current process has android.p - 開発者オプションで `USBデバッグ(セキュリティ設定)` を有効にします。 - ルートシェルでアクティベートコマンドを実行します。 -#### ColorOS +### ColorOS ```text java.lang.IllegalStateException: Unexpected @ProvisioningPreCondition diff --git a/Readme.md b/Readme.md index 87b2515..eaaf8d5 100644 --- a/Readme.md +++ b/Readme.md @@ -2,7 +2,7 @@ # OwnDroid -使用安卓Device owner特权管理你的设备。 +使用安卓的设备策略管理器API管理你的设备。 ## 下载 @@ -14,60 +14,28 @@ ## 功能 -- 系统 - - 选项:禁用摄像头、禁止截屏、全局静音、禁用USB信号... - - 锁定任务模式 - - 管理CA证书 - - _清除数据_ - - ... -- 网络 - - 添加/修改/删除 Wi-Fi - - 网络统计 - - 最小Wi-Fi安全等级 - - VPN保持打开 - - 网络日志 - - ... +- 系统:禁用摄像头、禁止截屏、全局静音、禁用USB信号、锁定任务模式、管理CA证书、清除数据... +- 网络:添加/修改/删除 Wi-Fi、网络统计、最小Wi-Fi安全等级、VPN保持打开、网络日志... +- 应用:挂起/隐藏应用、阻止应用卸载、授予/撤销权限、清除应用存储、安装/卸载应用... +- 用户限制:禁止发送短信、禁止拨出电话、禁用蓝牙、禁用NFC、禁用USB文件传输、禁止安装应用、禁止卸载应用... +- 用户:用户信息、启动/切换/停止/删除用户、创建用户... +- 密码与锁屏:重置密码、要求密码复杂度、设置屏幕超时... + +## 工作模式 + +- Device owner(推荐) + + 激活方式: + - Shizuku + - Dhizuku + - Root + - ADB shell命令 `dpm set-device-owner com.bintianqi.owndroid/.Receiver` +- [Dhizuku](https://github.com/iamr0s/Dhizuku) - 工作资料 - - 创建工作资料 - - 挂起个人应用 - - ... -- 应用管理 - - 挂起/隐藏应用 - - 阻止应用卸载 - - 授予/撤销权限 - - 清除应用存储 - - 安装/卸载应用 - - ... -- 用户限制 - - 网络:禁止配置移动网络、禁止配置Wi-Fi、禁用短信、禁止拨出电话... - - 连接:禁用蓝牙、禁止配置定位、禁用USB文件传输、禁用打印... - - 应用:禁止安装/卸载应用... - - 用户:禁止添加/删除/切换用户... - - 媒体:禁止调整亮度、禁止调整音量... - - 其他:禁止修改账号、禁止修改语言、禁止恢复出厂设置、禁用调试功能... -- 用户管理 - - 用户信息 - - 启动/切换/停止/删除用户 - - 创建用户 - - ... -- 密码与锁屏 - - _重置密码_ - - 要求密码复杂度 - - 设置屏幕超时 - - ... - -## 激活 - -- Shizuku (推荐) -- Dhizuku -- Root -- 在ADB命令行中执行命令: `dpm set-device-owner com.bintianqi.owndroid/.Receiver` ## FAQ -### 激活 - -#### 设备上有账号 +### 设备上有账号 ```text java.lang.IllegalStateException: Not allowed to set the device owner because there are already some accounts on the device @@ -77,7 +45,7 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the - 冻结持有这些账号的app。 - 删除这些账号。 -#### 设备上有多个用户 +### 设备上有多个用户 ```text java.lang.IllegalStateException: Not allowed to set the device owner because there are already several users on the device @@ -89,7 +57,7 @@ java.lang.IllegalStateException: Not allowed to set the device owner because the > [!NOTE] > 一些系统有应用克隆、儿童空间等功能,它们通常是用户。 -#### MIUI +### MIUI ```text java.lang.SecurityException: Neither user 2000 nor current process has android.permission.MANAGE_DEVICE_ADMINS. @@ -99,7 +67,7 @@ java.lang.SecurityException: Neither user 2000 nor current process has android.p - 在开发者设置中打开`USB调试(安全设置)`。 - 在root命令行中执行激活命令 -#### ColorOS +### ColorOS ```text java.lang.IllegalStateException: Unexpected @ProvisioningPreCondition @@ -147,9 +115,6 @@ context.sendBroadcast(intent) ``` (在Windows系统中应使用`./gradlew.bat`) -> [!TIP] -> 在中国大陆下载Gradle速度慢?打开`gradle/wrapper/gradle-wrapper.properties`文件,注释官方下载地址,取消注释一个镜像地址。 - ## 许可证 [License.md](LICENSE.md) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fa093e6..e95c56e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -96,7 +96,6 @@ dependencies { implementation(libs.androidx.fragment) implementation(libs.hiddenApiBypass) implementation(libs.libsu) - implementation(libs.libsu.service) implementation(libs.serialization) implementation(kotlin("reflect")) } \ No newline at end of file diff --git a/app/src/main/java/com/bintianqi/owndroid/DhizukuServer.kt b/app/src/main/java/com/bintianqi/owndroid/DhizukuServer.kt index 293ae3e..069308f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/DhizukuServer.kt +++ b/app/src/main/java/com/bintianqi/owndroid/DhizukuServer.kt @@ -8,6 +8,8 @@ import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size import androidx.compose.material3.AlertDialog @@ -16,11 +18,14 @@ import androidx.compose.material3.TextButton import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.rosan.dhizuku.aidl.IDhizukuClient import com.rosan.dhizuku.aidl.IDhizukuRequestPermissionListener @@ -97,43 +102,52 @@ class DhizukuActivity : ComponentActivity() { if (grantPermission) PackageManager.PERMISSION_GRANTED else PackageManager.PERMISSION_DENIED ) } + val vm by viewModels() + enableEdgeToEdge() setContent { - AlertDialog( - icon = { - Image(rememberDrawablePainter(icon), null, Modifier.size(35.dp)) - }, - title = { - Text(stringResource(R.string.request_permission)) - }, - text = { - Text("$label\n($packageName)") - }, - confirmButton = { - var time by remember { mutableIntStateOf(3) } - LaunchedEffect(Unit) { - (1..3).forEach { - delay(1000) - time -= 1 + var appLockDialog by remember { mutableStateOf(false) } + val theme by vm.theme.collectAsStateWithLifecycle() + OwnDroidTheme(theme) { + if (!appLockDialog) AlertDialog( + icon = { + Image(rememberDrawablePainter(icon), null, Modifier.size(35.dp)) + }, + title = { + Text(stringResource(R.string.request_permission)) + }, + text = { + Text("$label\n($packageName)") + }, + confirmButton = { + var time by remember { mutableIntStateOf(3) } + LaunchedEffect(Unit) { + (1..3).forEach { + delay(1000) + time -= 1 + } } - } - TextButton({ - close(true) - }, enabled = time == 0) { - val append = if (time > 0) " (${time}s)" else "" - Text(stringResource(R.string.allow) + append) - } - }, - dismissButton = { - TextButton({ - close(false) - }) { - Text(stringResource(R.string.reject)) - } - }, - onDismissRequest = { - finish() - } - ) + TextButton({ + if (SharedPrefs(this).lockPasswordHash.isNullOrEmpty()) { + close(true) + } else { + appLockDialog = true + } + }, enabled = time == 0) { + val append = if (time > 0) " (${time}s)" else "" + Text(stringResource(R.string.allow) + append) + } + }, + dismissButton = { + TextButton({ + close(false) + }) { + Text(stringResource(R.string.reject)) + } + }, + onDismissRequest = { close(false) } + ) + else AppLockDialog({ close(true) }) { close(false) } + } } } } diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 30ab02f..7dd9f1b 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -12,9 +12,10 @@ import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -48,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.core.view.WindowCompat import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver @@ -249,7 +249,6 @@ import java.util.Locale class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() - WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) val context = applicationContext if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("") @@ -309,7 +308,6 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { modifier = Modifier .fillMaxSize() .background(colorScheme.background) - .imePadding() .pointerInput(Unit) { detectTapGestures(onTap = { focusMgr.clearFocus() }) }, enterTransition = Animations.navHostEnterTransition, exitTransition = Animations.navHostExitTransition, @@ -514,7 +512,8 @@ private fun HomeScreen(onNavigate: (Any) -> Unit) { }, scrollBehavior = sb ) - } + }, + contentWindowInsets = WindowInsets.ime ) { Column(Modifier.fillMaxSize().padding(it).verticalScroll(rememberScrollState())) { if(privilege.device || privilege.profile) { diff --git a/app/src/main/java/com/bintianqi/owndroid/PackageChooser.kt b/app/src/main/java/com/bintianqi/owndroid/PackageChooser.kt index 39a771e..7f96ea7 100644 --- a/app/src/main/java/com/bintianqi/owndroid/PackageChooser.kt +++ b/app/src/main/java/com/bintianqi/owndroid/PackageChooser.kt @@ -10,16 +10,18 @@ import android.os.Bundle import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels -import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn @@ -75,6 +77,7 @@ class PackageChooserActivity: ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val vm by viewModels() + enableEdgeToEdge() setContent { val theme by vm.theme.collectAsStateWithLifecycle() OwnDroidTheme(theme) { @@ -177,7 +180,8 @@ fun AppChooserScreen(params: ApplicationsList, onChoosePackage: (String?) -> Uni }, colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceContainer) ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> LazyColumn(Modifier.fillMaxSize().padding(paddingValues)) { if (progress < 1F) stickyHeader { diff --git a/app/src/main/java/com/bintianqi/owndroid/Settings.kt b/app/src/main/java/com/bintianqi/owndroid/Settings.kt index 1b1eaa1..e797557 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Settings.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Settings.kt @@ -12,8 +12,10 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState @@ -116,7 +118,8 @@ fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) { } } ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> Column( modifier = Modifier 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 8c8f9d0..b1b1dac 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt @@ -23,8 +23,10 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyItemScope @@ -172,7 +174,8 @@ fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Un }, scrollBehavior = sb ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> Column( Modifier diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt index bab9bb3..7e86deb 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt @@ -520,6 +520,7 @@ fun handlePrivilegeChange(context: Context) { val privilege = myPrivilege.value val activated = privilege.device || privilege.profile val sp = SharedPrefs(context) + sp.dhizukuServer = false if(activated) { createShortcuts(context) if(!privilege.dhizuku) { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt index 74ad8f5..3d4f3e1 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -51,8 +51,10 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState @@ -239,7 +241,8 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo navigationIcon = { NavIcon(onNavigateUp) }, colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceContainer) ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> var wifiMacDialog by remember { mutableStateOf(false) } Column( diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt index 62ed867..f7e3632 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -1,19 +1,10 @@ package com.bintianqi.owndroid.dpm -import android.annotation.SuppressLint import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context -import android.content.Intent -import android.content.ServiceConnection import android.content.pm.PackageManager import android.os.Build.VERSION -import android.os.Bundle -import android.os.Handler -import android.os.IBinder -import android.os.Looper -import android.os.Message -import android.os.Messenger import android.os.PersistableBundle import androidx.activity.compose.rememberLauncherForActivityResult import androidx.annotation.Keep @@ -29,13 +20,14 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.selection.SelectionContainer @@ -44,6 +36,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.outlined.Edit @@ -55,6 +48,7 @@ import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme.colorScheme @@ -86,7 +80,6 @@ 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 androidx.core.os.bundleOf import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.bintianqi.owndroid.ChoosePackageContract import com.bintianqi.owndroid.DHIZUKU_CLIENTS_FILE @@ -114,14 +107,10 @@ import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.DhizukuRequestPermissionListener import com.topjohnwu.superuser.Shell -import com.topjohnwu.superuser.ipc.RootService -import dalvik.system.DexClassLoader import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import java.lang.invoke.MethodHandles -import java.lang.reflect.Proxy @Serializable data class WorkModes(val canNavigateUp: Boolean) @@ -134,7 +123,7 @@ fun WorkModesScreen( val context = LocalContext.current val coroutine = rememberCoroutineScope() val privilege by myPrivilege.collectAsStateWithLifecycle() - /** 0: none, 1: device owner, 2: circular progress indicator, 3: result, 4: deactivate, 5: command, 6: force activating warning */ + /** 0: none, 1: device owner, 2: circular progress indicator, 3: result, 4: deactivate, 5: command */ var dialog by remember { mutableIntStateOf(0) } Scaffold( topBar = { @@ -162,21 +151,24 @@ fun WorkModesScreen( { expanded = false dialog = 4 - } + }, + leadingIcon = { Icon(Icons.Default.Close, null) } ) if (VERSION.SDK_INT >= 26) DropdownMenuItem( { Text(stringResource(R.string.delegated_admins)) }, { expanded = false onNavigate(DelegatedAdmins) - } + }, + leadingIcon = { Icon(painterResource(R.drawable.admin_panel_settings_fill0), null) } ) if (!privilege.dhizuku && VERSION.SDK_INT >= 28) DropdownMenuItem( { Text(stringResource(R.string.transfer_ownership)) }, { expanded = false onNavigate(TransferOwnership) - } + }, + leadingIcon = { Icon(painterResource(R.drawable.swap_horiz_fill0), null) } ) } } @@ -185,7 +177,8 @@ fun WorkModesScreen( } } ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> var navigateUpOnSucceed by remember { mutableStateOf(true) } var operationSucceed by remember { mutableStateOf(false) } @@ -198,6 +191,7 @@ fun WorkModesScreen( updatePrivilege(context) handlePrivilegeChange(context) } else { + dialog = 0 context.showOperationResultToast(false) } } @@ -315,12 +309,6 @@ fun WorkModesScreen( } Spacer(Modifier.padding(horizontal = 2.dp)) Button({ dialog = 5 }) { Text(stringResource(R.string.adb_command)) } - Spacer(Modifier.padding(horizontal = 2.dp)) - if (VERSION.SDK_INT >= 33) Button({ - dialog = 6 - }) { - Text(stringResource(R.string.root_force_activate)) - } } }, confirmButton = { @@ -386,23 +374,6 @@ fun WorkModesScreen( }, onDismissRequest = { dialog = 0 } ) - if (dialog == 6) AlertDialog( - title = { Text(stringResource(R.string.warning)) }, - text = { Text(stringResource(R.string.info_force_activate)) }, - confirmButton = { - TextButton({ - dialog = 2 - navigateUpOnSucceed = false - forceActivateUsingRoot(context, ::handleResult) - }) { - Text(stringResource(R.string.continue_str)) - } - }, - dismissButton = { - TextButton({ dialog = 0 }) { Text(stringResource(R.string.cancel)) } - }, - onDismissRequest = { dialog = 0 } - ) } } @@ -469,75 +440,6 @@ fun activateUsingDhizuku(context: Context, callback: (Boolean, Boolean, String?) } } -fun forceActivateUsingRoot(context: Context, callback: (Boolean, Boolean, String?) -> Unit) { - val connection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName?, service: IBinder?) { - val handler = Handler(Looper.getMainLooper()) { msg -> - RootService.unbind(this) - val data = msg.data - val output = if (data.getBoolean("succeed")) context.getString(R.string.please_reboot) else null - callback(!data.getBoolean("error"), data.getBoolean("succeed"), output) - return@Handler true - } - val msg = Message() - msg.replyTo = Messenger(handler) - Messenger(service).send(msg) - } - override fun onServiceDisconnected(name: ComponentName?) {} - } - val intent = Intent(context, ForceActivateService::class.java) - RootService.bind(intent, connection) -} - -@RequiresApi(26) -class ForceActivateService(): RootService() { - override fun onBind(intent: Intent): IBinder = messenger.binder - val handler = Handler(Looper.getMainLooper()) { msg -> - val replyMessage = Message() - try { - replyMessage.data = activateDeviceOwnerAsRoot(getReceiver()).apply { putBoolean("error", false) } - } catch (e: Exception) { - e.printStackTrace() - replyMessage.data = bundleOf("error" to true, "succeed" to false) - } - msg.replyTo.send(replyMessage) - return@Handler false - } - val messenger = Messenger(handler) -} - -@SuppressLint("PrivateApi") -@RequiresApi(26) -fun activateDeviceOwnerAsRoot(cn: ComponentName): Bundle { - val dcl = DexClassLoader( - "/system/framework/services.jar", "/data/local/tmp", null, ClassLoader.getSystemClassLoader() - ) - val ppp = dcl.loadClass("com.android.server.devicepolicy.PolicyPathProvider") - val pppProxy = Proxy.newProxyInstance(ppp.classLoader, arrayOf(ppp)) { obj, method, args -> - method.isAccessible = true - val mh = MethodHandles.lookup().`in`(ppp).unreflectSpecial(method, ppp).bindTo(obj) - return@newProxyInstance if (args == null) { - mh.invokeWithArguments() - } else { - mh.invokeWithArguments(*args) - } - } - val od = dcl.loadClass("com.android.server.devicepolicy.OwnersData") - val odIns = od.getConstructor(ppp).apply { isAccessible = true }.newInstance(pppProxy) - val oi = dcl.loadClass("com.android.server.devicepolicy.OwnersData\$OwnerInfo") - val oiIns = oi.constructors[0].apply { isAccessible = true }.newInstance(cn, null, null, true) - od.getField("mDeviceOwner").apply { isAccessible = true }.set(odIns, oiIns) - od.getField("mDeviceOwnerUserId").apply { isAccessible = true }.set(odIns, 0) - val setDoResult = od.getMethod("writeDeviceOwner").apply { isAccessible = true }.invoke(odIns) as Boolean - return if (setDoResult) { - val proc = Runtime.getRuntime().exec("dpm set-active-admin ${cn.flattenToShortString()}") - proc.waitFor() - bundleOf("succeed" to true) - } else { - bundleOf("succeed" to false) - } -} - fun activateDhizukuMode(context: Context, callback: (Boolean, Boolean, String?) -> Unit) { fun onSucceed() { SharedPrefs(context).dhizuku = true @@ -589,7 +491,7 @@ fun DhizukuServerSettingsScreen(onNavigateUp: () -> Unit) { MyLazyScaffold(R.string.dhizuku_server, onNavigateUp) { item { SwitchItem(R.string.enable, getState = { sp.dhizukuServer }, onCheckedChange = ::changeEnableState) - Spacer(Modifier.padding(vertical = 8.dp)) + HorizontalDivider(Modifier.padding(vertical = 8.dp)) } if (enabled) itemsIndexed(clients) { index, client -> val name = pm.getNameForUid(client.uid) @@ -599,16 +501,13 @@ fun DhizukuServerSettingsScreen(onNavigateUp: () -> Unit) { } else { val info = pm.getApplicationInfo(name, 0) Row( - Modifier - .fillMaxWidth().padding(8.dp) - .background(colorScheme.surfaceVariant, RoundedCornerShape(8.dp)) - .padding(8.dp), + Modifier.fillMaxWidth().padding(HorizontalPadding, 8.dp), Arrangement.SpaceBetween, Alignment.CenterVertically ) { Row(verticalAlignment = Alignment.CenterVertically) { Image( rememberDrawablePainter(info.loadIcon(pm)), null, - Modifier.padding(end = 12.dp).size(50.dp) + Modifier.padding(end = 16.dp).size(50.dp) ) Text(info.loadLabel(pm).toString(), style = typography.titleLarge) } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt index 817284d..df88114 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt @@ -47,8 +47,10 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -1104,7 +1106,8 @@ fun LockTaskModeScreen(onNavigateUp: () -> Unit) { navigationIcon = { NavIcon(onNavigateUp) }, colors = TopAppBarDefaults.topAppBarColors(colorScheme.surfaceContainer) ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> Column( modifier = Modifier @@ -1405,7 +1408,8 @@ fun CaCertScreen(onNavigateUp: () -> Unit) { }) { Icon(Icons.Default.Add, stringResource(R.string.install)) } - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> LazyColumn( Modifier diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt index b92edc2..7555335 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt @@ -12,8 +12,11 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -101,7 +104,8 @@ fun UserRestrictionScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) { }, scrollBehavior = sb ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> Column( modifier = Modifier @@ -299,7 +303,8 @@ fun UserRestrictionEditorScreen(onNavigateUp: () -> Unit) { title = { Text(stringResource(R.string.edit)) }, navigationIcon = { NavIcon(onNavigateUp) } ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> LazyColumn(Modifier.fillMaxSize().padding(paddingValues)) { items(list, { it }) { diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt index a82681f..51447c1 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt @@ -10,8 +10,10 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -294,7 +296,8 @@ fun MyScaffold( navigationIcon = { NavIcon(onNavIconClicked) }, scrollBehavior = sb ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> Column( modifier = Modifier @@ -325,7 +328,8 @@ fun MyLazyScaffold( navigationIcon = { NavIcon(onNavIconClicked) }, scrollBehavior = sb ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> LazyColumn(Modifier.fillMaxSize().padding(paddingValues), content = content) } @@ -346,7 +350,8 @@ fun MySmallTitleScaffold( navigationIcon = { NavIcon(onNavIconClicked) }, colors = TopAppBarDefaults.topAppBarColors(colorScheme.surfaceContainer) ) - } + }, + contentWindowInsets = WindowInsets.ime ) { paddingValues -> Column( modifier = Modifier diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/theme/Theme.kt b/app/src/main/java/com/bintianqi/owndroid/ui/theme/Theme.kt index 0a546a0..3e72f83 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ui/theme/Theme.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ui/theme/Theme.kt @@ -3,7 +3,11 @@ package com.bintianqi.owndroid.ui.theme import android.app.Activity import android.os.Build.VERSION import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color @@ -106,7 +110,7 @@ fun OwnDroidTheme( } val view = LocalView.current SideEffect { - val window = (view.context as Activity).window + val window = (context as Activity).window WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme } MaterialTheme( diff --git a/app/src/main/res/drawable/swap_horiz_fill0.xml b/app/src/main/res/drawable/swap_horiz_fill0.xml new file mode 100644 index 0000000..9373a42 --- /dev/null +++ b/app/src/main/res/drawable/swap_horiz_fill0.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 7d3469b..c1316b2 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -693,13 +693,10 @@ 设置后,用户将无法输入与历史记录中任何密码相同的新密码。当前密码将保留,直到用户设置新密码为止,因此更改不会立即生效。值为0表示不做限制。 如果用户在这段时间内没有使用强认证(密码、PIN或图案)解锁设备,则要求使用强认证解锁设备。值为0表示OwnDroid不参与控制超时。一般来说,最少1小时,最多72小时。 OwnDroid将会丢失特权 - 强行激活可能无法在你的设备上使用,它甚至有可能会损坏你的设备,你应该仅在其他方法无法使用时尝试此方法。 选择一个工作模式 推荐 激活方法 ADB命令 - Root (强行激活) 此应用使用Device owner或Profile owner特权。这些特权十分危险,请谨慎使用。如果操作不当,可能会造成严重损失。开发者将不会对此负责。 - 请重启你的设备 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 04ba9d9..83d99c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -727,13 +727,10 @@ After setting this, the user will not be able to enter a new password that is the same as any password in the history. Note that the current password will remain until the user has set a new one, so the change does not take place immediately.\nA value of 0 means there is no restriction. Determine for how long the user will be able to use secondary, non strong auth for authentication, since last strong method authentication (password, pin or pattern) was used. After the returned timeout the user is required to use strong authentication method.\nA value of 0 means the admin is not participating in controlling the timeout. The minimum and maximum timeouts are platform-defined and are typically 1 hour and 72 hours, respectively. OwnDroid will lost its privilege - Force activation may not work and it may damage your device. This method should only be used when you can\'t use other methods. Choose a work mode Recommended Activate method ADB command - Root (force activate) This app uses Device owner or Profile owner privileges. These privileges are extremely dangerous, please use them with caution. If used improperly, they may result in severe losses. The developers will not be responsible for this. - Please reboot your device diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 7ed11ac..fa3d4e3 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,8 +1,6 @@