diff --git a/Readme-en.md b/Readme-en.md index 85f8c35..38c642b 100644 --- a/Readme-en.md +++ b/Readme-en.md @@ -4,9 +4,7 @@ ### Description -Use Device admin and Device owner privilege to take full control of your device. - -This app is renamed to "OwnDroid" from "Android Owner". +Use Device admin and Device owner privilege to take full control of your device. ### Docs diff --git a/Readme.md b/Readme.md index 8f6e279..9473621 100644 --- a/Readme.md +++ b/Readme.md @@ -6,8 +6,6 @@ 使用安卓的Device Admin和Device Owner特权,完全掌控你的设备。 -这个APP已经改名为"OwnDroid",原名"Android Owner" - ### 文档 全新的OwnDroid文档:[owndroid.pages.dev](https://owndroid.pages.dev/zh_CN) diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt index c5d750a..c694454 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt @@ -49,6 +49,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TextField import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableIntState @@ -131,7 +132,8 @@ fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState) singleLine = true ) }, - navigationIcon = { NavIcon { navCtrl.navigateUp() } } + navigationIcon = { NavIcon { navCtrl.navigateUp() } }, + colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.background) ) } ) { paddingValues-> diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt index 2c6fca0..e43e928 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt @@ -1,10 +1,13 @@ package com.bintianqi.owndroid.dpm +import android.accounts.Account import android.annotation.SuppressLint import android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE +import android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE import android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ALLOW_OFFLINE import android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME import android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME +import android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION import android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION import android.app.admin.DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT import android.app.admin.DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED @@ -43,6 +46,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource @@ -126,14 +131,48 @@ private fun Home(navCtrl: NavHostController) { private fun CreateWorkProfile() { val context = LocalContext.current val receiver = context.getReceiver() + val focusMgr = LocalFocusManager.current Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.create_work_profile), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) var skipEncrypt by remember { mutableStateOf(false) } - if(VERSION.SDK_INT>=24) { + var offlineProvisioning by remember { mutableStateOf(true) } + var migrateAccount by remember { mutableStateOf(false) } + var migrateAccountName by remember { mutableStateOf("") } + var migrateAccountType by remember { mutableStateOf("") } + var keepAccount by remember { mutableStateOf(true) } + if(VERSION.SDK_INT >= 22) { + CheckBoxItem(R.string.migrate_account, migrateAccount, { migrateAccount = it }) + AnimatedVisibility(migrateAccount) { + val fr = FocusRequester() + Column(modifier = Modifier.padding(start = 10.dp)) { + OutlinedTextField( + value = migrateAccountName, onValueChange = { migrateAccountName = it }, + label = { Text(stringResource(R.string.account_name)) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + keyboardActions = KeyboardActions { fr.requestFocus() }, + modifier = Modifier.fillMaxWidth() + ) + OutlinedTextField( + value = migrateAccountType, onValueChange = { migrateAccountType = it }, + label = { Text(stringResource(R.string.account_type)) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions { focusMgr.clearFocus() }, + modifier = Modifier.fillMaxWidth().focusRequester(fr) + ) + if(VERSION.SDK_INT >= 26) { + CheckBoxItem(R.string.keep_account, keepAccount, { keepAccount = it }) + } + } + } + } + if(VERSION.SDK_INT >= 24) { CheckBoxItem(R.string.skip_encryption, skipEncrypt, { skipEncrypt = it }) } + if(VERSION.SDK_INT >= 33) { + CheckBoxItem(R.string.offline_provisioning, offlineProvisioning, { offlineProvisioning = it }) + } Spacer(Modifier.padding(vertical = 5.dp)) Button( onClick = { @@ -141,13 +180,19 @@ private fun CreateWorkProfile() { val intent = Intent(ACTION_PROVISION_MANAGED_PROFILE) if(VERSION.SDK_INT>=23) { intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,receiver) - }else{ + } else { intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, context.packageName) } - if(VERSION.SDK_INT>=24) { intent.putExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION,skipEncrypt) } - if(VERSION.SDK_INT>=33) { intent.putExtra(EXTRA_PROVISIONING_ALLOW_OFFLINE,true) } + if(migrateAccount && VERSION.SDK_INT >= 22) { + intent.putExtra(EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE, Account(migrateAccountName, migrateAccountType)) + if(VERSION.SDK_INT >= 26) { + intent.putExtra(EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION, keepAccount) + } + } + if(VERSION.SDK_INT >= 24) { intent.putExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION, skipEncrypt) } + if(VERSION.SDK_INT >= 33) { intent.putExtra(EXTRA_PROVISIONING_ALLOW_OFFLINE, offlineProvisioning) } createManagedProfile.launch(intent) - }catch(e:ActivityNotFoundException) { + } catch(e:ActivityNotFoundException) { Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() } }, diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt index ae509e2..43e1d84 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt @@ -546,7 +546,11 @@ private fun MaxFailedPasswordForWipe() { ) Spacer(Modifier.padding(vertical = 5.dp)) Button( - onClick = { focusMgr.clearFocus(); dpm.setMaximumFailedPasswordsForWipe(receiver,inputContent.toInt()) }, + onClick = { + focusMgr.clearFocus() + dpm.setMaximumFailedPasswordsForWipe(receiver, inputContent.toInt()) + Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() + }, modifier = Modifier.fillMaxWidth(), enabled = inputContent != "" ) { 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 f6dc8e7..cf3e50d 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -454,14 +454,14 @@ fun DeviceInfo() { ) if(VERSION.SDK_INT >= 23) { encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY] = stringResource(R.string.es_active_default_key) } if(VERSION.SDK_INT >= 24) { encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER] = stringResource(R.string.es_active_per_user) } - Text(stringResource(R.string.encrypt_status_is)+encryptionStatus[dpm.storageEncryptionStatus]) + Text(stringResource(R.string.encrypt_status_is, encryptionStatus[dpm.storageEncryptionStatus] ?: "")) Spacer(Modifier.padding(vertical = 2.dp)) if(VERSION.SDK_INT >= 28) { - Text(stringResource(R.string.support_device_id_attestation) + dpm.isDeviceIdAttestationSupported) + Text(stringResource(R.string.support_device_id_attestation, dpm.isDeviceIdAttestationSupported)) } Spacer(Modifier.padding(vertical = 2.dp)) if (VERSION.SDK_INT >= 30) { - Text(stringResource(R.string.support_unique_device_attestation) + dpm.isUniqueDeviceAttestationSupported) + Text(stringResource(R.string.support_unique_device_attestation, dpm.isUniqueDeviceAttestationSupported)) } Spacer(Modifier.padding(vertical = 2.dp)) val adminList = dpm.activeAdmins @@ -658,7 +658,7 @@ private fun DisableAccountManagement() { OutlinedTextField( value = inputText, onValueChange = { inputText = it }, - label = { Text(stringResource(R.string.account_types_are)) }, + label = { Text(stringResource(R.string.account_type)) }, modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }) diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt index 66f38cf..968b457 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt @@ -1112,7 +1112,7 @@ fun FactoryResetProtection() { AnimatedVisibility(usePolicy) { Column { CheckBoxItem(R.string.enable_frp, enabled, { enabled = it }) - Text(stringResource(R.string.account_list_is)) + Text(stringResource(R.string.account_list_is) + "\n") Text( text = if(accountList.isEmpty()) stringResource(R.string.none) else accountList.joinToString(separator = "\n"), modifier = Modifier.animateContentSize() diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt index 70f03dc..4ead168 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt @@ -41,7 +41,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState 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 @@ -66,9 +65,9 @@ import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.getFile +import com.bintianqi.owndroid.toggle import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem -import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.TopBar import com.bintianqi.owndroid.uriToStream @@ -290,6 +289,7 @@ private fun CreateUser() { val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current var userName by remember { mutableStateOf("") } + val flags = remember { mutableStateListOf() } Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.create_user), style = typography.headlineLarge) @@ -303,31 +303,29 @@ private fun CreateUser() { keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }) ) Spacer(Modifier.padding(vertical = 5.dp)) - var selectedFlag by remember { mutableIntStateOf(0) } - RadioButtonItem(R.string.none, selectedFlag == 0, { selectedFlag = 0 }) - RadioButtonItem( + CheckBoxItem( R.string.create_user_skip_wizard, - selectedFlag == DevicePolicyManager.SKIP_SETUP_WIZARD, - { selectedFlag = DevicePolicyManager.SKIP_SETUP_WIZARD } + DevicePolicyManager.SKIP_SETUP_WIZARD in flags, + { flags.toggle(it, DevicePolicyManager.SKIP_SETUP_WIZARD) } ) if(VERSION.SDK_INT >= 28) { - RadioButtonItem( + CheckBoxItem( R.string.create_user_ephemeral_user, - selectedFlag == DevicePolicyManager.MAKE_USER_EPHEMERAL, - { selectedFlag = DevicePolicyManager.MAKE_USER_EPHEMERAL } + DevicePolicyManager.MAKE_USER_EPHEMERAL in flags, + { flags.toggle(it, DevicePolicyManager.MAKE_USER_EPHEMERAL) } ) - RadioButtonItem( + CheckBoxItem( R.string.create_user_enable_all_system_app, - selectedFlag == DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED, - { selectedFlag = DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED } + DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED in flags, + { flags.toggle(it, DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED) } ) } var newUserHandle: UserHandle? by remember { mutableStateOf(null) } Spacer(Modifier.padding(vertical = 5.dp)) Button( onClick = { - newUserHandle = dpm.createAndManageUser(receiver, userName, receiver, null, selectedFlag) focusMgr.clearFocus() + newUserHandle = dpm.createAndManageUser(receiver, userName, receiver, null, flags.sum()) Toast.makeText(context, if(newUserHandle!=null) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show() }, modifier = Modifier.fillMaxWidth() diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c406269..7f345be 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -66,8 +66,8 @@ Reset device policy Cihaz Yöneticisini Etkinleştir Cihaz Bilgisi - Cihaz Kimliği Doğrulama Desteği: - Benzersiz Cihaz Doğrulama Desteği: + Cihaz Kimliği Doğrulama Desteği: %1$s + Benzersiz Cihaz Doğrulama Desteği: %1$s Finansmanlı Cihaz: %1$s Cihaz Politikası Yönetim Rolü: %1$s @@ -81,7 +81,7 @@ Kayıt Özel Kimliği Organizasyon Adı Hesap Yönetimini Devre Dışı Bırak - Hesap Türleri: + Hesap Türleri Sahipliği Devret Cihaz sahibi veya profil sahibi ayrıcalığını başka bir uygulamaya devredin. Hedef Paket Adı @@ -89,7 +89,6 @@ Ekran Kilidi Bilgisi Destek Mesajı Kısa Mesaj - Bu uygulama tarafından devre dışı bırakılan işlevleri kullanmaya çalışan kullanıcılara destek mesajı gösterilir. (Çok satırlı izin verilir) Uzun Mesaj Transfer Cihaz Yöneticisini Burada Etkinleştir. @@ -186,12 +185,12 @@ Sessizce sil All data on your device will be ERASED Your work profile will be DELETED - Şifreleme durumu: + Şifreleme durumu: %1$s FRP politikası Fabrika ayarlarına sıfırlama koruma politikası FRP politikası bu cihazda desteklenmiyor FRP\'yi etkinleştir - Hesap listesi: + "Hesap listesi: " Güvenlik yaması: %1$s @@ -207,7 +206,7 @@ Sistem güncellemesini yükle OTA paketini seç... Sistem güncellemesi yüklemeye başla - Sistem güncellemesi yükleme başarısız: + "Sistem güncellemesi yükleme başarısız: " Pil zayıf Güncelleme dosyası geçersiz Yanlış işletim sistemi sürümü @@ -221,7 +220,7 @@ Tercihli ağ hizmeti Yönetici tarafından yapılandırılmış ağı kilitle WiFi SSID politikası - SSID listesi: + SSID listesi: Boş olamaz Zaten mevcut Özel DNS @@ -273,6 +272,10 @@ Profil sahibi (İş profili) İş profili etkinleştirildi İş profili oluştur + Offline mode + Migrate account + Account name + Keep account Kuruluşa ait iş profili: %1$s Kuruluş iş profili Şifrelemeyi atla @@ -313,7 +316,7 @@ Kullanıcı kontrolünü devre dışı bırak Bunu ayarlarsanız, bu uygulamaların depolama alanını temizleyemez veya zorla durduramazsınız. - Uygulama listesi: + Uygulama listesi: Listeyi temizle İzin yönetimi Profil geçişli paket @@ -463,7 +466,6 @@ Bağlılık ID Boş string dahil et Kullanıcı simgesini değiştir - Kare bir resim seçmelisiniz Galeri yerine dosya seçici kullan Resim seç... Bilinmeyen sonuç (başarısız olabilir) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b769131..3e5cf55 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -63,8 +63,8 @@ Dhizuku将被停用 激活Device admin 设备信息 - 支持设备ID认证: - 支持唯一设备认证: + 支持设备ID认证:%1$s + 支持唯一设备认证:%1$s Financed device: %1$s 设备策略管理器角色(DPMRH):%1$s 未使用 @@ -76,7 +76,7 @@ 设备注册专用ID 组织名称 禁用账号管理 - 账号类型: + 账号类型 转移所有权 把Device owner或Profile owner权限转移到另一个应用 目标包名 @@ -84,7 +84,6 @@ 锁屏提示信息 提供支持的消息 提供支持的短消息 - 如果你禁用了某个功能,用户尝试使用这个功能时会看见这个消息(可多行) 提供支持的长消息 转移 在这里激活Device admin @@ -181,7 +180,7 @@ 静默清除 你的设备上的所有数据将会被清除 你的工作资料将会被删除 - 加密状态: + 加密状态:%1$s FRP策略 恢复出厂设置保护策略 这个设备不支持恢复出厂设置保护策略 @@ -268,6 +267,10 @@ Profile owner(工作资料) 工作资料已激活 创建工作资料 + 离线模式 + 迁移账号 + 账号名 + 保留账号 由组织拥有的工作资料:%1$s 组织拥有的工作资料 跳过加密 @@ -455,7 +458,6 @@ 附属用户ID 有空字符串 更换用户头像 - 尽量选择正方形的图片,以免产生问题 使用文件选择器而不是相册 选择图片... 未知结果(失败) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3be6e3f..6f13500 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -68,8 +68,8 @@ dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver Device info - Support Device ID attestation: - Support unique device attestation: + Support Device ID attestation: %1$s + Support unique device attestation: %1$s Financed device: %1$s Device policy management role holder: %1$s @@ -82,7 +82,7 @@ Enrollment specific ID Organization name Disable account management - Account types: + Account type Transfer Ownership Transfer device owner or profile owner privilege to another app. Target package name @@ -90,7 +90,6 @@ Lockscreen info Support Message Short message - When users try to use functions disabled by this app, the support message will show. (Multi-line allowed) Long message Transfer Activate Device admin here. @@ -189,12 +188,12 @@ Wipe silently All data on your device will be ERASED Your work profile will be DELETED - Encrypt status: + Encrypt status: %1$s FRP policy Factory reset protection policy FRP policy is not supported on this device Enable FRP - Account list: + "Account list: " Security patch: %1$s @@ -211,7 +210,7 @@ Install system update Select OTA package... Start installing system update - Install system update failed: + "Install system update failed: " Battery is low Update file is invalid Incorrect OS version @@ -225,7 +224,7 @@ Preferential network service Lockdown admin configured network WiFi SSID policy - SSID list: + SSID list: Cannot be empty Already exist Private DNS @@ -277,6 +276,10 @@ Profile owner (Work profile) Work profile activated Create work profile + Offline mode + Migrate account + Account name + Keep account Organization owned work profile: %1$s Organization work profile @@ -319,7 +322,7 @@ Disable user control If you set this, you cannot clear these apps\' storage or force stop them. - App list: + App list: Clear list Permission manage Cross profile package @@ -468,7 +471,6 @@ Affiliation ID Include empty string Change user icon - You should pick a square image Use file picker instead of gallery Select image... Unknown result(may failed)