mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-27 12:26:11 +00:00
ViewModel refactoring: Users part
Bugfix and improvement (#166, #174, #177, #178)
This commit is contained in:
40
Readme-en.md
40
Readme-en.md
@@ -93,27 +93,25 @@ Samsung restricts Android's multiple users feature. There is currently no soluti
|
|||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
OwnDroid provides an API based on Intent and BroadcastReceiver.
|
OwnDroid provides an Intent-based API. You need to set the API key in settings and enable the API. The numbers in brackets represent the minimum Android version required.
|
||||||
|
|
||||||
| ID | Extras | Minimum Android version |
|
- HIDE(package: String)
|
||||||
|--------------------------|------------------------|:-----------------------:|
|
- UNHIDE(package: String)
|
||||||
| `HIDE` | `package` | |
|
- SUSPEND(package: String) (7)
|
||||||
| `UNHIDE` | `package` | |
|
- UNSUSPEND(package: String) (7)
|
||||||
| `SUSPEND` | `package` | 7 |
|
- ADD_USER_RESTRICTION(restriction: Boolean)
|
||||||
| `UNSUSPEND` | `package` | 7 |
|
- CLEAR_USER_RESTRICTION(restriction: Boolean)
|
||||||
| `ADD_USER_RESTRICTION` | `restriction` | |
|
- SET_PERMISSION_DEFAULT(package: String, permission: String) (6)
|
||||||
| `CLEAR_USER_RESTRICTION` | `restriction` | |
|
- SET_PERMISSION_GRANTED(package: String, permission: String) (6)
|
||||||
| `SET_PERMISSION_DEFAULT` | `package` `permission` | 6 |
|
- SET_PERMISSION_DENIED(package: String, permission: String) (6)
|
||||||
| `SET_PERMISSION_GRANTED` | `package` `permission` | 6 |
|
- SET_SCREEN_CAPTURE_DISABLED()
|
||||||
| `SET_PERMISSION_DENIED` | `package` `permission` | 6 |
|
- SET_SCREEN_CAPTURE_ENABLED()
|
||||||
| `SET_CAMERA_DISABLED` | | |
|
- SET_CAMERA_DISABLED()
|
||||||
| `SET_CAMERA_ENABLED` | | |
|
- SET_CAMERA_ENABLED()
|
||||||
| `SET_USB_DISABLED` | | 12 |
|
- SET_USB_DISABLED() (12)
|
||||||
| `SET_USB_ENABLED` | | 12 |
|
- SET_USB_ENABLED() (12)
|
||||||
| `LOCK` | | |
|
- LOCK()
|
||||||
| `REBOOT` | | 7 |
|
- REBOOT() (7)
|
||||||
|
|
||||||
[Available user restrictions](https://developer.android.com/reference/android/os/UserManager#constants_1)
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# An example of hiding app in ADB shell
|
# An example of hiding app in ADB shell
|
||||||
@@ -129,6 +127,8 @@ val intent = Intent("com.bintianqi.owndroid.action.HIDE")
|
|||||||
context.sendBroadcast(intent)
|
context.sendBroadcast(intent)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[Available user restrictions](https://developer.android.com/reference/android/os/UserManager#constants_1)
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
You can use Gradle in command line to build OwnDroid.
|
You can use Gradle in command line to build OwnDroid.
|
||||||
|
|||||||
40
Readme.md
40
Readme.md
@@ -91,27 +91,25 @@ user limit reached
|
|||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
OwnDroid提供了一个基于Intent和BroadcastReceiver的API。
|
OwnDroid提供了一个基于Intent的API。你需要在设置中设置密钥并启用API。括号中的数字是最小的安卓版本。
|
||||||
|
|
||||||
| ID | Extra | 最小安卓版本 |
|
- HIDE(package: String)
|
||||||
|--------------------------|------------------------|:------:|
|
- UNHIDE(package: String)
|
||||||
| `HIDE` | `package` | |
|
- SUSPEND(package: String) (7)
|
||||||
| `UNHIDE` | `package` | |
|
- UNSUSPEND(package: String) (7)
|
||||||
| `SUSPEND` | `package` | 7 |
|
- ADD_USER_RESTRICTION(restriction: Boolean)
|
||||||
| `UNSUSPEND` | `package` | 7 |
|
- CLEAR_USER_RESTRICTION(restriction: Boolean)
|
||||||
| `ADD_USER_RESTRICTION` | `restriction` | |
|
- SET_PERMISSION_DEFAULT(package: String, permission: String) (6)
|
||||||
| `CLEAR_USER_RESTRICTION` | `restriction` | |
|
- SET_PERMISSION_GRANTED(package: String, permission: String) (6)
|
||||||
| `SET_PERMISSION_DEFAULT` | `package` `permission` | 6 |
|
- SET_PERMISSION_DENIED(package: String, permission: String) (6)
|
||||||
| `SET_PERMISSION_GRANTED` | `package` `permission` | 6 |
|
- SET_SCREEN_CAPTURE_DISABLED()
|
||||||
| `SET_PERMISSION_DENIED` | `package` `permission` | 6 |
|
- SET_SCREEN_CAPTURE_ENABLED()
|
||||||
| `SET_CAMERA_DISABLED` | | |
|
- SET_CAMERA_DISABLED()
|
||||||
| `SET_CAMERA_ENABLED` | | |
|
- SET_CAMERA_ENABLED()
|
||||||
| `SET_USB_DISABLED` | | 12 |
|
- SET_USB_DISABLED() (12)
|
||||||
| `SET_USB_ENABLED` | | 12 |
|
- SET_USB_ENABLED() (12)
|
||||||
| `LOCK` | | |
|
- LOCK()
|
||||||
| `REBOOT` | | 7 |
|
- REBOOT() (7)
|
||||||
|
|
||||||
[可用的用户限制](https://developer.android.google.cn/reference/android/os/UserManager#constants_1)
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# 一个在ADB shell中隐藏app的示例
|
# 一个在ADB shell中隐藏app的示例
|
||||||
@@ -127,6 +125,8 @@ val intent = Intent("com.bintianqi.owndroid.action.HIDE")
|
|||||||
context.sendBroadcast(intent)
|
context.sendBroadcast(intent)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[可用的用户限制](https://developer.android.google.cn/reference/android/os/UserManager#constants_1)
|
||||||
|
|
||||||
## 构建
|
## 构建
|
||||||
|
|
||||||
你可以在命令行中使用Gradle以构建OwnDroid
|
你可以在命令行中使用Gradle以构建OwnDroid
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ class ApiReceiver: BroadcastReceiver() {
|
|||||||
if (!permission.isNullOrEmpty()) log += "\npermission: $permission"
|
if (!permission.isNullOrEmpty()) log += "\npermission: $permission"
|
||||||
try {
|
try {
|
||||||
@SuppressWarnings("NewApi")
|
@SuppressWarnings("NewApi")
|
||||||
val ok = when(intent.action?.removePrefix("com.bintianqi.owndroid.action.")) {
|
when(intent.action?.removePrefix("com.bintianqi.owndroid.action.")) {
|
||||||
"HIDE" -> Privilege.DPM.setApplicationHidden(Privilege.DAR, app, true)
|
"HIDE" -> Privilege.DPM.setApplicationHidden(Privilege.DAR, app, true)
|
||||||
"UNHIDE" -> Privilege.DPM.setApplicationHidden(Privilege.DAR, app, false)
|
"UNHIDE" -> Privilege.DPM.setApplicationHidden(Privilege.DAR, app, false)
|
||||||
"SUSPEND" -> Privilege.DPM.setPackagesSuspended(Privilege.DAR, arrayOf(app), true).isEmpty()
|
"SUSPEND" -> Privilege.DPM.setPackagesSuspended(Privilege.DAR, arrayOf(app), true)
|
||||||
"UNSUSPEND" -> Privilege.DPM.setPackagesSuspended(Privilege.DAR, arrayOf(app), false).isEmpty()
|
"UNSUSPEND" -> Privilege.DPM.setPackagesSuspended(Privilege.DAR, arrayOf(app), false)
|
||||||
"ADD_USER_RESTRICTION" -> { Privilege.DPM.addUserRestriction(Privilege.DAR, restriction); true }
|
"ADD_USER_RESTRICTION" -> { Privilege.DPM.addUserRestriction(Privilege.DAR, restriction) }
|
||||||
"CLEAR_USER_RESTRICTION" -> { Privilege.DPM.clearUserRestriction(Privilege.DAR, restriction); true }
|
"CLEAR_USER_RESTRICTION" -> { Privilege.DPM.clearUserRestriction(Privilege.DAR, restriction) }
|
||||||
"SET_PERMISSION_DEFAULT" -> {
|
"SET_PERMISSION_DEFAULT" -> {
|
||||||
Privilege.DPM.setPermissionGrantState(
|
Privilege.DPM.setPermissionGrantState(
|
||||||
Privilege.DAR, app!!, permission!!,
|
Privilege.DAR, app!!, permission!!,
|
||||||
@@ -45,30 +45,31 @@ class ApiReceiver: BroadcastReceiver() {
|
|||||||
DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|
DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
"LOCK" -> { Privilege.DPM.lockNow(); true }
|
"LOCK" -> { Privilege.DPM.lockNow() }
|
||||||
"REBOOT" -> { Privilege.DPM.reboot(Privilege.DAR); true }
|
"REBOOT" -> { Privilege.DPM.reboot(Privilege.DAR) }
|
||||||
"SET_CAMERA_DISABLED" -> {
|
"SET_CAMERA_DISABLED" -> {
|
||||||
Privilege.DPM.setCameraDisabled(Privilege.DAR, true)
|
Privilege.DPM.setCameraDisabled(Privilege.DAR, true)
|
||||||
true
|
|
||||||
}
|
}
|
||||||
"SET_CAMERA_ENABLED" -> {
|
"SET_CAMERA_ENABLED" -> {
|
||||||
Privilege.DPM.setCameraDisabled(Privilege.DAR, false)
|
Privilege.DPM.setCameraDisabled(Privilege.DAR, false)
|
||||||
true
|
|
||||||
}
|
}
|
||||||
"SET_USB_DISABLED" -> {
|
"SET_USB_DISABLED" -> {
|
||||||
Privilege.DPM.isUsbDataSignalingEnabled = false
|
Privilege.DPM.isUsbDataSignalingEnabled = false
|
||||||
true
|
|
||||||
}
|
}
|
||||||
"SET_USB_ENABLED" -> {
|
"SET_USB_ENABLED" -> {
|
||||||
Privilege.DPM.isUsbDataSignalingEnabled = true
|
Privilege.DPM.isUsbDataSignalingEnabled = true
|
||||||
true
|
}
|
||||||
|
"SET_SCREEN_CAPTURE_DISABLED" -> {
|
||||||
|
Privilege.DPM.setScreenCaptureDisabled(Privilege.DAR, true)
|
||||||
|
}
|
||||||
|
"SET_SCREEN_CAPTURE_ENABLED" -> {
|
||||||
|
Privilege.DPM.setScreenCaptureDisabled(Privilege.DAR, false)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
log += "\nInvalid action"
|
log += "\nInvalid action"
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log += "\nsuccess: $ok"
|
|
||||||
} catch(e: Exception) {
|
} catch(e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
val message = (e::class.qualifiedName ?: "Exception") + ": " + (e.message ?: "")
|
val message = (e::class.qualifiedName ?: "Exception") + ": " + (e.message ?: "")
|
||||||
|
|||||||
@@ -547,14 +547,24 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
|||||||
vm::setUserRestriction, ::navigateUp)
|
vm::setUserRestriction, ::navigateUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Users> { UsersScreen(::navigateUp, ::navigate) }
|
composable<Users> { UsersScreen(vm, ::navigateUp, ::navigate) }
|
||||||
composable<UserInfo> { UserInfoScreen(::navigateUp) }
|
composable<UserInfo> { UserInfoScreen(vm::getUserInformation, ::navigateUp) }
|
||||||
composable<UsersOptions> { UsersOptionsScreen(::navigateUp) }
|
composable<UsersOptions> {
|
||||||
composable<UserOperation> { UserOperationScreen(::navigateUp) }
|
UsersOptionsScreen(vm::getLogoutEnabled, vm::setLogoutEnabled, ::navigateUp)
|
||||||
composable<CreateUser> { CreateUserScreen(::navigateUp) }
|
}
|
||||||
composable<ChangeUsername> { ChangeUsernameScreen(::navigateUp) }
|
composable<UserOperation> {
|
||||||
composable<UserSessionMessage> { UserSessionMessageScreen(::navigateUp) }
|
UserOperationScreen(vm::startUser, vm::switchUser, vm::stopUser, vm::deleteUser, ::navigateUp)
|
||||||
composable<AffiliationId> { AffiliationIdScreen(::navigateUp) }
|
}
|
||||||
|
composable<CreateUser> { CreateUserScreen(vm::createUser, ::navigateUp) }
|
||||||
|
composable<ChangeUsername> { ChangeUsernameScreen(vm::setProfileName, ::navigateUp) }
|
||||||
|
composable<UserSessionMessage> {
|
||||||
|
UserSessionMessageScreen(vm::getUserSessionMessages, vm::setStartUserSessionMessage,
|
||||||
|
vm::setEndUserSessionMessage, ::navigateUp)
|
||||||
|
}
|
||||||
|
composable<AffiliationId> {
|
||||||
|
AffiliationIdScreen(vm.affiliationIds, vm::getAffiliationIds, vm::setAffiliationId,
|
||||||
|
::navigateUp)
|
||||||
|
}
|
||||||
|
|
||||||
composable<Password> { PasswordScreen(::navigateUp, ::navigate) }
|
composable<Password> { PasswordScreen(::navigateUp, ::navigate) }
|
||||||
composable<PasswordInfo> { PasswordInfoScreen(::navigateUp) }
|
composable<PasswordInfo> { PasswordInfoScreen(::navigateUp) }
|
||||||
|
|||||||
@@ -20,9 +20,13 @@ import android.content.IntentFilter
|
|||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Binder
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
import android.os.HardwarePropertiesManager
|
import android.os.HardwarePropertiesManager
|
||||||
|
import android.os.UserHandle
|
||||||
|
import android.os.UserManager
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
@@ -36,6 +40,7 @@ import com.bintianqi.owndroid.Privilege.DPM
|
|||||||
import com.bintianqi.owndroid.dpm.ACTIVATE_DEVICE_OWNER_COMMAND
|
import com.bintianqi.owndroid.dpm.ACTIVATE_DEVICE_OWNER_COMMAND
|
||||||
import com.bintianqi.owndroid.dpm.AppStatus
|
import com.bintianqi.owndroid.dpm.AppStatus
|
||||||
import com.bintianqi.owndroid.dpm.CaCertInfo
|
import com.bintianqi.owndroid.dpm.CaCertInfo
|
||||||
|
import com.bintianqi.owndroid.dpm.CreateUserResult
|
||||||
import com.bintianqi.owndroid.dpm.CreateWorkProfileOptions
|
import com.bintianqi.owndroid.dpm.CreateWorkProfileOptions
|
||||||
import com.bintianqi.owndroid.dpm.DelegatedAdmin
|
import com.bintianqi.owndroid.dpm.DelegatedAdmin
|
||||||
import com.bintianqi.owndroid.dpm.DeviceAdmin
|
import com.bintianqi.owndroid.dpm.DeviceAdmin
|
||||||
@@ -46,6 +51,7 @@ import com.bintianqi.owndroid.dpm.IntentFilterOptions
|
|||||||
import com.bintianqi.owndroid.dpm.PendingSystemUpdateInfo
|
import com.bintianqi.owndroid.dpm.PendingSystemUpdateInfo
|
||||||
import com.bintianqi.owndroid.dpm.SystemOptionsStatus
|
import com.bintianqi.owndroid.dpm.SystemOptionsStatus
|
||||||
import com.bintianqi.owndroid.dpm.SystemUpdatePolicyInfo
|
import com.bintianqi.owndroid.dpm.SystemUpdatePolicyInfo
|
||||||
|
import com.bintianqi.owndroid.dpm.UserInformation
|
||||||
import com.bintianqi.owndroid.dpm.activateOrgProfileCommand
|
import com.bintianqi.owndroid.dpm.activateOrgProfileCommand
|
||||||
import com.bintianqi.owndroid.dpm.delegatedScopesList
|
import com.bintianqi.owndroid.dpm.delegatedScopesList
|
||||||
import com.bintianqi.owndroid.dpm.getPackageInstaller
|
import com.bintianqi.owndroid.dpm.getPackageInstaller
|
||||||
@@ -68,6 +74,8 @@ import java.security.MessageDigest
|
|||||||
import java.security.cert.CertificateException
|
import java.security.cert.CertificateException
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.ZonedDateTime
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
class MyViewModel(application: Application): AndroidViewModel(application) {
|
class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||||
@@ -124,12 +132,10 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
|
|
||||||
val hiddenPackages = MutableStateFlow(emptyList<AppInfo>())
|
val hiddenPackages = MutableStateFlow(emptyList<AppInfo>())
|
||||||
fun getHiddenPackages() {
|
fun getHiddenPackages() {
|
||||||
viewModelScope.launch {
|
|
||||||
hiddenPackages.value = PM.getInstalledApplications(getInstalledAppsFlags).filter {
|
hiddenPackages.value = PM.getInstalledApplications(getInstalledAppsFlags).filter {
|
||||||
DPM.isApplicationHidden(DAR, it.packageName)
|
DPM.isApplicationHidden(DAR, it.packageName)
|
||||||
}.map { getAppInfo(it) }
|
}.map { getAppInfo(it) }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
fun setPackageHidden(name: String, status: Boolean): Boolean {
|
fun setPackageHidden(name: String, status: Boolean): Boolean {
|
||||||
val result = DPM.setApplicationHidden(DAR, name, status)
|
val result = DPM.setApplicationHidden(DAR, name, status)
|
||||||
getHiddenPackages()
|
getHiddenPackages()
|
||||||
@@ -139,12 +145,10 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
// Uninstall blocked packages
|
// Uninstall blocked packages
|
||||||
val ubPackages = MutableStateFlow(emptyList<AppInfo>())
|
val ubPackages = MutableStateFlow(emptyList<AppInfo>())
|
||||||
fun getUbPackages() {
|
fun getUbPackages() {
|
||||||
viewModelScope.launch {
|
|
||||||
ubPackages.value = PM.getInstalledApplications(getInstalledAppsFlags).filter {
|
ubPackages.value = PM.getInstalledApplications(getInstalledAppsFlags).filter {
|
||||||
DPM.isUninstallBlocked(DAR, it.packageName)
|
DPM.isUninstallBlocked(DAR, it.packageName)
|
||||||
}.map { getAppInfo(it) }
|
}.map { getAppInfo(it) }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
fun setPackageUb(name: String, status: Boolean) {
|
fun setPackageUb(name: String, status: Boolean) {
|
||||||
DPM.setUninstallBlocked(DAR, name, status)
|
DPM.setUninstallBlocked(DAR, name, status)
|
||||||
getUbPackages()
|
getUbPackages()
|
||||||
@@ -421,19 +425,33 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
fun requestBugReport(): Boolean {
|
fun requestBugReport(): Boolean {
|
||||||
return DPM.requestBugreport(DAR)
|
return try {
|
||||||
|
DPM.requestBugreport(DAR)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
fun getOrgName(): String {
|
fun getOrgName(): String {
|
||||||
return DPM.getOrganizationName(DAR).toString()
|
return try {
|
||||||
|
DPM.getOrganizationName(DAR)?.toString() ?: ""
|
||||||
|
} catch (_: Exception) {
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
fun setOrgName(name: String) {
|
fun setOrgName(name: String) {
|
||||||
DPM.setOrganizationName(DAR, name)
|
DPM.setOrganizationName(DAR, name)
|
||||||
}
|
}
|
||||||
@RequiresApi(31)
|
@RequiresApi(31)
|
||||||
fun setOrgId(id: String) {
|
fun setOrgId(id: String): Boolean {
|
||||||
|
return try {
|
||||||
DPM.setOrganizationId(id)
|
DPM.setOrganizationId(id)
|
||||||
|
true
|
||||||
|
} catch (_: IllegalStateException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@RequiresApi(31)
|
@RequiresApi(31)
|
||||||
fun getEnrollmentSpecificId(): String {
|
fun getEnrollmentSpecificId(): String {
|
||||||
@@ -557,12 +575,16 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
properties.temperatures.isEmpty()) {
|
properties.temperatures.isEmpty()) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
hardwareProperties.value = properties
|
||||||
delay(hpRefreshInterval)
|
delay(hpRefreshInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@RequiresApi(28)
|
@RequiresApi(28)
|
||||||
fun setTime(time: Long): Boolean {
|
fun setTime(time: Long, useCurrentTz: Boolean): Boolean {
|
||||||
return DPM.setTime(DAR, time)
|
val offset = if (useCurrentTz) {
|
||||||
|
ZonedDateTime.now(ZoneId.systemDefault()).offset.totalSeconds * 1000L
|
||||||
|
} else 0L
|
||||||
|
return DPM.setTime(DAR, time - offset)
|
||||||
}
|
}
|
||||||
@RequiresApi(28)
|
@RequiresApi(28)
|
||||||
fun setTimeZone(tz: String): Boolean {
|
fun setTimeZone(tz: String): Boolean {
|
||||||
@@ -674,10 +696,8 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
val installedCaCerts = MutableStateFlow(emptyList<CaCertInfo>())
|
val installedCaCerts = MutableStateFlow(emptyList<CaCertInfo>())
|
||||||
fun getCaCerts() {
|
fun getCaCerts() {
|
||||||
viewModelScope.launch {
|
|
||||||
installedCaCerts.value = DPM.getInstalledCaCerts(DAR).mapNotNull { parseCaCert(it) }
|
installedCaCerts.value = DPM.getInstalledCaCerts(DAR).mapNotNull { parseCaCert(it) }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
fun parseCaCert(uri: Uri): CaCertInfo? {
|
fun parseCaCert(uri: Uri): CaCertInfo? {
|
||||||
return try {
|
return try {
|
||||||
application.contentResolver.openInputStream(uri)?.use {
|
application.contentResolver.openInputStream(uri)?.use {
|
||||||
@@ -696,7 +716,7 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
CaCertInfo(
|
CaCertInfo(
|
||||||
hash, cert.serialNumber.toString(16),
|
hash, cert.serialNumber.toString(16),
|
||||||
cert.issuerX500Principal.name, cert.subjectX500Principal.name,
|
cert.issuerX500Principal.name, cert.subjectX500Principal.name,
|
||||||
parseDate(cert.notBefore), parseDate(cert.notAfter), bytes
|
cert.notBefore.time, cert.notAfter.time, bytes
|
||||||
)
|
)
|
||||||
} catch (e: CertificateException) {
|
} catch (e: CertificateException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -809,7 +829,7 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
return DPM.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)
|
return DPM.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)
|
||||||
}
|
}
|
||||||
fun activateDoByShizuku(callback: (Boolean, String?) -> Unit) {
|
fun activateDoByShizuku(callback: (Boolean, String?) -> Unit) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
useShizuku(application) { service ->
|
useShizuku(application) { service ->
|
||||||
try {
|
try {
|
||||||
val result = IUserService.Stub.asInterface(service)
|
val result = IUserService.Stub.asInterface(service)
|
||||||
@@ -887,7 +907,7 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
val dhizukuClients = MutableStateFlow(emptyList<Pair<DhizukuClientInfo, AppInfo>>())
|
val dhizukuClients = MutableStateFlow(emptyList<Pair<DhizukuClientInfo, AppInfo>>())
|
||||||
fun getDhizukuClients() {
|
fun getDhizukuClients() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
dhizukuClients.value = myRepo.getDhizukuClients().mapNotNull {
|
dhizukuClients.value = myRepo.getDhizukuClients().mapNotNull {
|
||||||
val packageName = PM.getNameForUid(it.uid)
|
val packageName = PM.getNameForUid(it.uid)
|
||||||
if (packageName == null) {
|
if (packageName == null) {
|
||||||
@@ -985,7 +1005,7 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
val deviceAdminReceivers = MutableStateFlow(emptyList<DeviceAdmin>())
|
val deviceAdminReceivers = MutableStateFlow(emptyList<DeviceAdmin>())
|
||||||
fun getDeviceAdminReceivers() {
|
fun getDeviceAdminReceivers() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
deviceAdminReceivers.value = PM.queryBroadcastReceivers(
|
deviceAdminReceivers.value = PM.queryBroadcastReceivers(
|
||||||
Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
|
Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
|
||||||
PackageManager.GET_META_DATA
|
PackageManager.GET_META_DATA
|
||||||
@@ -1064,7 +1084,7 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
return intent
|
return intent
|
||||||
}
|
}
|
||||||
fun activateOrgProfileByShizuku(callback: (Boolean) -> Unit) {
|
fun activateOrgProfileByShizuku(callback: (Boolean) -> Unit) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
var succeed = false
|
var succeed = false
|
||||||
useShizuku(application) { service ->
|
useShizuku(application) { service ->
|
||||||
val result = IUserService.Stub.asInterface(service).execute(activateOrgProfileCommand)
|
val result = IUserService.Stub.asInterface(service).execute(activateOrgProfileCommand)
|
||||||
@@ -1102,6 +1122,130 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
DPM.addCrossProfileIntentFilter(DAR, filter, flags)
|
DPM.addCrossProfileIntentFilter(DAR, filter, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val UM = application.getSystemService(Context.USER_SERVICE) as UserManager
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun getLogoutEnabled(): Boolean {
|
||||||
|
return DPM.isLogoutEnabled
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun setLogoutEnabled(enabled: Boolean) {
|
||||||
|
DPM.setLogoutEnabled(DAR, enabled)
|
||||||
|
}
|
||||||
|
fun getUserInformation(): UserInformation {
|
||||||
|
val uh = Binder.getCallingUserHandle()
|
||||||
|
return UserInformation(
|
||||||
|
if (VERSION.SDK_INT >= 24) UserManager.supportsMultipleUsers() else false,
|
||||||
|
if (VERSION.SDK_INT >= 31) UserManager.isHeadlessSystemUserMode() else false,
|
||||||
|
if (VERSION.SDK_INT >= 23) UM.isSystemUser else false,
|
||||||
|
if (VERSION.SDK_INT >= 34) UM.isAdminUser else false,
|
||||||
|
if (VERSION.SDK_INT >= 25) UM.isDemoUser else false,
|
||||||
|
if (VERSION.SDK_INT >= 23) UM.getUserCreationTime(uh) else 0,
|
||||||
|
if (VERSION.SDK_INT >= 28) DPM.isLogoutEnabled else false,
|
||||||
|
if (VERSION.SDK_INT >= 28) DPM.isEphemeralUser(DAR) else false,
|
||||||
|
if (VERSION.SDK_INT >= 28) DPM.isAffiliatedUser else false,
|
||||||
|
UM.getSerialNumberForUser(uh)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun startUser(id: Int, isUserId: Boolean): Int {
|
||||||
|
val uh = getUserHandle(id, isUserId)
|
||||||
|
if (uh == null) return R.string.user_not_exist
|
||||||
|
return getUserOperationResultText(DPM.startUserInBackground(DAR, uh))
|
||||||
|
}
|
||||||
|
fun switchUser(id: Int, isUserId: Boolean): Boolean {
|
||||||
|
val uh = getUserHandle(id, isUserId)
|
||||||
|
if (uh == null) return false
|
||||||
|
DPM.switchUser(DAR, uh)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun stopUser(id: Int, isUserId: Boolean): Int {
|
||||||
|
val uh = getUserHandle(id, isUserId)
|
||||||
|
if (uh == null) return R.string.user_not_exist
|
||||||
|
return getUserOperationResultText(DPM.stopUser(DAR, uh))
|
||||||
|
}
|
||||||
|
fun deleteUser(id: Int, isUserId: Boolean): Boolean {
|
||||||
|
val uh = getUserHandle(id, isUserId)
|
||||||
|
if (uh == null) return false
|
||||||
|
return DPM.removeUser(DAR, uh)
|
||||||
|
}
|
||||||
|
fun getUserHandle(id: Int, isUserId: Boolean): UserHandle? {
|
||||||
|
return if (isUserId && VERSION.SDK_INT >= 24) {
|
||||||
|
UserHandle.getUserHandleForUid(id * 100000)
|
||||||
|
} else {
|
||||||
|
UM.getUserForSerialNumber(id.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun getUserOperationResultText(code: Int): Int {
|
||||||
|
return when (code) {
|
||||||
|
UserManager.USER_OPERATION_SUCCESS -> R.string.success
|
||||||
|
UserManager.USER_OPERATION_ERROR_UNKNOWN -> R.string.unknown_error
|
||||||
|
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE-> R.string.fail_managed_profile
|
||||||
|
UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS -> R.string.limit_reached
|
||||||
|
UserManager.USER_OPERATION_ERROR_CURRENT_USER -> R.string.fail_current_user
|
||||||
|
else -> R.string.unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@RequiresApi(24)
|
||||||
|
fun createUser(name: String, flags: Int, callback: (CreateUserResult) -> Unit) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val uh = DPM.createAndManageUser(DAR, name, DAR, null, flags)
|
||||||
|
if (uh == null) {
|
||||||
|
callback(CreateUserResult(R.string.failed))
|
||||||
|
} else {
|
||||||
|
callback(CreateUserResult(R.string.succeeded, UM.getSerialNumberForUser(uh)))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
if (VERSION.SDK_INT >= 28 && e is UserManager.UserOperationException) {
|
||||||
|
callback(CreateUserResult(getUserOperationResultText(e.userOperationResult)))
|
||||||
|
} else {
|
||||||
|
callback(CreateUserResult(R.string.error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val affiliationIds = MutableStateFlow(emptyList<String>())
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun getAffiliationIds() {
|
||||||
|
affiliationIds.value = DPM.getAffiliationIds(DAR).toList()
|
||||||
|
}
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun setAffiliationId(id: String, state: Boolean) {
|
||||||
|
val newList = affiliationIds.value.run { if (state) plus(id) else minus(id) }
|
||||||
|
DPM.setAffiliationIds(DAR, newList.toSet())
|
||||||
|
affiliationIds.value = newList
|
||||||
|
}
|
||||||
|
fun setProfileName(name: String) {
|
||||||
|
DPM.setProfileName(DAR, name)
|
||||||
|
}
|
||||||
|
@RequiresApi(23)
|
||||||
|
fun setUserIcon(bitmap: Bitmap) {
|
||||||
|
DPM.setUserIcon(DAR, bitmap)
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun getSecondaryUsers(): List<Long> {
|
||||||
|
return DPM.getSecondaryUsers(DAR).map { UM.getSerialNumberForUser(it) }
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun getUserSessionMessages(): Pair<String, String> {
|
||||||
|
return (DPM.getStartUserSessionMessage(DAR)?.toString() ?: "") to
|
||||||
|
(DPM.getEndUserSessionMessage(DAR)?.toString() ?: "")
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun setStartUserSessionMessage(message: String?) {
|
||||||
|
DPM.setStartUserSessionMessage(DAR, message)
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun setEndUserSessionMessage(message: String?) {
|
||||||
|
DPM.setEndUserSessionMessage(DAR, message)
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun logoutUser(): Int {
|
||||||
|
return getUserOperationResultText(DPM.logoutUser(DAR))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ThemeSettings(
|
data class ThemeSettings(
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.runtime.saveable.Saver
|
import androidx.compose.runtime.saveable.Saver
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
import androidx.compose.runtime.saveable.SaverScope
|
||||||
@@ -23,9 +22,6 @@ import java.io.IOException
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.time.Instant
|
|
||||||
import java.time.ZoneId
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@@ -72,20 +68,12 @@ fun formatFileSize(bytes: Long): String {
|
|||||||
val Boolean.yesOrNo
|
val Boolean.yesOrNo
|
||||||
@StringRes get() = if(this) R.string.yes else R.string.no
|
@StringRes get() = if(this) R.string.yes else R.string.no
|
||||||
|
|
||||||
@RequiresApi(26)
|
fun formatTime(ms: Long): String {
|
||||||
fun parseTimestamp(timestamp: Long): String {
|
return SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(Date(ms))
|
||||||
val instant = Instant.ofEpochMilli(timestamp)
|
}
|
||||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault())
|
fun formatDate(date: Date): String {
|
||||||
return formatter.format(instant)
|
return SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseDate(date: Date): String = SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(date)
|
|
||||||
|
|
||||||
val Long.humanReadableDate: String
|
|
||||||
get() = SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(Date(this))
|
|
||||||
|
|
||||||
fun formatDate(pattern: String, value: Long): String
|
|
||||||
= SimpleDateFormat(pattern, Locale.getDefault()).format(Date(value))
|
|
||||||
|
|
||||||
fun Context.showOperationResultToast(success: Boolean) {
|
fun Context.showOperationResultToast(success: Boolean) {
|
||||||
popToast(if(success) R.string.success else R.string.failed)
|
popToast(if(success) R.string.success else R.string.failed)
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.ime
|
import androidx.compose.foundation.layout.ime
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
@@ -130,9 +131,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import com.bintianqi.owndroid.HorizontalPadding
|
import com.bintianqi.owndroid.HorizontalPadding
|
||||||
import com.bintianqi.owndroid.Privilege
|
import com.bintianqi.owndroid.Privilege
|
||||||
import com.bintianqi.owndroid.R
|
import com.bintianqi.owndroid.R
|
||||||
import com.bintianqi.owndroid.formatDate
|
|
||||||
import com.bintianqi.owndroid.formatFileSize
|
import com.bintianqi.owndroid.formatFileSize
|
||||||
import com.bintianqi.owndroid.humanReadableDate
|
import com.bintianqi.owndroid.formatTime
|
||||||
import com.bintianqi.owndroid.popToast
|
import com.bintianqi.owndroid.popToast
|
||||||
import com.bintianqi.owndroid.showOperationResultToast
|
import com.bintianqi.owndroid.showOperationResultToast
|
||||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||||
@@ -157,9 +157,6 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Date
|
|
||||||
import java.util.Locale
|
|
||||||
import kotlin.reflect.jvm.jvmErasure
|
import kotlin.reflect.jvm.jvmErasure
|
||||||
|
|
||||||
@Serializable object Network
|
@Serializable object Network
|
||||||
@@ -1036,14 +1033,14 @@ fun NetworkStatsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = startTime.let { if(it == -1L) "" else it.humanReadableDate }, onValueChange = {}, readOnly = true,
|
value = startTime.let { if(it == -1L) "" else formatTime(it) }, onValueChange = {}, readOnly = true,
|
||||||
label = { Text(stringResource(R.string.start_time)) },
|
label = { Text(stringResource(R.string.start_time)) },
|
||||||
interactionSource = startTimeTextFieldInteractionSource,
|
interactionSource = startTimeTextFieldInteractionSource,
|
||||||
isError = startTime >= endTime,
|
isError = startTime >= endTime,
|
||||||
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = endTime.humanReadableDate, onValueChange = {}, readOnly = true,
|
value = formatTime(endTime), onValueChange = {}, readOnly = true,
|
||||||
label = { Text(stringResource(R.string.end_time)) },
|
label = { Text(stringResource(R.string.end_time)) },
|
||||||
interactionSource = endTimeTextFieldInteractionSource,
|
interactionSource = endTimeTextFieldInteractionSource,
|
||||||
isError = startTime >= endTime,
|
isError = startTime >= endTime,
|
||||||
@@ -1315,18 +1312,9 @@ fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit)
|
|||||||
HorizontalPager(ps, Modifier.padding(top = 8.dp)) { page ->
|
HorizontalPager(ps, Modifier.padding(top = 8.dp)) { page ->
|
||||||
val data = nsv.stats[page]
|
val data = nsv.stats[page]
|
||||||
Column(Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)) {
|
Column(Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)) {
|
||||||
Row(Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp), verticalAlignment = Alignment.CenterVertically) {
|
Text(formatTime(data.startTime) + "\n~\n" + formatTime(data.endTime),
|
||||||
SimpleDateFormat("", Locale.getDefault()).format(Date(data.startTime))
|
Modifier.align(Alignment.CenterHorizontally), textAlign = TextAlign.Center)
|
||||||
Text(
|
Spacer(Modifier.height(5.dp))
|
||||||
formatDate("yyyy/MM/dd", data.startTime) + "\n" + formatDate("HH:mm:ss", data.startTime),
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
Text("~", Modifier.padding(horizontal = 8.dp))
|
|
||||||
Text(
|
|
||||||
formatDate("yyyy/MM/dd", data.endTime) + "\n" + formatDate("HH:mm:ss", data.endTime),
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val txBytes = data.txBytes
|
val txBytes = data.txBytes
|
||||||
Text(stringResource(R.string.transmitted), style = typography.titleLarge)
|
Text(stringResource(R.string.transmitted), style = typography.titleLarge)
|
||||||
Column(modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)) {
|
Column(modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)) {
|
||||||
|
|||||||
@@ -112,8 +112,7 @@ import com.bintianqi.owndroid.Privilege
|
|||||||
import com.bintianqi.owndroid.R
|
import com.bintianqi.owndroid.R
|
||||||
import com.bintianqi.owndroid.SP
|
import com.bintianqi.owndroid.SP
|
||||||
import com.bintianqi.owndroid.formatFileSize
|
import com.bintianqi.owndroid.formatFileSize
|
||||||
import com.bintianqi.owndroid.humanReadableDate
|
import com.bintianqi.owndroid.formatTime
|
||||||
import com.bintianqi.owndroid.parseTimestamp
|
|
||||||
import com.bintianqi.owndroid.popToast
|
import com.bintianqi.owndroid.popToast
|
||||||
import com.bintianqi.owndroid.showOperationResultToast
|
import com.bintianqi.owndroid.showOperationResultToast
|
||||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||||
@@ -290,7 +289,7 @@ fun SystemManagerScreen(
|
|||||||
onClick = {
|
onClick = {
|
||||||
if (dialog == 3 && VERSION.SDK_INT >= 24) vm.setOrgName(input)
|
if (dialog == 3 && VERSION.SDK_INT >= 24) vm.setOrgName(input)
|
||||||
if (dialog == 4 && VERSION.SDK_INT >= 31) {
|
if (dialog == 4 && VERSION.SDK_INT >= 31) {
|
||||||
vm.setOrgId(input)
|
context.showOperationResultToast(vm.setOrgId(input))
|
||||||
enrollmentSpecificId = vm.getEnrollmentSpecificId()
|
enrollmentSpecificId = vm.getEnrollmentSpecificId()
|
||||||
}
|
}
|
||||||
dialog = 0
|
dialog = 0
|
||||||
@@ -368,6 +367,24 @@ fun SystemOptionsScreen(vm: MyViewModel, onNavigateUp: () -> Unit) {
|
|||||||
SwitchItem(R.string.enable_usb_signal, status.usbSignalEnabled,
|
SwitchItem(R.string.enable_usb_signal, status.usbSignalEnabled,
|
||||||
vm::setUsbSignalEnabled, R.drawable.usb_fill0)
|
vm::setUsbSignalEnabled, R.drawable.usb_fill0)
|
||||||
}
|
}
|
||||||
|
if (VERSION.SDK_INT >= 23 && VERSION.SDK_INT < 34) {
|
||||||
|
Row(
|
||||||
|
Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.status_bar), style = typography.titleMedium)
|
||||||
|
Button({
|
||||||
|
vm.setStatusBarDisabled(true)
|
||||||
|
}, Modifier.padding(horizontal = 4.dp)) {
|
||||||
|
Text(stringResource(R.string.disable))
|
||||||
|
}
|
||||||
|
Button({
|
||||||
|
vm.setStatusBarDisabled(false)
|
||||||
|
}) {
|
||||||
|
Text(stringResource(R.string.enable))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(dialog != 0) AlertDialog(
|
if(dialog != 0) AlertDialog(
|
||||||
text = {
|
text = {
|
||||||
@@ -520,7 +537,7 @@ fun HardwareMonitorScreen(
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@RequiresApi(28)
|
@RequiresApi(28)
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeTimeScreen(setTime: (Long) -> Boolean, onNavigateUp: () -> Unit) {
|
fun ChangeTimeScreen(setTime: (Long, Boolean) -> Boolean, onNavigateUp: () -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var tab by remember { mutableIntStateOf(0) }
|
var tab by remember { mutableIntStateOf(0) }
|
||||||
@@ -528,8 +545,9 @@ fun ChangeTimeScreen(setTime: (Long) -> Boolean, onNavigateUp: () -> Unit) {
|
|||||||
tab = pagerState.currentPage
|
tab = pagerState.currentPage
|
||||||
val coroutine = rememberCoroutineScope()
|
val coroutine = rememberCoroutineScope()
|
||||||
var picker by remember { mutableIntStateOf(0) } //0:None, 1:DatePicker, 2:TimePicker
|
var picker by remember { mutableIntStateOf(0) } //0:None, 1:DatePicker, 2:TimePicker
|
||||||
|
var useCurrentTz by remember { mutableStateOf(true) }
|
||||||
val datePickerState = rememberDatePickerState()
|
val datePickerState = rememberDatePickerState()
|
||||||
val timePickerState = rememberTimePickerState()
|
val timePickerState = rememberTimePickerState(is24Hour = true)
|
||||||
val dateInteractionSource = remember { MutableInteractionSource() }
|
val dateInteractionSource = remember { MutableInteractionSource() }
|
||||||
val timeInteractionSource = remember { MutableInteractionSource() }
|
val timeInteractionSource = remember { MutableInteractionSource() }
|
||||||
if(dateInteractionSource.collectIsPressedAsState().value) picker = 1
|
if(dateInteractionSource.collectIsPressedAsState().value) picker = 1
|
||||||
@@ -571,14 +589,15 @@ fun ChangeTimeScreen(setTime: (Long) -> Boolean, onNavigateUp: () -> Unit) {
|
|||||||
) {
|
) {
|
||||||
if(page == 0) {
|
if(page == 0) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = datePickerState.selectedDateMillis?.humanReadableDate ?: "",
|
value = datePickerState.selectedDateMillis?.let { formatTime(it) } ?: "",
|
||||||
onValueChange = {}, readOnly = true,
|
onValueChange = {}, readOnly = true,
|
||||||
label = { Text(stringResource(R.string.date)) },
|
label = { Text(stringResource(R.string.date)) },
|
||||||
interactionSource = dateInteractionSource,
|
interactionSource = dateInteractionSource,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = timePickerState.hour.toString() + ":" + timePickerState.minute.toString(),
|
value = timePickerState.hour.toString().padStart(2, '0') + ":" +
|
||||||
|
timePickerState.minute.toString().padStart(2, '0'),
|
||||||
onValueChange = {}, readOnly = true,
|
onValueChange = {}, readOnly = true,
|
||||||
label = { Text(stringResource(R.string.time)) },
|
label = { Text(stringResource(R.string.time)) },
|
||||||
interactionSource = timeInteractionSource,
|
interactionSource = timeInteractionSource,
|
||||||
@@ -586,11 +605,14 @@ fun ChangeTimeScreen(setTime: (Long) -> Boolean, onNavigateUp: () -> Unit) {
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 4.dp)
|
.padding(vertical = 4.dp)
|
||||||
)
|
)
|
||||||
|
CheckBoxItem(R.string.use_current_timezone, useCurrentTz) {
|
||||||
|
useCurrentTz = it
|
||||||
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
val timeMillis = datePickerState.selectedDateMillis!! +
|
val timeMillis = datePickerState.selectedDateMillis!! +
|
||||||
timePickerState.hour * 3600000 + timePickerState.minute * 60000
|
timePickerState.hour * 3600000 + timePickerState.minute * 60000
|
||||||
context.showOperationResultToast(setTime(timeMillis))
|
context.showOperationResultToast(setTime(timeMillis, useCurrentTz))
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = datePickerState.selectedDateMillis != null
|
enabled = datePickerState.selectedDateMillis != null
|
||||||
@@ -609,7 +631,7 @@ fun ChangeTimeScreen(setTime: (Long) -> Boolean, onNavigateUp: () -> Unit) {
|
|||||||
)
|
)
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
context.showOperationResultToast(setTime(inputTime.toLong()))
|
context.showOperationResultToast(setTime(inputTime.toLong(), false))
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -653,11 +675,14 @@ fun ChangeTimeZoneScreen(setTimeZone: (String) -> Boolean, onNavigateUp: () -> U
|
|||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var inputTimezone by remember { mutableStateOf("") }
|
var inputTimezone by remember { mutableStateOf("") }
|
||||||
var dialog by remember { mutableStateOf(false) }
|
var dialog by remember { mutableStateOf(false) }
|
||||||
|
val availableIds = TimeZone.getAvailableIDs()
|
||||||
|
val validInput = inputTimezone in availableIds
|
||||||
MyScaffold(R.string.change_timezone, onNavigateUp) {
|
MyScaffold(R.string.change_timezone, onNavigateUp) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = inputTimezone,
|
value = inputTimezone,
|
||||||
label = { Text(stringResource(R.string.timezone_id)) },
|
label = { Text(stringResource(R.string.timezone_id)) },
|
||||||
onValueChange = { inputTimezone = it },
|
onValueChange = { inputTimezone = it },
|
||||||
|
isError = inputTimezone.isNotEmpty() && !validInput,
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
IconButton(onClick = { dialog = true }) {
|
IconButton(onClick = { dialog = true }) {
|
||||||
Icon(imageVector = Icons.AutoMirrored.Default.List, contentDescription = null)
|
Icon(imageVector = Icons.AutoMirrored.Default.List, contentDescription = null)
|
||||||
@@ -673,7 +698,7 @@ fun ChangeTimeZoneScreen(setTimeZone: (String) -> Boolean, onNavigateUp: () -> U
|
|||||||
context.showOperationResultToast(setTimeZone(inputTimezone))
|
context.showOperationResultToast(setTimeZone(inputTimezone))
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = inputTimezone.isNotEmpty()
|
enabled = inputTimezone.isNotEmpty() && validInput
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.apply))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
@@ -683,7 +708,7 @@ fun ChangeTimeZoneScreen(setTimeZone: (String) -> Boolean, onNavigateUp: () -> U
|
|||||||
if(dialog) AlertDialog(
|
if(dialog) AlertDialog(
|
||||||
text = {
|
text = {
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
items(TimeZone.getAvailableIDs()) {
|
items(availableIds) {
|
||||||
Text(
|
Text(
|
||||||
text = it,
|
text = it,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -1322,8 +1347,8 @@ data class CaCertInfo(
|
|||||||
val serialNumber: String,
|
val serialNumber: String,
|
||||||
val issuer: String,
|
val issuer: String,
|
||||||
val subject: String,
|
val subject: String,
|
||||||
val issuedTime: String,
|
val issuedTime: Long,
|
||||||
val expiresTime: String,
|
val expiresTime: Long,
|
||||||
val bytes: ByteArray
|
val bytes: ByteArray
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1373,8 +1398,7 @@ fun CaCertScreen(
|
|||||||
}) {
|
}) {
|
||||||
Icon(Icons.Default.Add, stringResource(R.string.install))
|
Icon(Icons.Default.Add, stringResource(R.string.install))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
contentWindowInsets = WindowInsets.ime
|
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
Modifier
|
Modifier
|
||||||
@@ -1388,6 +1412,7 @@ fun CaCertScreen(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
selectedCaCert = cert
|
selectedCaCert = cert
|
||||||
|
dialog = 2
|
||||||
}
|
}
|
||||||
.animateItem()
|
.animateItem()
|
||||||
.padding(vertical = 10.dp, horizontal = 8.dp)
|
.padding(vertical = 10.dp, horizontal = 8.dp)
|
||||||
@@ -1412,9 +1437,9 @@ fun CaCertScreen(
|
|||||||
Text("Issuer", style = typography.labelLarge)
|
Text("Issuer", style = typography.labelLarge)
|
||||||
SelectionContainer { Text(cert.issuer) }
|
SelectionContainer { Text(cert.issuer) }
|
||||||
Text("Issued on", style = typography.labelLarge)
|
Text("Issued on", style = typography.labelLarge)
|
||||||
SelectionContainer { Text(cert.issuedTime) }
|
SelectionContainer { Text(formatTime(cert.issuedTime)) }
|
||||||
Text("Expires on", style = typography.labelLarge)
|
Text("Expires on", style = typography.labelLarge)
|
||||||
SelectionContainer { Text(cert.expiresTime) }
|
SelectionContainer { Text(formatTime(cert.expiresTime)) }
|
||||||
Text("SHA-256 fingerprint", style = typography.labelLarge)
|
Text("SHA-256 fingerprint", style = typography.labelLarge)
|
||||||
SelectionContainer { Text(cert.hash) }
|
SelectionContainer { Text(cert.hash) }
|
||||||
if (dialog == 2) Row(
|
if (dialog == 2) Row(
|
||||||
@@ -1693,7 +1718,6 @@ fun FrpPolicyScreen(
|
|||||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
}
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
focusMgr.clearFocus()
|
focusMgr.clearFocus()
|
||||||
@@ -1706,6 +1730,7 @@ fun FrpPolicyScreen(
|
|||||||
Text(stringResource(R.string.apply))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Notes(R.string.info_frp_policy, HorizontalPadding)
|
Notes(R.string.info_frp_policy, HorizontalPadding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1755,7 +1780,7 @@ fun WipeDataScreen(
|
|||||||
dialog = 1
|
dialog = 1
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().padding(HorizontalPadding, 5.dp)
|
||||||
) {
|
) {
|
||||||
Text("WipeData")
|
Text("WipeData")
|
||||||
}
|
}
|
||||||
@@ -1767,7 +1792,7 @@ fun WipeDataScreen(
|
|||||||
dialog = 2
|
dialog = 2
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().padding(HorizontalPadding, 5.dp)
|
||||||
) {
|
) {
|
||||||
Text("WipeDevice")
|
Text("WipeDevice")
|
||||||
}
|
}
|
||||||
@@ -1905,8 +1930,7 @@ fun SystemUpdatePolicyScreen(
|
|||||||
if (VERSION.SDK_INT >= 26) {
|
if (VERSION.SDK_INT >= 26) {
|
||||||
Column(Modifier.padding(HorizontalPadding)) {
|
Column(Modifier.padding(HorizontalPadding)) {
|
||||||
if (pendingUpdate.exists) {
|
if (pendingUpdate.exists) {
|
||||||
Text(stringResource(R.string.update_received_time,
|
Text(stringResource(R.string.update_received_time, formatTime(pendingUpdate.time)))
|
||||||
parseTimestamp(pendingUpdate.time)))
|
|
||||||
Text(stringResource(R.string.is_security_patch,
|
Text(stringResource(R.string.is_security_patch,
|
||||||
stringResource(pendingUpdate.securityPatch.yesOrNo)))
|
stringResource(pendingUpdate.securityPatch.yesOrNo)))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
package com.bintianqi.owndroid.dpm
|
package com.bintianqi.owndroid.dpm
|
||||||
|
|
||||||
import android.app.admin.DevicePolicyManager
|
import android.app.admin.DevicePolicyManager
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
import android.os.Process
|
|
||||||
import android.os.UserHandle
|
|
||||||
import android.os.UserManager
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -20,6 +15,7 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
@@ -44,11 +40,8 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableLongStateOf
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -62,9 +55,10 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.bintianqi.owndroid.HorizontalPadding
|
import com.bintianqi.owndroid.HorizontalPadding
|
||||||
|
import com.bintianqi.owndroid.MyViewModel
|
||||||
import com.bintianqi.owndroid.Privilege
|
import com.bintianqi.owndroid.Privilege
|
||||||
import com.bintianqi.owndroid.R
|
import com.bintianqi.owndroid.R
|
||||||
import com.bintianqi.owndroid.parseTimestamp
|
import com.bintianqi.owndroid.formatTime
|
||||||
import com.bintianqi.owndroid.popToast
|
import com.bintianqi.owndroid.popToast
|
||||||
import com.bintianqi.owndroid.showOperationResultToast
|
import com.bintianqi.owndroid.showOperationResultToast
|
||||||
import com.bintianqi.owndroid.ui.CircularProgressDialog
|
import com.bintianqi.owndroid.ui.CircularProgressDialog
|
||||||
@@ -77,17 +71,16 @@ import com.bintianqi.owndroid.ui.Notes
|
|||||||
import com.bintianqi.owndroid.ui.SwitchItem
|
import com.bintianqi.owndroid.ui.SwitchItem
|
||||||
import com.bintianqi.owndroid.uriToStream
|
import com.bintianqi.owndroid.uriToStream
|
||||||
import com.bintianqi.owndroid.yesOrNo
|
import com.bintianqi.owndroid.yesOrNo
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable object Users
|
@Serializable object Users
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
fun UsersScreen(vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
||||||
|
/** 1: secondary users, 2: logout*/
|
||||||
var dialog by remember { mutableIntStateOf(0) }
|
var dialog by remember { mutableIntStateOf(0) }
|
||||||
MyScaffold(R.string.users, onNavigateUp, 0.dp) {
|
MyScaffold(R.string.users, onNavigateUp, 0.dp) {
|
||||||
if(VERSION.SDK_INT >= 28 && privilege.profile && privilege.affiliated) {
|
if(VERSION.SDK_INT >= 28 && privilege.profile && privilege.affiliated) {
|
||||||
@@ -118,7 +111,11 @@ fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
|||||||
context.popToast(R.string.select_an_image)
|
context.popToast(R.string.select_an_image)
|
||||||
launcher.launch("image/*")
|
launcher.launch("image/*")
|
||||||
}
|
}
|
||||||
if(changeUserIconDialog) ChangeUserIconDialog(bitmap!!) { changeUserIconDialog = false }
|
if (changeUserIconDialog) ChangeUserIconDialog(
|
||||||
|
bitmap!!, {
|
||||||
|
vm.setUserIcon(bitmap!!)
|
||||||
|
changeUserIconDialog = false
|
||||||
|
}) { changeUserIconDialog = false }
|
||||||
}
|
}
|
||||||
if(VERSION.SDK_INT >= 28 && privilege.device) {
|
if(VERSION.SDK_INT >= 28 && privilege.device) {
|
||||||
FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) { onNavigate(UserSessionMessage) }
|
FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) { onNavigate(UserSessionMessage) }
|
||||||
@@ -127,36 +124,39 @@ fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
|||||||
FunctionItem(R.string.affiliation_id, icon = R.drawable.id_card_fill0) { onNavigate(AffiliationId) }
|
FunctionItem(R.string.affiliation_id, icon = R.drawable.id_card_fill0) { onNavigate(AffiliationId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(dialog != 0 && VERSION.SDK_INT >= 28) AlertDialog(
|
if (VERSION.SDK_INT >= 28 && dialog == 1) AlertDialog(
|
||||||
title = { Text(stringResource(if(dialog == 1) R.string.secondary_users else R.string.logout)) },
|
title = { Text(stringResource(R.string.secondary_users)) },
|
||||||
text = {
|
text = {
|
||||||
if(dialog == 1) {
|
val list = vm.getSecondaryUsers()
|
||||||
val um = context.getSystemService(Context.USER_SERVICE) as UserManager
|
val text = if (list.isEmpty()) {
|
||||||
val list = Privilege.DPM.getSecondaryUsers(Privilege.DAR)
|
stringResource(R.string.no_secondary_users)
|
||||||
if(list.isEmpty()) {
|
|
||||||
Text(stringResource(R.string.no_secondary_users))
|
|
||||||
} else {
|
} else {
|
||||||
Text("(" + stringResource(R.string.serial_number) + ")\n" + list.joinToString("\n") { um.getSerialNumberForUser(it).toString() })
|
"(" + stringResource(R.string.serial_number) + ")\n" + list.joinToString("\n")
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Text(stringResource(R.string.info_logout))
|
|
||||||
}
|
}
|
||||||
|
Text(text)
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton({ dialog = 0 }) {
|
||||||
onClick = {
|
Text(stringResource(R.string.confirm))
|
||||||
if(dialog == 2) {
|
|
||||||
val result = Privilege.DPM.logoutUser(Privilege.DAR)
|
|
||||||
context.popToast(userOperationResultCode(result))
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = { dialog = 0 }
|
||||||
|
)
|
||||||
|
if (VERSION.SDK_INT >= 28 && dialog == 2) AlertDialog(
|
||||||
|
title = { Text(stringResource(R.string.logout)) },
|
||||||
|
text = {
|
||||||
|
Text(stringResource(R.string.info_logout))
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton({
|
||||||
|
context.popToast(vm.logoutUser())
|
||||||
dialog = 0
|
dialog = 0
|
||||||
}
|
}) {
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.confirm))
|
Text(stringResource(R.string.confirm))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
if(dialog != 1) TextButton(onClick = { dialog = 0 }) {
|
TextButton({ dialog = 0 }) {
|
||||||
Text(stringResource(R.string.cancel))
|
Text(stringResource(R.string.cancel))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -167,41 +167,53 @@ fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
|||||||
@Serializable object UsersOptions
|
@Serializable object UsersOptions
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UsersOptionsScreen(onNavigateUp: () -> Unit) {
|
fun UsersOptionsScreen(
|
||||||
|
getLogoutEnabled: () -> Boolean, setLogoutEnabled: (Boolean) -> Unit, onNavigateUp: () -> Unit
|
||||||
|
) {
|
||||||
|
var logoutEnabled by remember { mutableStateOf(false) }
|
||||||
|
LaunchedEffect(Unit) { logoutEnabled = getLogoutEnabled() }
|
||||||
MyScaffold(R.string.options, onNavigateUp, 0.dp) {
|
MyScaffold(R.string.options, onNavigateUp, 0.dp) {
|
||||||
if(VERSION.SDK_INT >= 28) {
|
if(VERSION.SDK_INT >= 28) {
|
||||||
SwitchItem(R.string.enable_logout, getState = { Privilege.DPM.isLogoutEnabled },
|
SwitchItem(R.string.enable_logout, logoutEnabled, {
|
||||||
onCheckedChange = { Privilege.DPM.setLogoutEnabled(Privilege.DAR, it) })
|
setLogoutEnabled(it)
|
||||||
|
logoutEnabled = it
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class UserInformation(
|
||||||
|
val multiUser: Boolean = false, val headless: Boolean = false, val system: Boolean = false,
|
||||||
|
val admin: Boolean = false, val demo: Boolean = false, val time: Long = 0,
|
||||||
|
val logout: Boolean = false, val ephemeral: Boolean = false, val affiliated: Boolean = false,
|
||||||
|
val serial: Long = 0
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable object UserInfo
|
@Serializable object UserInfo
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UserInfoScreen(onNavigateUp: () -> Unit) {
|
fun UserInfoScreen(getInfo: () -> UserInformation, onNavigateUp: () -> Unit) {
|
||||||
val context = LocalContext.current
|
var info by remember { mutableStateOf(UserInformation()) }
|
||||||
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
|
||||||
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
|
|
||||||
val user = Process.myUserHandle()
|
|
||||||
var infoDialog by remember { mutableIntStateOf(0) }
|
var infoDialog by remember { mutableIntStateOf(0) }
|
||||||
MyScaffold(R.string.user_info, onNavigateUp, 0.dp) {
|
LaunchedEffect(Unit) {
|
||||||
if(VERSION.SDK_INT >= 24) InfoItem(R.string.support_multiuser, UserManager.supportsMultipleUsers().yesOrNo)
|
info = getInfo()
|
||||||
if(VERSION.SDK_INT >= 31) InfoItem(R.string.headless_system_user_mode, UserManager.isHeadlessSystemUserMode().yesOrNo, true) { infoDialog = 1 }
|
|
||||||
Spacer(Modifier.padding(vertical = 8.dp))
|
|
||||||
if(VERSION.SDK_INT >= 23) InfoItem(R.string.system_user, userManager.isSystemUser.yesOrNo)
|
|
||||||
if(VERSION.SDK_INT >= 34) InfoItem(R.string.admin_user, userManager.isAdminUser.yesOrNo)
|
|
||||||
if(VERSION.SDK_INT >= 25) InfoItem(R.string.demo_user, userManager.isDemoUser.yesOrNo)
|
|
||||||
if(VERSION.SDK_INT >= 26) userManager.getUserCreationTime(user).let {
|
|
||||||
if(it != 0L) InfoItem(R.string.creation_time, parseTimestamp(it))
|
|
||||||
}
|
}
|
||||||
|
MyScaffold(R.string.user_info, onNavigateUp, 0.dp) {
|
||||||
|
if (VERSION.SDK_INT >= 24) InfoItem(R.string.support_multiuser, info.multiUser.yesOrNo)
|
||||||
|
if (VERSION.SDK_INT >= 31) InfoItem(R.string.headless_system_user_mode, info.headless.yesOrNo, true) { infoDialog = 1 }
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
if (VERSION.SDK_INT >= 23) InfoItem(R.string.system_user, info.system.yesOrNo)
|
||||||
|
if (VERSION.SDK_INT >= 34) InfoItem(R.string.admin_user, info.admin.yesOrNo)
|
||||||
|
if (VERSION.SDK_INT >= 25) InfoItem(R.string.demo_user, info.demo.yesOrNo)
|
||||||
|
if (info.time != 0L) InfoItem(R.string.creation_time, formatTime(info.time))
|
||||||
|
|
||||||
if (VERSION.SDK_INT >= 28) {
|
if (VERSION.SDK_INT >= 28) {
|
||||||
InfoItem(R.string.logout_enabled, Privilege.DPM.isLogoutEnabled.yesOrNo)
|
InfoItem(R.string.logout_enabled, info.logout.yesOrNo)
|
||||||
InfoItem(R.string.ephemeral_user, Privilege.DPM.isEphemeralUser(Privilege.DAR).yesOrNo)
|
InfoItem(R.string.ephemeral_user, info.ephemeral.yesOrNo)
|
||||||
InfoItem(R.string.affiliated_user, privilege.affiliated.yesOrNo)
|
InfoItem(R.string.affiliated_user, info.affiliated.yesOrNo)
|
||||||
}
|
}
|
||||||
InfoItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString())
|
InfoItem(R.string.user_id, (Binder.getCallingUid() / 100000).toString())
|
||||||
InfoItem(R.string.user_serial_number, userManager.getSerialNumberForUser(Process.myUserHandle()).toString())
|
InfoItem(R.string.user_serial_number, info.serial.toString())
|
||||||
}
|
}
|
||||||
if(infoDialog != 0) AlertDialog(
|
if(infoDialog != 0) AlertDialog(
|
||||||
text = { Text(stringResource(R.string.info_headless_system_user_mode)) },
|
text = { Text(stringResource(R.string.info_headless_system_user_mode)) },
|
||||||
@@ -217,24 +229,15 @@ fun UserInfoScreen(onNavigateUp: () -> Unit) {
|
|||||||
@Serializable object UserOperation
|
@Serializable object UserOperation
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UserOperationScreen(onNavigateUp: () -> Unit) {
|
fun UserOperationScreen(
|
||||||
|
startUser: (Int, Boolean) -> Int, switchUser: (Int, Boolean) -> Boolean,
|
||||||
|
stopUser: (Int, Boolean) -> Int, deleteUser: (Int, Boolean) -> Boolean, onNavigateUp: () -> Unit
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
|
|
||||||
var input by remember { mutableStateOf("") }
|
var input by remember { mutableStateOf("") }
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var useUserId by remember { mutableStateOf(false) }
|
var useUserId by remember { mutableStateOf(false) }
|
||||||
fun withUserHandle(operation: (UserHandle) -> Unit) {
|
var dialog by remember { mutableStateOf(false) }
|
||||||
val userHandle = if(useUserId && VERSION.SDK_INT >= 24) {
|
|
||||||
UserHandle.getUserHandleForUid(input.toInt() * 100000)
|
|
||||||
} else {
|
|
||||||
userManager.getUserForSerialNumber(input.toLong())
|
|
||||||
}
|
|
||||||
if(userHandle == null) {
|
|
||||||
context.popToast(R.string.user_not_exist)
|
|
||||||
} else {
|
|
||||||
operation(userHandle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val legalInput = input.toIntOrNull() != null
|
val legalInput = input.toIntOrNull() != null
|
||||||
MyScaffold(R.string.user_operation, onNavigateUp) {
|
MyScaffold(R.string.user_operation, onNavigateUp) {
|
||||||
if(VERSION.SDK_INT >= 24) SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
if(VERSION.SDK_INT >= 24) SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
||||||
@@ -257,10 +260,7 @@ fun UserOperationScreen(onNavigateUp: () -> Unit) {
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
focusMgr.clearFocus()
|
focusMgr.clearFocus()
|
||||||
withUserHandle {
|
context.popToast(startUser(input.toInt(), useUserId))
|
||||||
val result = Privilege.DPM.startUserInBackground(Privilege.DAR, it)
|
|
||||||
context.popToast(userOperationResultCode(result))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
enabled = legalInput,
|
enabled = legalInput,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
@@ -272,7 +272,7 @@ fun UserOperationScreen(onNavigateUp: () -> Unit) {
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
focusMgr.clearFocus()
|
focusMgr.clearFocus()
|
||||||
withUserHandle { context.showOperationResultToast(Privilege.DPM.switchUser(Privilege.DAR, it)) }
|
if (switchUser(input.toInt(), useUserId)) context.popToast(R.string.user_not_exist)
|
||||||
},
|
},
|
||||||
enabled = legalInput,
|
enabled = legalInput,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
@@ -284,10 +284,7 @@ fun UserOperationScreen(onNavigateUp: () -> Unit) {
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
focusMgr.clearFocus()
|
focusMgr.clearFocus()
|
||||||
withUserHandle {
|
context.popToast(stopUser(input.toInt(), useUserId))
|
||||||
val result = Privilege.DPM.stopUser(Privilege.DAR, it)
|
|
||||||
context.popToast(userOperationResultCode(result))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
enabled = legalInput,
|
enabled = legalInput,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
@@ -299,14 +296,7 @@ fun UserOperationScreen(onNavigateUp: () -> Unit) {
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
focusMgr.clearFocus()
|
focusMgr.clearFocus()
|
||||||
withUserHandle {
|
dialog = true
|
||||||
if(Privilege.DPM.removeUser(Privilege.DAR, it)) {
|
|
||||||
context.showOperationResultToast(true)
|
|
||||||
input = ""
|
|
||||||
} else {
|
|
||||||
context.showOperationResultToast(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
enabled = legalInput,
|
enabled = legalInput,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
@@ -315,21 +305,39 @@ fun UserOperationScreen(onNavigateUp: () -> Unit) {
|
|||||||
Text(stringResource(R.string.delete))
|
Text(stringResource(R.string.delete))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (dialog) AlertDialog(
|
||||||
|
text = {
|
||||||
|
Text(stringResource(R.string.delete_user_confirmation, input))
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton({
|
||||||
|
context.showOperationResultToast(deleteUser(input.toInt(), useUserId))
|
||||||
|
dialog = false
|
||||||
|
}) {
|
||||||
|
Text(stringResource(R.string.confirm))
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton({ dialog = false }) { Text(stringResource(R.string.cancel)) }
|
||||||
|
},
|
||||||
|
onDismissRequest = { dialog = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class CreateUserResult(val message: Int, val serial: Long = -1)
|
||||||
|
|
||||||
@Serializable object CreateUser
|
@Serializable object CreateUser
|
||||||
|
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
@Composable
|
@Composable
|
||||||
fun CreateUserScreen(onNavigateUp: () -> Unit) {
|
fun CreateUserScreen(
|
||||||
val context = LocalContext.current
|
createUser: (String, Int, (CreateUserResult) -> Unit) -> Unit, onNavigateUp: () -> Unit
|
||||||
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
|
) {
|
||||||
|
var result by remember { mutableStateOf<CreateUserResult?>(null) }
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var userName by remember { mutableStateOf("") }
|
var userName by remember { mutableStateOf("") }
|
||||||
var creating by remember { mutableStateOf(false) }
|
var creating by remember { mutableStateOf(false) }
|
||||||
var createdUserSerialNumber by remember { mutableLongStateOf(-1) }
|
var flags by remember { mutableIntStateOf(0) }
|
||||||
var flag by remember { mutableIntStateOf(0) }
|
|
||||||
val coroutine = rememberCoroutineScope()
|
|
||||||
MyScaffold(R.string.create_user, onNavigateUp, 0.dp) {
|
MyScaffold(R.string.create_user, onNavigateUp, 0.dp) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
userName, { userName= it }, Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding),
|
userName, { userName= it }, Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding),
|
||||||
@@ -340,53 +348,45 @@ fun CreateUserScreen(onNavigateUp: () -> Unit) {
|
|||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
Spacer(Modifier.padding(vertical = 5.dp))
|
||||||
FullWidthCheckBoxItem(
|
FullWidthCheckBoxItem(
|
||||||
R.string.create_user_skip_wizard,
|
R.string.create_user_skip_wizard,
|
||||||
flag and DevicePolicyManager.SKIP_SETUP_WIZARD != 0
|
flags and DevicePolicyManager.SKIP_SETUP_WIZARD != 0
|
||||||
) { flag = flag xor DevicePolicyManager.SKIP_SETUP_WIZARD }
|
) { flags = flags xor DevicePolicyManager.SKIP_SETUP_WIZARD }
|
||||||
if(VERSION.SDK_INT >= 28) {
|
if(VERSION.SDK_INT >= 28) {
|
||||||
FullWidthCheckBoxItem(
|
FullWidthCheckBoxItem(
|
||||||
R.string.create_user_ephemeral_user,
|
R.string.create_user_ephemeral_user,
|
||||||
flag and DevicePolicyManager.MAKE_USER_EPHEMERAL != 0
|
flags and DevicePolicyManager.MAKE_USER_EPHEMERAL != 0
|
||||||
) { flag = flag xor DevicePolicyManager.MAKE_USER_EPHEMERAL }
|
) { flags = flags xor DevicePolicyManager.MAKE_USER_EPHEMERAL }
|
||||||
FullWidthCheckBoxItem(
|
FullWidthCheckBoxItem(
|
||||||
R.string.create_user_enable_all_system_app,
|
R.string.create_user_enable_all_system_app,
|
||||||
flag and DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED != 0
|
flags and DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED != 0
|
||||||
) { flag = flag xor DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED }
|
) { flags = flags xor DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED }
|
||||||
}
|
}
|
||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
Spacer(Modifier.padding(vertical = 5.dp))
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
focusMgr.clearFocus()
|
focusMgr.clearFocus()
|
||||||
creating = true
|
creating = true
|
||||||
coroutine.launch(Dispatchers.IO) {
|
createUser(userName, flags) {
|
||||||
try {
|
creating = false
|
||||||
val uh = Privilege.DPM.createAndManageUser(Privilege.DAR, userName, Privilege.DAR, null, flag)
|
result = it
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
createdUserSerialNumber = userManager.getSerialNumberForUser(uh)
|
|
||||||
}
|
|
||||||
} catch(e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
if (VERSION.SDK_INT >= 28 && e is UserManager.UserOperationException) {
|
|
||||||
context.popToast(e.message ?: context.getString(R.string.error))
|
|
||||||
} else {
|
|
||||||
context.showOperationResultToast(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
withContext(Dispatchers.Main) { creating = false }
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)
|
modifier = Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.create))
|
Text(stringResource(R.string.create))
|
||||||
}
|
}
|
||||||
if(createdUserSerialNumber != -1L) AlertDialog(
|
if (result != null) AlertDialog(
|
||||||
title = { Text(stringResource(R.string.success)) },
|
text = {
|
||||||
text = { Text(stringResource(R.string.serial_number_of_new_user_is, createdUserSerialNumber)) },
|
Column {
|
||||||
confirmButton = {
|
Text(stringResource(result!!.message))
|
||||||
TextButton({ createdUserSerialNumber = -1 }) { Text(stringResource(R.string.confirm)) }
|
if (result?.serial != -1L) {
|
||||||
|
Text(stringResource(R.string.serial_number) + ": " + result!!.serial)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onDismissRequest = { createdUserSerialNumber = -1 }
|
confirmButton = {
|
||||||
|
TextButton({ result = null }) { Text(stringResource(R.string.confirm)) }
|
||||||
|
},
|
||||||
|
onDismissRequest = { result = null }
|
||||||
)
|
)
|
||||||
if (creating) CircularProgressDialog { }
|
if (creating) CircularProgressDialog { }
|
||||||
}
|
}
|
||||||
@@ -396,24 +396,21 @@ fun CreateUserScreen(onNavigateUp: () -> Unit) {
|
|||||||
|
|
||||||
@RequiresApi(26)
|
@RequiresApi(26)
|
||||||
@Composable
|
@Composable
|
||||||
fun AffiliationIdScreen(onNavigateUp: () -> Unit) {
|
fun AffiliationIdScreen(
|
||||||
val context = LocalContext.current
|
affiliationIds: StateFlow<List<String>>, getIds: () -> Unit, setId: (String, Boolean) -> Unit,
|
||||||
|
onNavigateUp: () -> Unit
|
||||||
|
) {
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var input by remember { mutableStateOf("") }
|
var input by remember { mutableStateOf("") }
|
||||||
val list = remember { mutableStateListOf<String>() }
|
val list by affiliationIds.collectAsStateWithLifecycle()
|
||||||
val refreshIds = {
|
LaunchedEffect(Unit) { getIds() }
|
||||||
list.clear()
|
|
||||||
list.addAll(Privilege.DPM.getAffiliationIds(Privilege.DAR))
|
|
||||||
}
|
|
||||||
LaunchedEffect(Unit) { refreshIds() }
|
|
||||||
MyScaffold(R.string.affiliation_id, onNavigateUp) {
|
MyScaffold(R.string.affiliation_id, onNavigateUp) {
|
||||||
Column(modifier = Modifier.animateContentSize()) {
|
Column(modifier = Modifier.animateContentSize()) {
|
||||||
if (list.isEmpty()) Text(stringResource(R.string.none))
|
if (list.isEmpty()) Text(stringResource(R.string.none))
|
||||||
for (i in list) {
|
for (i in list) {
|
||||||
ListItem(i) { list -= i }
|
ListItem(i) { setId(i, false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = input,
|
value = input,
|
||||||
onValueChange = { input = it },
|
onValueChange = { input = it },
|
||||||
@@ -421,7 +418,7 @@ fun AffiliationIdScreen(onNavigateUp: () -> Unit) {
|
|||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
list += input
|
setId(input, true)
|
||||||
input = ""
|
input = ""
|
||||||
},
|
},
|
||||||
enabled = input.isNotEmpty()
|
enabled = input.isNotEmpty()
|
||||||
@@ -429,22 +426,10 @@ fun AffiliationIdScreen(onNavigateUp: () -> Unit) {
|
|||||||
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add))
|
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp),
|
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
|
||||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() })
|
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() })
|
||||||
)
|
)
|
||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
list.removeAll(setOf(""))
|
|
||||||
Privilege.DPM.setAffiliationIds(Privilege.DAR, list.toSet())
|
|
||||||
context.showOperationResultToast(true)
|
|
||||||
refreshIds()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.apply))
|
|
||||||
}
|
|
||||||
Notes(R.string.info_affiliation_id)
|
Notes(R.string.info_affiliation_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,7 +437,7 @@ fun AffiliationIdScreen(onNavigateUp: () -> Unit) {
|
|||||||
@Serializable object ChangeUsername
|
@Serializable object ChangeUsername
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeUsernameScreen(onNavigateUp: () -> Unit) {
|
fun ChangeUsernameScreen(setName: (String) -> Unit, onNavigateUp: () -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var inputUsername by remember { mutableStateOf("") }
|
var inputUsername by remember { mutableStateOf("") }
|
||||||
@@ -468,19 +453,13 @@ fun ChangeUsernameScreen(onNavigateUp: () -> Unit) {
|
|||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
Spacer(Modifier.padding(vertical = 5.dp))
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
Privilege.DPM.setProfileName(Privilege.DAR, inputUsername)
|
setName(inputUsername)
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.apply))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
Button(
|
|
||||||
onClick = { Privilege.DPM.setProfileName(Privilege.DAR, null) },
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.reset))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,16 +467,19 @@ fun ChangeUsernameScreen(onNavigateUp: () -> Unit) {
|
|||||||
|
|
||||||
@RequiresApi(28)
|
@RequiresApi(28)
|
||||||
@Composable
|
@Composable
|
||||||
fun UserSessionMessageScreen(onNavigateUp: () -> Unit) {
|
fun UserSessionMessageScreen(
|
||||||
|
getMessages: () -> Pair<String, String>, setStartMessage: (String?) -> Unit,
|
||||||
|
setEndMessage: (String?) -> Unit, onNavigateUp: () -> Unit
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var start by remember { mutableStateOf("") }
|
var start by remember { mutableStateOf("") }
|
||||||
var end by remember { mutableStateOf("") }
|
var end by remember { mutableStateOf("") }
|
||||||
val refreshMsg = {
|
LaunchedEffect(Unit) {
|
||||||
start = Privilege.DPM.getStartUserSessionMessage(Privilege.DAR)?.toString() ?: ""
|
val messages = getMessages()
|
||||||
end = Privilege.DPM.getEndUserSessionMessage(Privilege.DAR)?.toString() ?: ""
|
start = messages.first
|
||||||
|
end = messages.second
|
||||||
}
|
}
|
||||||
LaunchedEffect(Unit) { refreshMsg() }
|
|
||||||
MyScaffold(R.string.user_session_msg, onNavigateUp) {
|
MyScaffold(R.string.user_session_msg, onNavigateUp) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = start,
|
value = start,
|
||||||
@@ -510,8 +492,7 @@ fun UserSessionMessageScreen(onNavigateUp: () -> Unit) {
|
|||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
Privilege.DPM.setStartUserSessionMessage(Privilege.DAR, start)
|
setStartMessage(start)
|
||||||
refreshMsg()
|
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
modifier = Modifier.fillMaxWidth(0.49F)
|
||||||
) {
|
) {
|
||||||
@@ -519,8 +500,7 @@ fun UserSessionMessageScreen(onNavigateUp: () -> Unit) {
|
|||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
Privilege.DPM.setStartUserSessionMessage(Privilege.DAR, null)
|
setStartMessage(null)
|
||||||
refreshMsg()
|
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
modifier = Modifier.fillMaxWidth(0.96F)
|
||||||
@@ -540,8 +520,7 @@ fun UserSessionMessageScreen(onNavigateUp: () -> Unit) {
|
|||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
Privilege.DPM.setEndUserSessionMessage(Privilege.DAR, end)
|
setStartMessage(end)
|
||||||
refreshMsg()
|
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
modifier = Modifier.fillMaxWidth(0.49F)
|
||||||
@@ -550,8 +529,7 @@ fun UserSessionMessageScreen(onNavigateUp: () -> Unit) {
|
|||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
Privilege.DPM.setEndUserSessionMessage(Privilege.DAR, null)
|
setEndMessage(null)
|
||||||
refreshMsg()
|
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
modifier = Modifier.fillMaxWidth(0.96F)
|
||||||
@@ -564,8 +542,7 @@ fun UserSessionMessageScreen(onNavigateUp: () -> Unit) {
|
|||||||
|
|
||||||
@RequiresApi(23)
|
@RequiresApi(23)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ChangeUserIconDialog(bitmap: Bitmap, onClose: () -> Unit) {
|
private fun ChangeUserIconDialog(bitmap: Bitmap, onSet: () -> Unit, onClose: () -> Unit) {
|
||||||
val context = LocalContext.current
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title = { Text(stringResource(R.string.change_user_icon)) },
|
title = { Text(stringResource(R.string.change_user_icon)) },
|
||||||
text = {
|
text = {
|
||||||
@@ -577,11 +554,7 @@ private fun ChangeUserIconDialog(bitmap: Bitmap, onClose: () -> Unit) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton({
|
TextButton(onSet) {
|
||||||
Privilege.DPM.setUserIcon(Privilege.DAR, bitmap)
|
|
||||||
context.showOperationResultToast(true)
|
|
||||||
onClose()
|
|
||||||
}) {
|
|
||||||
Text(stringResource(R.string.confirm))
|
Text(stringResource(R.string.confirm))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -593,13 +566,3 @@ private fun ChangeUserIconDialog(bitmap: Bitmap, onClose: () -> Unit) {
|
|||||||
onDismissRequest = onClose
|
onDismissRequest = onClose
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@StringRes
|
|
||||||
private fun userOperationResultCode(result:Int): Int =
|
|
||||||
when(result) {
|
|
||||||
UserManager.USER_OPERATION_SUCCESS -> R.string.success
|
|
||||||
UserManager.USER_OPERATION_ERROR_UNKNOWN -> R.string.unknown_error
|
|
||||||
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE-> R.string.fail_managed_profile
|
|
||||||
UserManager.USER_OPERATION_ERROR_CURRENT_USER-> R.string.fail_current_user
|
|
||||||
else -> R.string.unknown
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -482,7 +482,6 @@
|
|||||||
<string name="create_user_skip_wizard">Пропустить мастер настройки</string>
|
<string name="create_user_skip_wizard">Пропустить мастер настройки</string>
|
||||||
<string name="create_user_ephemeral_user">Временный пользователь</string>
|
<string name="create_user_ephemeral_user">Временный пользователь</string>
|
||||||
<string name="create_user_enable_all_system_app">Включить все системные приложения</string>
|
<string name="create_user_enable_all_system_app">Включить все системные приложения</string>
|
||||||
<string name="serial_number_of_new_user_is">Серийный номер этого пользователя: %1$d</string>
|
|
||||||
<string name="affiliation_id">Аффилированный идентификатор</string>
|
<string name="affiliation_id">Аффилированный идентификатор</string>
|
||||||
<string name="change_user_icon">Изменить значок пользователя</string>
|
<string name="change_user_icon">Изменить значок пользователя</string>
|
||||||
<string name="select_an_image">Select an image</string> <!--TODO-->
|
<string name="select_an_image">Select an image</string> <!--TODO-->
|
||||||
@@ -625,7 +624,7 @@
|
|||||||
|
|
||||||
<string name="info_device_id_attestation">Указывает, поддерживает ли устройство проверку идентификаторов устройств в дополнение к проверке ключей.</string>
|
<string name="info_device_id_attestation">Указывает, поддерживает ли устройство проверку идентификаторов устройств в дополнение к проверке ключей.</string>
|
||||||
<string name="info_unique_device_attestation">Да, если реализация StrongBox Keymaster на устройстве была обеспечена индивидуальным сертификатом аттестации и может использовать его для подписи записей аттестации (индивидуальный сертификат аттестации могут использовать только Keymaster с уровнем безопасности StrongBox).</string>
|
<string name="info_unique_device_attestation">Да, если реализация StrongBox Keymaster на устройстве была обеспечена индивидуальным сертификатом аттестации и может использовать его для подписи записей аттестации (индивидуальный сертификат аттестации могут использовать только Keymaster с уровнем безопасности StrongBox).</string>
|
||||||
<string name="info_org_id">Устанавливает идентификатор предприятия (Enterprise ID). Это необходимо для создания идентификатора устройства, специфичного для регистрации.</string>
|
<!--TODO--><string name="info_org_id">Устанавливает идентификатор предприятия (Enterprise ID). Это необходимо для создания идентификатора устройства, специфичного для регистрации.</string>
|
||||||
<string name="info_enrollment_specific_id">Идентификатор останется неизменным, даже если рабочий профиль будет удален и создан заново (для той же организации), или если устройство будет сброшено до заводских настроек и перерегистрировано</string>
|
<string name="info_enrollment_specific_id">Идентификатор останется неизменным, даже если рабочий профиль будет удален и создан заново (для той же организации), или если устройство будет сброшено до заводских настроек и перерегистрировано</string>
|
||||||
<string name="info_lock_screen_info">Отобразить краткое сообщение на экране блокировки. Переопределяет любую информацию о владельце, установленную пользователем вручную, и предотвращает ее дальнейшее изменение.</string>
|
<string name="info_lock_screen_info">Отобразить краткое сообщение на экране блокировки. Переопределяет любую информацию о владельце, установленную пользователем вручную, и предотвращает ее дальнейшее изменение.</string>
|
||||||
<string name="info_short_support_message">Это будет отображено пользователю на экранах настроек, функциональность которых была отключена администратором. Если длина сообщения превышает 200 символов, оно может быть обрезано.</string>
|
<string name="info_short_support_message">Это будет отображено пользователю на экранах настроек, функциональность которых была отключена администратором. Если длина сообщения превышает 200 символов, оно может быть обрезано.</string>
|
||||||
|
|||||||
@@ -507,7 +507,6 @@
|
|||||||
<string name="create_user_skip_wizard">Sihirbazı Atla</string>
|
<string name="create_user_skip_wizard">Sihirbazı Atla</string>
|
||||||
<string name="create_user_ephemeral_user">Geçici Kullanıcı</string>
|
<string name="create_user_ephemeral_user">Geçici Kullanıcı</string>
|
||||||
<string name="create_user_enable_all_system_app">Tüm Sistem Uygulamalarını Etkinleştir</string>
|
<string name="create_user_enable_all_system_app">Tüm Sistem Uygulamalarını Etkinleştir</string>
|
||||||
<string name="serial_number_of_new_user_is">Bu kullanıcının seri numarası: %1$d</string>
|
|
||||||
<string name="affiliation_id">Bağlılık Kimliği</string>
|
<string name="affiliation_id">Bağlılık Kimliği</string>
|
||||||
<string name="change_user_icon">Kullanıcı Simgesini Değiştir</string>
|
<string name="change_user_icon">Kullanıcı Simgesini Değiştir</string>
|
||||||
<string name="select_an_image">Bir görüntü seç</string>
|
<string name="select_an_image">Bir görüntü seç</string>
|
||||||
@@ -664,7 +663,7 @@
|
|||||||
|
|
||||||
<string name="info_device_id_attestation">Cihazın, anahtar doğrulamasına ek olarak cihaz kimlik doğrulamalarını destekleyip desteklemediğini belirtir.</string>
|
<string name="info_device_id_attestation">Cihazın, anahtar doğrulamasına ek olarak cihaz kimlik doğrulamalarını destekleyip desteklemediğini belirtir.</string>
|
||||||
<string name="info_unique_device_attestation">Evet, eğer cihazdaki StrongBox Keymaster uygulaması bireysel bir doğrulama sertifikasıyla sağlanmışsa ve bunu kullanarak doğrulama kayıtlarını imzalayabiliyorsa (yalnızca StrongBox güvenlik seviyesine sahip Keymaster bireysel doğrulama sertifikası kullanabilir).</string>
|
<string name="info_unique_device_attestation">Evet, eğer cihazdaki StrongBox Keymaster uygulaması bireysel bir doğrulama sertifikasıyla sağlanmışsa ve bunu kullanarak doğrulama kayıtlarını imzalayabiliyorsa (yalnızca StrongBox güvenlik seviyesine sahip Keymaster bireysel doğrulama sertifikası kullanabilir).</string>
|
||||||
<string name="info_org_id">Kurumsal Kimliği ayarlar. Bu, cihaz için kayıt özel bir kimlik oluşturmak için bir gerekliliktir.</string>
|
<!--TODO--><string name="info_org_id">Kurumsal Kimliği ayarlar. Bu, cihaz için kayıt özel bir kimlik oluşturmak için bir gerekliliktir.</string>
|
||||||
<string name="info_enrollment_specific_id">Kimlik, iş profili kaldırılsa ve aynı Kurum Kimliği ile yeniden oluşturulsa veya cihaz fabrika ayarlarına sıfırlanıp yeniden kaydedilse bile tutarlı kalır.</string>
|
<string name="info_enrollment_specific_id">Kimlik, iş profili kaldırılsa ve aynı Kurum Kimliği ile yeniden oluşturulsa veya cihaz fabrika ayarlarına sıfırlanıp yeniden kaydedilse bile tutarlı kalır.</string>
|
||||||
<string name="info_lock_screen_info">Kilit ekranında kısa bir mesaj gösterir.\nKullanıcı tarafından manuel olarak ayarlanan herhangi bir sahip bilgisini geçersiz kılar ve kullanıcının bunu daha fazla değiştirmesini engeller.</string>
|
<string name="info_lock_screen_info">Kilit ekranında kısa bir mesaj gösterir.\nKullanıcı tarafından manuel olarak ayarlanan herhangi bir sahip bilgisini geçersiz kılar ve kullanıcının bunu daha fazla değiştirmesini engeller.</string>
|
||||||
<string name="info_short_support_message">Bu, yönetici tarafından devre dışı bırakılan işlevlerin bulunduğu ayar ekranlarında kullanıcıya gösterilecektir. Mesaj 200 karakterden uzunsa kesilebilir.</string>
|
<string name="info_short_support_message">Bu, yönetici tarafından devre dışı bırakılan işlevlerin bulunduğu ayar ekranlarında kullanıcıya gösterilecektir. Mesaj 200 karakterden uzunsa kesilebilir.</string>
|
||||||
|
|||||||
@@ -124,6 +124,7 @@
|
|||||||
<string name="enable_camera">启用相机</string>
|
<string name="enable_camera">启用相机</string>
|
||||||
<string name="disable_screen_capture">禁止屏幕捕获</string>
|
<string name="disable_screen_capture">禁止屏幕捕获</string>
|
||||||
<string name="disable_status_bar">禁用状态栏</string>
|
<string name="disable_status_bar">禁用状态栏</string>
|
||||||
|
<string name="status_bar">状态栏</string>
|
||||||
<string name="auto_time">自动设置时间</string>
|
<string name="auto_time">自动设置时间</string>
|
||||||
<string name="auto_timezone">自动设置时区</string>
|
<string name="auto_timezone">自动设置时区</string>
|
||||||
<string name="require_auto_time">要求自动时间</string>
|
<string name="require_auto_time">要求自动时间</string>
|
||||||
@@ -157,6 +158,7 @@
|
|||||||
<string name="manually_input">手动输入</string>
|
<string name="manually_input">手动输入</string>
|
||||||
<string name="date">日期</string>
|
<string name="date">日期</string>
|
||||||
<string name="time">时间</string>
|
<string name="time">时间</string>
|
||||||
|
<string name="use_current_timezone">使用当前时区</string>
|
||||||
<string name="change_timezone">更改时区</string>
|
<string name="change_timezone">更改时区</string>
|
||||||
<string name="timezone_id">时区ID</string>
|
<string name="timezone_id">时区ID</string>
|
||||||
<string name="disable_auto_time_zone_before_set">在设置时区前需要关闭自动时区</string>
|
<string name="disable_auto_time_zone_before_set">在设置时区前需要关闭自动时区</string>
|
||||||
@@ -483,12 +485,13 @@
|
|||||||
<string name="logout">登出</string>
|
<string name="logout">登出</string>
|
||||||
<string name="start_in_background">在后台启动</string>
|
<string name="start_in_background">在后台启动</string>
|
||||||
<string name="user_operation_switch">切换</string>
|
<string name="user_operation_switch">切换</string>
|
||||||
|
<string name="limit_reached">已达到上限</string>
|
||||||
|
<string name="delete_user_confirmation">你确定要删除用户%1$s吗?</string>
|
||||||
<string name="create_user">创建用户</string>
|
<string name="create_user">创建用户</string>
|
||||||
<string name="username">用户名</string>
|
<string name="username">用户名</string>
|
||||||
<string name="create_user_skip_wizard">跳过创建用户向导</string>
|
<string name="create_user_skip_wizard">跳过创建用户向导</string>
|
||||||
<string name="create_user_ephemeral_user">临时用户</string>
|
<string name="create_user_ephemeral_user">临时用户</string>
|
||||||
<string name="create_user_enable_all_system_app">启用所有系统应用</string>
|
<string name="create_user_enable_all_system_app">启用所有系统应用</string>
|
||||||
<string name="serial_number_of_new_user_is">新用户的序列号:%1$d</string>
|
|
||||||
<string name="affiliation_id">附属用户ID</string>
|
<string name="affiliation_id">附属用户ID</string>
|
||||||
<string name="change_user_icon">更换用户头像</string>
|
<string name="change_user_icon">更换用户头像</string>
|
||||||
<string name="select_an_image">选择一个图片</string>
|
<string name="select_an_image">选择一个图片</string>
|
||||||
@@ -644,7 +647,7 @@
|
|||||||
|
|
||||||
<string name="info_device_id_attestation">指示设备是否除了密钥证明之外还支持设备标识符证明</string>
|
<string name="info_device_id_attestation">指示设备是否除了密钥证明之外还支持设备标识符证明</string>
|
||||||
<string name="info_unique_device_attestation">如果设备上的StrongBox Keymaster可以配置单独的证明证书并且可以使用该证书签署证明记录,则返回true(只有StrongBox安全级别的Keymaster才能使用单独的证明证书进行证明)</string>
|
<string name="info_unique_device_attestation">如果设备上的StrongBox Keymaster可以配置单独的证明证书并且可以使用该证书签署证明记录,则返回true(只有StrongBox安全级别的Keymaster才能使用单独的证明证书进行证明)</string>
|
||||||
<string name="info_org_id">设置组织ID后才能获取设备注册专用ID</string>
|
<string name="info_org_id">设置组织ID后才能获取设备注册专用ID。ID只能设置一次。</string>
|
||||||
<string name="info_enrollment_specific_id">不同组织ID的设备注册专用ID不同,恢复出厂设置或删除工作资料后不变</string>
|
<string name="info_enrollment_specific_id">不同组织ID的设备注册专用ID不同,恢复出厂设置或删除工作资料后不变</string>
|
||||||
<string name="info_lock_screen_info">在锁屏界面上显示的一段简短的消息。将会覆盖用户当前设置的锁屏信息,并且防止用户在系统设置中设置新的锁屏信息</string>
|
<string name="info_lock_screen_info">在锁屏界面上显示的一段简短的消息。将会覆盖用户当前设置的锁屏信息,并且防止用户在系统设置中设置新的锁屏信息</string>
|
||||||
<string name="info_short_support_message">用户试图使用被管理员禁用的功能时会显示此消息。不应多于200字</string>
|
<string name="info_short_support_message">用户试图使用被管理员禁用的功能时会显示此消息。不应多于200字</string>
|
||||||
|
|||||||
@@ -133,6 +133,7 @@
|
|||||||
<string name="enable_camera">Enable camera</string>
|
<string name="enable_camera">Enable camera</string>
|
||||||
<string name="disable_screen_capture">Disable screen capture</string>
|
<string name="disable_screen_capture">Disable screen capture</string>
|
||||||
<string name="disable_status_bar">Disable status bar</string>
|
<string name="disable_status_bar">Disable status bar</string>
|
||||||
|
<string name="status_bar">Status bar</string>
|
||||||
<string name="auto_time">Auto time</string>
|
<string name="auto_time">Auto time</string>
|
||||||
<string name="require_auto_time">Require auto time</string>
|
<string name="require_auto_time">Require auto time</string>
|
||||||
<string name="auto_timezone">Auto timezone</string>
|
<string name="auto_timezone">Auto timezone</string>
|
||||||
@@ -166,6 +167,7 @@
|
|||||||
<string name="manually_input">Manually input</string>
|
<string name="manually_input">Manually input</string>
|
||||||
<string name="date">Date</string>
|
<string name="date">Date</string>
|
||||||
<string name="time">Time</string>
|
<string name="time">Time</string>
|
||||||
|
<string name="use_current_timezone">Use current timezone</string>
|
||||||
<string name="change_timezone">Change timezone</string>
|
<string name="change_timezone">Change timezone</string>
|
||||||
<string name="timezone_id">Timezone ID</string>
|
<string name="timezone_id">Timezone ID</string>
|
||||||
<string name="disable_auto_time_zone_before_set">Auto timezone should be disabled before set a custom timezone. </string>
|
<string name="disable_auto_time_zone_before_set">Auto timezone should be disabled before set a custom timezone. </string>
|
||||||
@@ -516,12 +518,13 @@
|
|||||||
<string name="logout">Logout</string>
|
<string name="logout">Logout</string>
|
||||||
<string name="start_in_background">Start in background</string>
|
<string name="start_in_background">Start in background</string>
|
||||||
<string name="user_operation_switch">Switch</string>
|
<string name="user_operation_switch">Switch</string>
|
||||||
|
<string name="limit_reached">Limit reached</string>
|
||||||
|
<string name="delete_user_confirmation">Are you sure you want to delete user %1$s ?</string>
|
||||||
<string name="create_user">Create user</string>
|
<string name="create_user">Create user</string>
|
||||||
<string name="username">Username</string>
|
<string name="username">Username</string>
|
||||||
<string name="create_user_skip_wizard">Skip wizard</string>
|
<string name="create_user_skip_wizard">Skip wizard</string>
|
||||||
<string name="create_user_ephemeral_user">Ephemeral user</string>
|
<string name="create_user_ephemeral_user">Ephemeral user</string>
|
||||||
<string name="create_user_enable_all_system_app">Enable all system app</string>
|
<string name="create_user_enable_all_system_app">Enable all system app</string>
|
||||||
<string name="serial_number_of_new_user_is">Serial number of this user: %1$d</string>
|
|
||||||
<string name="affiliation_id">Affiliation ID</string>
|
<string name="affiliation_id">Affiliation ID</string>
|
||||||
<string name="change_user_icon">Change user icon</string>
|
<string name="change_user_icon">Change user icon</string>
|
||||||
<string name="select_an_image">Select an image</string>
|
<string name="select_an_image">Select an image</string>
|
||||||
@@ -678,7 +681,7 @@
|
|||||||
|
|
||||||
<string name="info_device_id_attestation">Indicates if the device supports attestation of device identifiers in addition to key attestation.</string>
|
<string name="info_device_id_attestation">Indicates if the device supports attestation of device identifiers in addition to key attestation.</string>
|
||||||
<string name="info_unique_device_attestation">Yes if the StrongBox Keymaster implementation on the device was provisioned with an individual attestation certificate and can sign attestation records using it (only Keymaster with StrongBox security level can use an individual attestation certificate).</string>
|
<string name="info_unique_device_attestation">Yes if the StrongBox Keymaster implementation on the device was provisioned with an individual attestation certificate and can sign attestation records using it (only Keymaster with StrongBox security level can use an individual attestation certificate).</string>
|
||||||
<string name="info_org_id">Sets the Enterprise ID. This is a requirement for generating an enrollment-specific ID for the device.</string>
|
<string name="info_org_id">Sets the Enterprise ID. This is a requirement for generating an enrollment-specific ID for the device. The ID can only be set once.</string>
|
||||||
<string name="info_enrollment_specific_id">The identifier would be consistent even if the work profile is removed and create again (to the same Organization ID), or the device is factory reset and re-enrolled.</string>
|
<string name="info_enrollment_specific_id">The identifier would be consistent even if the work profile is removed and create again (to the same Organization ID), or the device is factory reset and re-enrolled.</string>
|
||||||
<string name="info_lock_screen_info">Show a brief message on your lock screen.\nOverrides any owner information manually set by the user and prevents the user from further changing it.</string>
|
<string name="info_lock_screen_info">Show a brief message on your lock screen.\nOverrides any owner information manually set by the user and prevents the user from further changing it.</string>
|
||||||
<string name="info_short_support_message">This will be displayed to the user in settings screens where functionality has been disabled by the admin. If the message is longer than 200 characters it may be truncated</string>
|
<string name="info_short_support_message">This will be displayed to the user in settings screens where functionality has been disabled by the admin. If the message is longer than 200 characters it may be truncated</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user