diff --git a/app/src/main/java/com/bintianqi/owndroid/Utils.kt b/app/src/main/java/com/bintianqi/owndroid/Utils.kt
index aea2e6d..dc244ca 100644
--- a/app/src/main/java/com/bintianqi/owndroid/Utils.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/Utils.kt
@@ -39,8 +39,8 @@ fun uriToStream(
if(stream != null) { operation(stream) }
stream?.close()
}
- catch(e: FileNotFoundException) { Toast.makeText(context, R.string.file_not_exist, Toast.LENGTH_SHORT).show() }
- catch(e: IOException) { Toast.makeText(context, R.string.io_exception, Toast.LENGTH_SHORT).show() }
+ catch(_: FileNotFoundException) { Toast.makeText(context, R.string.file_not_exist, Toast.LENGTH_SHORT).show() }
+ catch(_: IOException) { Toast.makeText(context, R.string.io_exception, Toast.LENGTH_SHORT).show() }
}
}
@@ -52,7 +52,7 @@ fun writeClipBoard(context: Context, string: String):Boolean{
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
try {
clipboardManager.setPrimaryClip(ClipData.newPlainText("", string))
- }catch(e:Exception){
+ } catch(_:Exception) {
return false
}
return true
diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt
index 43e1d84..e449a96 100644
--- a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt
@@ -118,16 +118,12 @@ fun Password(navCtrl: NavHostController) {
composable(route = "ResetPassword") { ResetPassword() }
composable(route = "RequirePasswordComplexity") { PasswordComplexity() }
composable(route = "DisableKeyguardFeatures") { DisableKeyguardFeatures() }
- composable(route = "MaxTimeToLock") { ScreenTimeout() }
- composable(route = "PasswordTimeout") { PasswordExpiration() }
- composable(route = "MaxPasswordFail") { MaxFailedPasswordForWipe() }
- composable(route = "RequiredStrongAuthTimeout") { RequiredStrongAuthTimeout() }
- composable(route = "PasswordHistoryLength") { PasswordHistoryLength() }
composable(route = "RequirePasswordQuality") { PasswordQuality() }
}
}
}
+@SuppressLint("NewApi")
@Composable
private fun Home(navCtrl:NavHostController, scrollState: ScrollState) {
val context = LocalContext.current
@@ -135,6 +131,7 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState) {
val deviceAdmin = context.isDeviceAdmin
val deviceOwner = context.isDeviceOwner
val profileOwner = context.isProfileOwner
+ var dialog by remember { mutableIntStateOf(0) }
Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
Text(
text = stringResource(R.string.password_and_keyguard),
@@ -157,21 +154,101 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState) {
SubPageItem(R.string.disable_keyguard_features, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("DisableKeyguardFeatures") }
}
if(deviceOwner) {
- SubPageItem(R.string.max_time_to_lock, "", R.drawable.schedule_fill0) { navCtrl.navigate("MaxTimeToLock") }
- SubPageItem(R.string.pwd_expiration_timeout, "", R.drawable.lock_clock_fill0) { navCtrl.navigate("PasswordTimeout") }
- SubPageItem(R.string.max_pwd_fail, "", R.drawable.no_encryption_fill0) { navCtrl.navigate("MaxPasswordFail") }
- }
- if(deviceAdmin){
- SubPageItem(R.string.pwd_history, "", R.drawable.history_fill0) { navCtrl.navigate("PasswordHistoryLength") }
+ SubPageItem(R.string.max_time_to_lock, "", R.drawable.schedule_fill0) { dialog = 1 }
+ SubPageItem(R.string.pwd_expiration_timeout, "", R.drawable.lock_clock_fill0) { dialog = 3 }
+ SubPageItem(R.string.max_pwd_fail, "", R.drawable.no_encryption_fill0) { dialog = 4 }
}
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
- SubPageItem(R.string.required_strong_auth_timeout, "", R.drawable.fingerprint_off_fill0) { navCtrl.navigate("RequiredStrongAuthTimeout") }
+ SubPageItem(R.string.required_strong_auth_timeout, "", R.drawable.fingerprint_off_fill0) { dialog = 2 }
+ }
+ if(deviceAdmin){
+ SubPageItem(R.string.pwd_history, "", R.drawable.history_fill0) { dialog = 5 }
}
if(VERSION.SDK_INT < 31 && (deviceOwner || profileOwner)) {
SubPageItem(R.string.required_password_quality, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordQuality") }
}
Spacer(Modifier.padding(vertical = 30.dp))
}
+ if(dialog != 0) {
+ val dpm = context.getDPM()
+ val receiver = context.getReceiver()
+ var input by remember { mutableStateOf("") }
+ LaunchedEffect(Unit) {
+ input = when(dialog) {
+ 1 -> dpm.getMaximumTimeToLock(receiver).toString()
+ 2 -> dpm.getRequiredStrongAuthTimeout(receiver).toString()
+ 3 -> dpm.getPasswordExpirationTimeout(receiver).toString()
+ 4 -> dpm.getMaximumFailedPasswordsForWipe(receiver).toString()
+ 5 -> dpm.getPasswordHistoryLength(receiver).toString()
+ else -> ""
+ }
+ }
+ AlertDialog(
+ title = {
+ Text(stringResource(
+ when(dialog) {
+ 1 -> R.string.max_time_to_lock
+ 2 -> R.string.required_strong_auth_timeout
+ 3 -> R.string.pwd_expiration_timeout
+ 4 -> R.string.max_pwd_fail
+ 5 -> R.string.pwd_history
+ else -> R.string.password
+ }
+ ))
+ },
+ text = {
+ val focusMgr = LocalFocusManager.current
+ Column {
+ OutlinedTextField(
+ value = input,
+ label = {
+ Text(stringResource(
+ when(dialog) {
+ 1,2,3 -> R.string.time_unit_ms
+ 4 -> R.string.max_pwd_fail_textfield
+ 5 -> R.string.length
+ else -> R.string.password
+ }
+ ))
+ },
+ onValueChange = { input = it },
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
+ modifier = Modifier.fillMaxWidth().padding(bottom = 5.dp)
+ )
+ when(dialog) {
+ 4 -> Text(stringResource(R.string.max_pwd_fail_desc))
+ 5 -> Text(stringResource(R.string.pwd_history_desc))
+ }
+ Text(stringResource(R.string.zero_means_no_restriction))
+ }
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ when(dialog) {
+ 1 -> dpm.setMaximumTimeToLock(receiver, input.toLong())
+ 2 -> dpm.setRequiredStrongAuthTimeout(receiver, input.toLong())
+ 3 -> dpm.setPasswordExpirationTimeout(receiver, input.toLong())
+ 4 -> dpm.setMaximumFailedPasswordsForWipe(receiver, input.toInt())
+ 5 -> dpm.setPasswordHistoryLength(receiver, input.toInt())
+ }
+ dialog = 0
+ }
+ ) {
+ Text(stringResource(R.string.apply))
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = { dialog = 0 }) {
+ Text(stringResource(R.string.cancel))
+ }
+ },
+ onDismissRequest = {
+ dialog = 0
+ }
+ )
+ }
}
@Composable
@@ -242,7 +319,7 @@ private fun ResetPasswordToken() {
if(dpm.setResetPasswordToken(receiver, tokenByteArray)) R.string.success else R.string.failed,
Toast.LENGTH_SHORT
).show()
- }catch(e:SecurityException) {
+ }catch(_:SecurityException) {
Toast.makeText(context, R.string.security_exception, Toast.LENGTH_SHORT).show()
}
},
@@ -259,7 +336,7 @@ private fun ResetPasswordToken() {
onClick = {
if(!dpm.isResetPasswordTokenActive(receiver)) {
try { activateToken(context) }
- catch(e:NullPointerException) { Toast.makeText(context, R.string.please_set_a_token, Toast.LENGTH_SHORT).show() }
+ catch(_:NullPointerException) { Toast.makeText(context, R.string.please_set_a_token, Toast.LENGTH_SHORT).show() }
} else { Toast.makeText(context, R.string.token_already_activated, Toast.LENGTH_SHORT).show() }
},
modifier = Modifier.fillMaxWidth(0.49F)
@@ -454,175 +531,6 @@ private fun PasswordComplexity() {
}
}
-@Composable
-private fun ScreenTimeout() {
- val context = LocalContext.current
- val dpm = context.getDPM()
- val receiver = context.getReceiver()
- val focusMgr = LocalFocusManager.current
- var inputContent by remember { mutableStateOf("") }
- LaunchedEffect(Unit) { inputContent = dpm.getMaximumTimeToLock(receiver).toString() }
- Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
- Spacer(Modifier.padding(vertical = 10.dp))
- Text(text = stringResource(R.string.max_time_to_lock), style = typography.headlineLarge)
- Spacer(Modifier.padding(vertical = 5.dp))
- Text(text= stringResource(R.string.max_time_to_lock_desc),modifier=Modifier.padding(vertical = 2.dp))
- Spacer(Modifier.padding(vertical = 5.dp))
- OutlinedTextField(
- value = inputContent,
- label = { Text(stringResource(R.string.time_unit_ms)) },
- onValueChange = { inputContent = it },
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
- modifier = Modifier.fillMaxWidth()
- )
- Spacer(Modifier.padding(vertical = 5.dp))
- Button(
- onClick = { focusMgr.clearFocus(); dpm.setMaximumTimeToLock(receiver, inputContent.toLong()) },
- modifier = Modifier.fillMaxWidth(),
- enabled = inputContent != ""
- ) {
- Text(stringResource(R.string.apply))
- }
- }
-}
-
-@SuppressLint("NewApi")
-@Composable
-private fun RequiredStrongAuthTimeout() {
- val context = LocalContext.current
- val dpm = context.getDPM()
- val receiver = context.getReceiver()
- val focusMgr = LocalFocusManager.current
- var input by remember { mutableStateOf("") }
- LaunchedEffect(Unit) { input = dpm.getRequiredStrongAuthTimeout(receiver).toString() }
- Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
- Spacer(Modifier.padding(vertical = 10.dp))
- Text(text = stringResource(R.string.required_strong_auth_timeout), style = typography.headlineLarge)
- Spacer(Modifier.padding(vertical = 5.dp))
- OutlinedTextField(
- value = input,
- label = { Text(stringResource(R.string.time_unit_ms)) },
- onValueChange = { input = it },
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
- modifier = Modifier.fillMaxWidth()
- )
- Spacer(Modifier.padding(vertical = 5.dp))
- Button(
- onClick = { focusMgr.clearFocus(); dpm.setRequiredStrongAuthTimeout(receiver, input.toLong()) },
- modifier = Modifier.fillMaxWidth(),
- enabled = input != ""
- ) {
- Text(stringResource(R.string.apply))
- }
- Spacer(Modifier.padding(vertical = 10.dp))
- Information { Text(stringResource(R.string.zero_means_no_control)) }
- Spacer(Modifier.padding(vertical = 60.dp))
- }
-}
-
-@Composable
-private fun MaxFailedPasswordForWipe() {
- val context = LocalContext.current
- val dpm = context.getDPM()
- val receiver = context.getReceiver()
- val focusMgr = LocalFocusManager.current
- var inputContent by remember { mutableStateOf("") }
- LaunchedEffect(Unit) { inputContent = dpm.getMaximumFailedPasswordsForWipe(receiver).toString() }
- Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
- Spacer(Modifier.padding(vertical = 10.dp))
- Text(text = stringResource(R.string.max_pwd_fail), style = typography.headlineLarge)
- Spacer(Modifier.padding(vertical = 5.dp))
- Text(text= stringResource(R.string.max_pwd_fail_desc))
- Spacer(Modifier.padding(vertical = 5.dp))
- OutlinedTextField(
- value = inputContent,
- label = { Text(stringResource(R.string.max_pwd_fail_textfield)) },
- onValueChange = { inputContent = it },
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
- modifier = Modifier.fillMaxWidth()
- )
- Spacer(Modifier.padding(vertical = 5.dp))
- Button(
- onClick = {
- focusMgr.clearFocus()
- dpm.setMaximumFailedPasswordsForWipe(receiver, inputContent.toInt())
- Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
- },
- modifier = Modifier.fillMaxWidth(),
- enabled = inputContent != ""
- ) {
- Text(stringResource(R.string.apply))
- }
- }
-}
-
-@Composable
-private fun PasswordExpiration() {
- val context = LocalContext.current
- val dpm = context.getDPM()
- val receiver = context.getReceiver()
- val focusMgr = LocalFocusManager.current
- var inputContent by remember { mutableStateOf("") }
- LaunchedEffect(Unit) { inputContent = dpm.getPasswordExpirationTimeout(receiver).toString() }
- Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
- Spacer(Modifier.padding(vertical = 10.dp))
- Text(text = stringResource(R.string.pwd_expiration_timeout), style = typography.headlineLarge)
- Spacer(Modifier.padding(vertical = 5.dp))
- OutlinedTextField(
- value = inputContent,
- label = { Text(stringResource(R.string.time_unit_ms)) },
- onValueChange = { inputContent = it },
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
- modifier = Modifier.fillMaxWidth()
- )
- Spacer(Modifier.padding(vertical = 5.dp))
- Button(
- onClick = { focusMgr.clearFocus() ; dpm.setPasswordExpirationTimeout(receiver,inputContent.toLong()) },
- modifier = Modifier.fillMaxWidth(),
- enabled = inputContent != ""
- ) {
- Text(stringResource(R.string.apply))
- }
- }
-}
-
-@Composable
-private fun PasswordHistoryLength() {
- val context = LocalContext.current
- val dpm = context.getDPM()
- val receiver = context.getReceiver()
- val focusMgr = LocalFocusManager.current
- var inputContent by remember { mutableStateOf("") }
- LaunchedEffect(Unit) { inputContent = dpm.getPasswordHistoryLength(receiver).toString() }
- Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
- Spacer(Modifier.padding(vertical = 10.dp))
- Text(text = stringResource(R.string.pwd_history), style = typography.headlineLarge)
- Spacer(Modifier.padding(vertical = 5.dp))
- Text(text= stringResource(R.string.pwd_history_desc))
- Spacer(Modifier.padding(vertical = 5.dp))
- OutlinedTextField(
- value = inputContent,
- label = { Text(stringResource(R.string.length)) },
- onValueChange = { inputContent = it },
- keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
- modifier = Modifier.fillMaxWidth()
- )
- Spacer(Modifier.padding(vertical = 5.dp))
- Button(
- onClick = { focusMgr.clearFocus() ; dpm.setPasswordHistoryLength(receiver,inputContent.toInt()) },
- modifier = Modifier.fillMaxWidth(),
- enabled = inputContent != ""
- ) {
- Text(stringResource(R.string.apply))
- }
- }
-}
-
@Composable
private fun DisableKeyguardFeatures() {
val context = LocalContext.current
diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt
index f07adaa..2b69a10 100644
--- a/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt
@@ -26,6 +26,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -49,7 +50,7 @@ fun ShizukuActivate() {
val outputTextScrollState = rememberScrollState()
var enabled by remember { mutableStateOf(false) }
var bindShizuku by remember { mutableStateOf(false) }
- var outputText by remember { mutableStateOf("") }
+ var outputText by rememberSaveable { mutableStateOf("") }
var showDeviceAdminButton by remember { mutableStateOf(!context.isDeviceAdmin) }
var showDeviceOwnerButton by remember { mutableStateOf(!context.isDeviceOwner) }
var showOrgProfileOwnerButton by remember { mutableStateOf(true) }
diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt
index 4ead168..5b1956b 100644
--- a/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt
@@ -69,6 +69,7 @@ import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.SubPageItem
+import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.uriToStream
@@ -99,6 +100,7 @@ fun UserManage(navCtrl: NavHostController) {
) {
composable(route = "Home") { Home(localNavCtrl, scrollState) }
composable(route = "UserInfo") { CurrentUserInfo() }
+ composable(route = "Options") { Options() }
composable(route = "UserOperation") { UserOperation() }
composable(route = "CreateUser") { CreateUser() }
composable(route = "EditUsername") { Username() }
@@ -121,6 +123,9 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState) {
modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 16.dp)
)
SubPageItem(R.string.user_info, "", R.drawable.person_fill0) { navCtrl.navigate("UserInfo") }
+ if(deviceOwner && VERSION.SDK_INT >= 28) {
+ SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") }
+ }
if(deviceOwner) {
SubPageItem(R.string.user_operation, "", R.drawable.sync_alt_fill0) { navCtrl.navigate("UserOperation") }
}
@@ -144,6 +149,18 @@ private fun Home(navCtrl: NavHostController,scrollState: ScrollState) {
}
}
+@Composable
+private fun Options() {
+ val context = LocalContext.current
+ val dpm = context.getDPM()
+ val receiver = context.getReceiver()
+ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
+ if(VERSION.SDK_INT >= 28) {
+ SwitchItem(R.string.enable_logout, "", null, { dpm.isLogoutEnabled }, { dpm.setLogoutEnabled(receiver, it) })
+ }
+ }
+}
+
@Composable
private fun CurrentUserInfo() {
val context = LocalContext.current
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 7f345be..1ee51a5 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -435,6 +435,7 @@
Kullanıcı yöneticisi
+ Enable logout
Kullanıcı adını düzenle
Kullanıcı oturumunu başlat mesajı
Kullanıcı oturumunu sonlandır mesajı
@@ -483,8 +484,7 @@
Şifre geçerlilik süresi
Ekran zaman aşımı
Gereken güçlü doğrulama zaman aşımı
- 0 değeri, yöneticinin zaman aşımını kontrol etmediği anlamına gelir
- Kullanıcı kararına izin vermek için 0 girin, birim: milisaniye
+ 0 means no restriction
Şifre geçmişi uzunluğu
Belirtilen aralıktaki geçmiş şifreler kullanıcı tarafından ayarlanamaz
Yok (Şifreye izin verilmez)
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 3e5cf55..59721c9 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -29,7 +29,7 @@
自定义
未知
重置
- 时间(ms)
+ 时间(毫秒)
长度
无
无日志
@@ -427,6 +427,7 @@
用户管理
+ 允许登出
修改用户名
用户会话开始消息
用户会话结束消息
@@ -470,13 +471,12 @@
密码信息
留空以清除密码
最大密码错误次数
- 达到该限制会恢复出厂设置,0为无限制
+ 达到该限制会恢复出厂设置
错误次数
- 密码失效超时时间
+ 密码失效超时
屏幕超时
要求强验证超时
- 值为0表示OwnDroid不参与控制超时
- 超时后锁屏(毫秒),0为由用户决定
+ 0表示不做限制
密码历史长度
用户无法设置指定历史范围内之前曾设置过的密码
无(允许不设密码)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6f13500..a565f19 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -440,6 +440,7 @@
User manager
+ Enable logout
Edit username
Start user session message
End user session message
@@ -488,8 +489,7 @@
Password expiration timeout
Screen timeout
Required strong auth timeout
- A value of 0 means the admin is not participating in controlling the timeout
- Enter 0 to allow user decision, unit: millisecond
+ 0 means no restriction
Password history length
Historical passwords within the specified range cannot be set by user
None (No password allowed)