From 13fa0d7b4d5c8d1160e60a9522ccf691993829ee Mon Sep 17 00:00:00 2001 From: BinTianqi <1220958406@qq.com> Date: Thu, 18 Jan 2024 20:53:16 +0800 Subject: [PATCH] Add new features to the password section --- .../com/binbin/androidowner/MainActivity.kt | 6 +- .../com/binbin/androidowner/Permissions.kt | 4 +- .../java/com/binbin/androidowner/Security.kt | 252 +++++++++++------- app/src/main/res/values/strings.xml | 14 +- 4 files changed, 180 insertions(+), 96 deletions(-) diff --git a/app/src/main/java/com/binbin/androidowner/MainActivity.kt b/app/src/main/java/com/binbin/androidowner/MainActivity.kt index 6f4b0da..cb5ddeb 100644 --- a/app/src/main/java/com/binbin/androidowner/MainActivity.kt +++ b/app/src/main/java/com/binbin/androidowner/MainActivity.kt @@ -86,7 +86,7 @@ fun MyScaffold(mainDpm:DevicePolicyManager, mainComponent:ComponentName, mainCon "UIControl" to R.string.ui_ctrl, "ApplicationManage" to R.string.app_manage, "UserRestriction" to R.string.user_restrict, - "Security" to R.string.security + "Password" to R.string.password ) val topBarName = topBarNameMap[backStackEntry?.destination?.route]?: R.string.app_name Scaffold( @@ -135,7 +135,7 @@ fun MyScaffold(mainDpm:DevicePolicyManager, mainComponent:ComponentName, mainCon composable(route = "Permissions", content = { DpmPermissions(mainDpm,mainComponent,mainContext,navCtrl)}) composable(route = "ApplicationManage", content = { ApplicationManage(mainDpm,mainComponent,mainContext)}) composable(route = "UserRestriction", content = { UserRestriction(mainDpm,mainComponent)}) - composable(route = "Security", content = { Security(mainDpm,mainComponent,mainContext)}) + composable(route = "Password", content = { Password(mainDpm,mainComponent,mainContext)}) } } } @@ -181,7 +181,7 @@ fun HomePage(navCtrl:NavHostController,myDpm:DevicePolicyManager,myComponent:Com HomePageItem(R.string.device_ctrl, R.drawable.mobile_phone_fill0, R.string.device_ctrl_desc, "DeviceControl", navCtrl) HomePageItem(R.string.app_manage, R.drawable.apps_fill0, R.string.apps_ctrl_description, "ApplicationManage", navCtrl) HomePageItem(R.string.user_restrict, R.drawable.manage_accounts_fill0, R.string.user_restrict_desc, "UserRestriction", navCtrl) - HomePageItem(R.string.security, R.drawable.security_fill0,R.string.security_desc, "Security",navCtrl) + HomePageItem(R.string.password, R.drawable.password_fill0,R.string.security_desc, "Password",navCtrl) } } diff --git a/app/src/main/java/com/binbin/androidowner/Permissions.kt b/app/src/main/java/com/binbin/androidowner/Permissions.kt index 222aba3..28cd7ac 100644 --- a/app/src/main/java/com/binbin/androidowner/Permissions.kt +++ b/app/src/main/java/com/binbin/androidowner/Permissions.kt @@ -74,7 +74,7 @@ fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myCon Text("撤销") } }else{ - Button(onClick = { ActivateDeviceAdmin(myDpm, myComponent, myContext) }) { + Button(onClick = { ActivateDeviceAdmin(myComponent, myContext) }) { Text("激活") } } @@ -141,7 +141,7 @@ fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myCon } } -fun ActivateDeviceAdmin(myDpm: DevicePolicyManager,myComponent: ComponentName,myContext: Context){ +fun ActivateDeviceAdmin(myComponent: ComponentName,myContext: Context){ val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN) intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, myComponent) intent.putExtra( diff --git a/app/src/main/java/com/binbin/androidowner/Security.kt b/app/src/main/java/com/binbin/androidowner/Security.kt index ed74819..c169bae 100644 --- a/app/src/main/java/com/binbin/androidowner/Security.kt +++ b/app/src/main/java/com/binbin/androidowner/Security.kt @@ -7,15 +7,24 @@ import android.content.Context import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.os.Build.VERSION import android.widget.Toast -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Check import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextField @@ -27,40 +36,52 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat.startActivity @Composable -fun Security(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Context){ +fun Password(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Context){ var newPwd by remember{ mutableStateOf("") } var confirmed by remember{ mutableStateOf(false) } + val focusMgr = LocalFocusManager.current Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) ) { val myByteArray by remember{ mutableStateOf(byteArrayOf(1,1,4,5,1,4,1,9,1,9,8,1,0,1,1,4,5,1,4,1,9,1,9,8,1,0,1,1,4,5,1,4,1,9,1,9,8,1,0)) } + Text( + text = "以下操作可能会造成不可挽回的损失,请先备份好数据。执行操作时一定要谨慎!!!", + color = MaterialTheme.colorScheme.onErrorContainer, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 4.dp) + .clip(RoundedCornerShape(15)) + .background(color = MaterialTheme.colorScheme.errorContainer) + .padding(8.dp) + ) if(VERSION.SDK_INT>=26){ Column( horizontalAlignment = Alignment.Start, modifier = Modifier .fillMaxWidth() - .padding(8.dp) + .padding(horizontal = 8.dp, vertical = 4.dp) .clip(RoundedCornerShape(10)) .background(color = MaterialTheme.colorScheme.primaryContainer) .padding(8.dp) ) { - Text( - text = "密码重置令牌", - style = MaterialTheme.typography.titleLarge - ) + Text(text = "密码重置令牌", style = MaterialTheme.typography.titleLarge) Row { Button( onClick = { - if(myDpm.clearResetPasswordToken(myComponent)){ - Toast.makeText(myContext, "清除成功", Toast.LENGTH_SHORT).show() - }else{ - Toast.makeText(myContext, "清除失败", Toast.LENGTH_SHORT).show() - } + if(myDpm.clearResetPasswordToken(myComponent)){ Toast.makeText(myContext, "清除成功", Toast.LENGTH_SHORT).show() + }else{ Toast.makeText(myContext, "清除失败", Toast.LENGTH_SHORT).show() } }, modifier = Modifier.padding(end = 8.dp) ) { @@ -68,11 +89,8 @@ fun Security(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte } Button( onClick = { - if(myDpm.setResetPasswordToken(myComponent, myByteArray)){ - Toast.makeText(myContext, "设置成功", Toast.LENGTH_SHORT).show() - }else{ - Toast.makeText(myContext, "设置失败", Toast.LENGTH_SHORT).show() - } + if(myDpm.setResetPasswordToken(myComponent, myByteArray)){ Toast.makeText(myContext, "设置成功", Toast.LENGTH_SHORT).show() + }else{ Toast.makeText(myContext, "设置失败", Toast.LENGTH_SHORT).show() } }, modifier = Modifier.padding(end = 8.dp) ) { @@ -81,14 +99,9 @@ fun Security(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte Button( onClick = { if(!myDpm.isResetPasswordTokenActive(myComponent)){ - try{ - activateToken(myContext) - }catch(e:NullPointerException){ - Toast.makeText(myContext, "请先设置令牌", Toast.LENGTH_SHORT).show() - } - }else{ - Toast.makeText(myContext, "已经激活", Toast.LENGTH_SHORT).show() - } + try{ activateToken(myContext) + }catch(e:NullPointerException){ Toast.makeText(myContext, "请先设置令牌", Toast.LENGTH_SHORT).show() } + }else{ Toast.makeText(myContext, "已经激活", Toast.LENGTH_SHORT).show() } } ) { Text("激活") @@ -97,83 +110,144 @@ fun Security(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte Text("没有密码时会自动激活令牌") } } - TextField( - value = newPwd, - onValueChange = {newPwd=it}, - enabled = !confirmed, - label = { Text("密码")} - ) - Text( - text = "(留空可以清除密码)", - modifier = Modifier.padding(vertical = 5.dp) - ) - Row { - Button( - onClick = { - if(newPwd.length>=4||newPwd.isEmpty()){ - confirmed=!confirmed - }else{ - Toast.makeText(myContext, "需要4位数字或字母", Toast.LENGTH_SHORT).show() - } - }, - modifier = Modifier.padding(end = 10.dp) - ) { - Text("确认密码") - } - if(VERSION.SDK_INT>=26){ + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 4.dp) + .clip(RoundedCornerShape(10)) + .background(color = MaterialTheme.colorScheme.primaryContainer) + .padding(10.dp) + ) { + TextField( + value = newPwd, + onValueChange = {newPwd=it}, + enabled = !confirmed, + label = { Text("密码")}, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}) + ) + Text(text = stringResource(R.string.reset_pwd_desc), modifier = Modifier.padding(vertical = 5.dp)) + Row { Button( onClick = { - val resetSuccess = myDpm.resetPasswordWithToken(myComponent,newPwd,myByteArray,0) - if(resetSuccess){ - Toast.makeText(myContext, "设置成功", Toast.LENGTH_SHORT).show() - }else{ - Toast.makeText(myContext, "设置失败", Toast.LENGTH_SHORT).show() - } - confirmed=false + if(newPwd.length>=4||newPwd.isEmpty()){ confirmed=!confirmed + }else{ Toast.makeText(myContext, "需要4位数字或字母", Toast.LENGTH_SHORT).show() } }, - enabled = confirmed, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.error, - contentColor = MaterialTheme.colorScheme.onError - ) + modifier = Modifier.padding(end = 10.dp) ) { - Text("设置密码") + Text("确认密码") } - }else{ - Button( - onClick = { - val resetSuccess = myDpm.resetPassword(newPwd,0) - if(resetSuccess){ - Toast.makeText(myContext, "设置成功", Toast.LENGTH_SHORT).show() - }else{ - Toast.makeText(myContext, "设置失败", Toast.LENGTH_SHORT).show() - } - confirmed=false - }, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.error, - contentColor = MaterialTheme.colorScheme.onError - ) - ) { - Text("设置密码") + if(VERSION.SDK_INT>=26){ + Button( + onClick = { + val resetSuccess = myDpm.resetPasswordWithToken(myComponent,newPwd,myByteArray,0) + if(resetSuccess){ Toast.makeText(myContext, "设置成功", Toast.LENGTH_SHORT).show() + }else{ Toast.makeText(myContext, "设置失败", Toast.LENGTH_SHORT).show() } + confirmed=false + }, + enabled = confirmed, + colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.error, contentColor = MaterialTheme.colorScheme.onError) + ) { + Text("设置密码") + } + }else{ + Button( + onClick = { + val resetSuccess = myDpm.resetPassword(newPwd,0) + if(resetSuccess){ Toast.makeText(myContext, "设置成功", Toast.LENGTH_SHORT).show() + }else{ Toast.makeText(myContext, "设置失败", Toast.LENGTH_SHORT).show() } + confirmed=false + }, + colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.error, contentColor = MaterialTheme.colorScheme.onError) + ) { + Text("设置密码") + } } } } - Text( - text = "该操作可能会造成不可挽回的损失,请先备份好数据。设置密码的时候一定要谨慎!!!", - color = MaterialTheme.colorScheme.onErrorContainer, + PasswordItem(R.string.max_pwd_fail,R.string.max_pwd_fail_desc,R.string.max_pwd_fail_textfield, focusMgr,false, + {myDpm.getMaximumFailedPasswordsForWipe(null).toString()},{ic -> myDpm.setMaximumFailedPasswordsForWipe(myComponent, ic.toInt()) }) + PasswordItem(R.string.pwd_timeout,R.string.pwd_timeout_desc,R.string.pwd_timeout_textfield, focusMgr,true, + {myDpm.getPasswordExpirationTimeout(null).toString()},{ic -> myDpm.setPasswordExpirationTimeout(myComponent, ic.toLong()) }) + PasswordItem(R.string.pwd_history,R.string.pwd_history_desc,R.string.pwd_history_textfield, focusMgr,true, + {myDpm.getPasswordHistoryLength(null).toString()},{ic -> myDpm.setPasswordHistoryLength(myComponent, ic.toInt()) }) + + } +} + +@Composable +fun PasswordItem( + itemName:Int, + itemDesc:Int, + textFieldLabel:Int, + focusMgr:FocusManager, + allowZero:Boolean, + getMethod:()->String, + setMethod:(ic:String)->Unit +){ + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 4.dp) + .clip(RoundedCornerShape(10)) + .background(color = MaterialTheme.colorScheme.primaryContainer) + .padding(10.dp) + ) { + var inputContent by remember{ mutableStateOf(getMethod()) } + var inputContentEdited by remember{ mutableStateOf(false) } + var ableToApply by remember{ mutableStateOf(true) } + Text(text = stringResource(itemName), style = MaterialTheme.typography.titleLarge) + Text(text= stringResource(itemDesc),modifier=Modifier.padding(vertical = 2.dp)) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier .fillMaxWidth() - .padding(8.dp) - .clip(RoundedCornerShape(15)) - .background(color = MaterialTheme.colorScheme.errorContainer) - .padding(8.dp) - ) + .padding(end = 8.dp) + ){ + TextField( + value = inputContent, + label = { Text(stringResource(textFieldLabel))}, + onValueChange = { + inputContent = it + if(inputContent!=""&&((inputContent=="0"&&allowZero)||inputContent!="0")){ + inputContentEdited = inputContent!=getMethod() + ableToApply = true + }else{ + ableToApply = false + } + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}) + ) + if(ableToApply){ + Icon( + imageVector = Icons.Outlined.Check, + contentDescription = null, + tint = if(inputContentEdited){MaterialTheme.colorScheme.onError}else{MaterialTheme.colorScheme.onPrimary}, + modifier = Modifier + .clip(RoundedCornerShape(20)) + .background( + color = if (inputContentEdited) { + MaterialTheme.colorScheme.error + } else { + MaterialTheme.colorScheme.primary + } + ) + .clickable(onClick = { + focusMgr.clearFocus() + setMethod(inputContent) + inputContentEdited = inputContent != getMethod() + }) + .padding(8.dp) + ) + } + } } } fun activateToken(myContext: Context){ - val ACTIVATE_TOKEN_PROMPT = "Use your credentials to enable remote password reset" + val ACTIVATE_TOKEN_PROMPT = "在这里激活密码重置令牌" val keyguardManager = myContext.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager val confirmIntent = keyguardManager.createConfirmDeviceCredentialIntent(null, ACTIVATE_TOKEN_PROMPT) confirmIntent.setFlags(FLAG_ACTIVITY_NEW_TASK) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index caade63..a9ee006 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,7 +47,17 @@ 有时候不能用 禁止用户控制 阻止清除应用数据和缓存 - 安全 + 密码 安装未知来源应用 - 修改或清除锁屏密码> + 锁屏密码相关操作 + 最大密码错误次数 + 达到该限制会恢复出厂设置 + 错误次数 + 密码失效超时时间 + 超时后用户需重新设置密码(毫秒),0为无限制 + 超时时间 + 密码历史记录长度 + 用户输入的密码不能与历史记录中的任何密码相同,0为无限制 + 历史记录长度 + 留空可以清除密码,纯数字将使用PIN码 \ No newline at end of file