optimize activate and deactivate logic

This commit is contained in:
BinTianqi
2024-05-02 17:01:25 +08:00
parent 83a924378d
commit 3d09eb958b
6 changed files with 135 additions and 146 deletions

View File

@@ -1,74 +1,46 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.annotation.SuppressLint
import android.app.admin.DeviceAdminReceiver import android.app.admin.DeviceAdminReceiver
import android.app.admin.DevicePolicyManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageInstaller.* import android.content.pm.PackageInstaller.*
import android.os.Build.VERSION
import android.os.PersistableBundle
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity
import com.bintianqi.owndroid.dpm.isDeviceOwner
import com.bintianqi.owndroid.dpm.isProfileOwner
class Receiver : DeviceAdminReceiver() { class Receiver : DeviceAdminReceiver() {
override fun onEnabled(context: Context, intent: Intent) { override fun onEnabled(context: Context, intent: Intent) {
super.onEnabled(context, intent) super.onEnabled(context, intent)
Toast.makeText(context, "已启用", Toast.LENGTH_SHORT).show() val myDpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(context, this::class.java)
if(myDpm.isAdminActive(myComponent)||isProfileOwner(myDpm)||isDeviceOwner(myDpm)){
Toast.makeText(context, context.getString(R.string.onEnabled), Toast.LENGTH_SHORT).show()
if(myDpm.isAdminActive(myComponent)&&!isProfileOwner(myDpm)&&!isDeviceOwner(myDpm)){ backToHome=true }
}
} }
override fun onTransferOwnershipComplete(context: Context, bundle: PersistableBundle?) { override fun onDisabled(context: Context, intent: Intent) {
super.onTransferOwnershipComplete(context, bundle) super.onDisabled(context, intent)
if(bundle!=null){ Toast.makeText(context, context.getString(R.string.onDisabled), Toast.LENGTH_SHORT).show()
Toast.makeText(context,"转移所有权完毕,附加内容长度:${bundle.size()}",Toast.LENGTH_SHORT).show()
Log.d("TransferOwnerShip",bundle.toString())
}else{
Toast.makeText(context,"转移所有权完毕,无附加内容}",Toast.LENGTH_SHORT).show()
}
} }
override fun onProfileProvisioningComplete(context: Context, intent: Intent) { override fun onProfileProvisioningComplete(context: Context, intent: Intent) {
super.onProfileProvisioningComplete(context, intent) super.onProfileProvisioningComplete(context, intent)
Toast.makeText(context, "创建工作资料完成", Toast.LENGTH_SHORT).show() Toast.makeText(context, context.getString(R.string.create_work_profile_success), Toast.LENGTH_SHORT).show()
} }
@SuppressLint("UnsafeProtectedBroadcastReceiver")
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
}
override fun onNetworkLogsAvailable(context: Context, intent: Intent, batchToken: Long, networkLogsCount: Int) {
super.onNetworkLogsAvailable(context, intent, batchToken, networkLogsCount)
Toast.makeText(context,"可以收集网络日志",Toast.LENGTH_SHORT).show()
Log.e("","网络日志可用")
}
override fun onSecurityLogsAvailable(context: Context, intent: Intent) {
super.onSecurityLogsAvailable(context, intent)
Toast.makeText(context,"可以收集安全日志",Toast.LENGTH_SHORT).show()
Log.e("","安全日志可用")
}
override fun onDisableRequested(context: Context, intent: Intent): CharSequence {
Toast.makeText(context, "撤销授权", Toast.LENGTH_SHORT).show()
return "这是取消时的提示"
}
override fun onDisabled(context: Context, intent: Intent) {
super.onDisabled(context, intent)
Toast.makeText(context, "已禁用", Toast.LENGTH_SHORT).show()
}
override fun onSystemUpdatePending(context: Context, intent: Intent, receivedTime: Long) {
if (VERSION.SDK_INT < 26) { return }
Toast.makeText(context, "新的系统更新!", Toast.LENGTH_SHORT).show()
}
} }
class PackageInstallerReceiver:BroadcastReceiver(){ class PackageInstallerReceiver:BroadcastReceiver(){
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val toastText = when(intent.getIntExtra(EXTRA_STATUS,666)){ val toastText = when(intent.getIntExtra(EXTRA_STATUS,666)){
STATUS_PENDING_USER_ACTION->"等待用户交互" STATUS_PENDING_USER_ACTION->"等待用户交互"
STATUS_SUCCESS->"成功" STATUS_SUCCESS->context.getString(R.string.success)
STATUS_FAILURE->"失败" STATUS_FAILURE->context.getString(R.string.fail)
STATUS_FAILURE_BLOCKED->"失败:被阻止" STATUS_FAILURE_BLOCKED->"失败:被阻止"
STATUS_FAILURE_ABORTED->"失败:被打断" STATUS_FAILURE_ABORTED->"失败:被打断"
STATUS_FAILURE_INVALID->"失败:无效" STATUS_FAILURE_INVALID->"失败:无效"
@@ -76,8 +48,8 @@ class PackageInstallerReceiver:BroadcastReceiver(){
STATUS_FAILURE_STORAGE->"失败:空间不足" STATUS_FAILURE_STORAGE->"失败:空间不足"
STATUS_FAILURE_INCOMPATIBLE->"失败:不兼容" STATUS_FAILURE_INCOMPATIBLE->"失败:不兼容"
STATUS_FAILURE_TIMEOUT->"失败:超时" STATUS_FAILURE_TIMEOUT->"失败:超时"
else->"未知" else->context.getString(R.string.unknown)
} }
Log.e("静默安装","${intent.getIntExtra(EXTRA_STATUS,666)}:$toastText") Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show()
} }
} }

View File

@@ -53,14 +53,6 @@ fun Set<Any>.toText():String{
fun writeClipBoard(context: Context, string: String):Boolean{ fun writeClipBoard(context: Context, string: String):Boolean{
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
try { try {
if(VERSION.SDK_INT>=23){
val hasPermission: Boolean = clipboardManager.hasPrimaryClip()
if(!hasPermission) {
val intent = Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS)
intent.setData(Uri.parse("package:"+context.packageName))
startActivity(context,intent,null)
}
}
clipboardManager.setPrimaryClip(ClipData.newPlainText("", string)) clipboardManager.setPrimaryClip(ClipData.newPlainText("", string))
}catch(e:Exception){ }catch(e:Exception){
return false return false

View File

@@ -9,6 +9,7 @@ import android.content.Intent
import android.os.Build.VERSION import android.os.Build.VERSION
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.* import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
@@ -191,30 +192,29 @@ private fun DeviceAdmin(navCtrl: NavHostController){
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(myContext,Receiver::class.java) val myComponent = ComponentName(myContext,Receiver::class.java)
val co = rememberCoroutineScope() val co = rememberCoroutineScope()
var showDeactivateButton by remember{mutableStateOf(myDpm.isAdminActive(myComponent))}
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.device_admin), style = typography.headlineLarge) Text(text = stringResource(R.string.device_admin), style = typography.headlineLarge)
Text(text = stringResource(if(myDpm.isAdminActive(myComponent)) { R.string.activated } else { R.string.deactivated }), style = typography.titleLarge) Text(text = stringResource(if(myDpm.isAdminActive(myComponent)) { R.string.activated } else { R.string.deactivated }), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(myDpm.isAdminActive(myComponent)) { AnimatedVisibility(showDeactivateButton) {
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)) {
Button( Button(
onClick = { onClick = {
myDpm.removeActiveAdmin(myComponent) myDpm.removeActiveAdmin(myComponent)
co.launch{ delay(600); if(!myDpm.isAdminActive(myComponent)){navCtrl.navigateUp()} } co.launch{ delay(400); showDeactivateButton=myDpm.isAdminActive(myComponent) }
}, },
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError) colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
) { ) {
Text(stringResource(R.string.deactivate)) Text(stringResource(R.string.deactivate))
} }
} }
} else { AnimatedVisibility(!showDeactivateButton) {
Column {
Button(onClick = {activateDeviceAdmin(myContext, myComponent)}, modifier = Modifier.fillMaxWidth()) { Button(onClick = {activateDeviceAdmin(myContext, myComponent)}, modifier = Modifier.fillMaxWidth()) {
Text(stringResource(R.string.activate)) Text(stringResource(R.string.activate_jump))
}
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(!myDpm.isAdminActive(myComponent)) {
SelectionContainer { SelectionContainer {
Text(text = stringResource(R.string.activate_device_admin_command)) Text(text = stringResource(R.string.activate_device_admin_command))
} }
@@ -222,30 +222,35 @@ private fun DeviceAdmin(navCtrl: NavHostController){
} }
} }
} }
}
@Composable @Composable
private fun ProfileOwner(){ private fun ProfileOwner(){
val myContext = LocalContext.current val myContext = LocalContext.current
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(myContext,Receiver::class.java) val myComponent = ComponentName(myContext,Receiver::class.java)
var showDeactivateButton by remember{mutableStateOf(isProfileOwner(myDpm))}
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.profile_owner), style = typography.headlineLarge) Text(text = stringResource(R.string.profile_owner), style = typography.headlineLarge)
Text(stringResource(if(isProfileOwner(myDpm)){R.string.activated}else{R.string.deactivated}), style = typography.titleLarge) Text(stringResource(if(isProfileOwner(myDpm)){R.string.activated}else{R.string.deactivated}), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(isProfileOwner(myDpm)&&VERSION.SDK_INT>=24){ if(VERSION.SDK_INT>=24){
AnimatedVisibility(showDeactivateButton) {
val co = rememberCoroutineScope() val co = rememberCoroutineScope()
Button( Button(
onClick = { onClick = {
myDpm.clearProfileOwner(myComponent) myDpm.clearProfileOwner(myComponent)
co.launch { delay(600); if(!isProfileOwner(myDpm)){ backToHome=true } } co.launch { delay(400); showDeactivateButton=isProfileOwner(myDpm) }
}, },
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError) colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
) { ) {
Text(stringResource(R.string.deactivate)) Text(stringResource(R.string.deactivate))
} }
} }
if(!isProfileOwner(myDpm)){ }
AnimatedVisibility(!showDeactivateButton) {
Column {
SelectionContainer{ SelectionContainer{
Text(text = stringResource(R.string.activate_profile_owner_command)) Text(text = stringResource(R.string.activate_profile_owner_command))
} }
@@ -253,29 +258,32 @@ private fun ProfileOwner(){
} }
} }
} }
}
@Composable @Composable
private fun DeviceOwner(navCtrl: NavHostController){ private fun DeviceOwner(navCtrl: NavHostController){
val myContext = LocalContext.current val myContext = LocalContext.current
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val co = rememberCoroutineScope() val co = rememberCoroutineScope()
var showDeactivateButton by remember{mutableStateOf(isDeviceOwner(myDpm))}
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){ Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)){
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.device_owner), style = typography.headlineLarge) Text(text = stringResource(R.string.device_owner), style = typography.headlineLarge)
Text(text = stringResource(if(isDeviceOwner(myDpm)){R.string.activated}else{R.string.deactivated}), style = typography.titleLarge) Text(text = stringResource(if(isDeviceOwner(myDpm)){R.string.activated}else{R.string.deactivated}), style = typography.titleLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(isDeviceOwner(myDpm)){ AnimatedVisibility(showDeactivateButton) {
Button( Button(
onClick = { onClick = {
myDpm.clearDeviceOwnerApp(myContext.packageName) myDpm.clearDeviceOwnerApp(myContext.packageName)
co.launch{ delay(600); if(!isDeviceOwner(myDpm)){ backToHome=true } } co.launch{ delay(400); showDeactivateButton=isDeviceOwner(myDpm) }
}, },
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError) colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError)
) { ) {
Text(text = stringResource(R.string.deactivate)) Text(text = stringResource(R.string.deactivate))
} }
} }
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){ AnimatedVisibility(!showDeactivateButton) {
Column {
SelectionContainer{ SelectionContainer{
Text(text = stringResource(R.string.activate_device_owner_command)) Text(text = stringResource(R.string.activate_device_owner_command))
} }
@@ -283,6 +291,7 @@ private fun DeviceOwner(navCtrl: NavHostController){
} }
} }
} }
}
@Composable @Composable
fun DeviceInfo(){ fun DeviceInfo(){

View File

@@ -32,7 +32,6 @@ import androidx.compose.ui.unit.dp
import com.bintianqi.owndroid.IUserService import com.bintianqi.owndroid.IUserService
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.Receiver import com.bintianqi.owndroid.Receiver
import com.bintianqi.owndroid.backToHome
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import rikka.shizuku.Shizuku import rikka.shizuku.Shizuku
@@ -49,6 +48,10 @@ fun ShizukuActivate(){
var enabled by remember{ mutableStateOf(false) } var enabled by remember{ mutableStateOf(false) }
var bindShizuku by remember{ mutableStateOf(false) } var bindShizuku by remember{ mutableStateOf(false) }
var outputText by remember{mutableStateOf("")} var outputText by remember{mutableStateOf("")}
var showDeviceAdminButton by remember{mutableStateOf(!myDpm.isAdminActive(myComponent))}
var showProfileOwnerButton by remember{mutableStateOf(!isProfileOwner(myDpm))}
var showDeviceOwnerButton by remember{mutableStateOf(!isDeviceOwner(myDpm))}
var showOrgProfileOwnerButton by remember{mutableStateOf(true)}
LaunchedEffect(Unit){ LaunchedEffect(Unit){
if(service==null){userServiceControl(myContext, true)} if(service==null){userServiceControl(myContext, true)}
while(true){ while(true){
@@ -104,16 +107,14 @@ fun ShizukuActivate(){
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){ AnimatedVisibility(showDeviceAdminButton&&showProfileOwnerButton&&showDeviceOwnerButton) {
Column {
if(!myDpm.isAdminActive(myComponent)){
Button( Button(
onClick = { onClick = {
coScope.launch{ coScope.launch{
outputText = service!!.execute(myContext.getString(R.string.dpm_activate_da_command)) outputText = service!!.execute(myContext.getString(R.string.dpm_activate_da_command))
outputTextScrollState.animateScrollTo(0, scrollAnim()) outputTextScrollState.animateScrollTo(0, scrollAnim())
delay(600) delay(500)
if(myDpm.isAdminActive(myComponent)){backToHome=true} showDeviceAdminButton = !myDpm.isAdminActive(myComponent)
} }
}, },
enabled = enabled enabled = enabled
@@ -122,41 +123,42 @@ fun ShizukuActivate(){
} }
} }
AnimatedVisibility(showProfileOwnerButton&&showDeviceOwnerButton) {
Button( Button(
onClick = { onClick = {
coScope.launch{ coScope.launch{
outputText = service!!.execute(myContext.getString(R.string.dpm_activate_po_command)) outputText = service!!.execute(myContext.getString(R.string.dpm_activate_po_command))
outputTextScrollState.animateScrollTo(0, scrollAnim()) outputTextScrollState.animateScrollTo(0, scrollAnim())
delay(600) delay(600)
if(isProfileOwner(myDpm)){backToHome=true} showProfileOwnerButton = !isProfileOwner(myDpm)
} }
}, },
enabled = enabled enabled = enabled
) { ) {
Text(text = stringResource(R.string.activate_profile_owner)) Text(text = stringResource(R.string.activate_profile_owner))
} }
}
AnimatedVisibility(showDeviceOwnerButton&&showProfileOwnerButton) {
Button( Button(
onClick = { onClick = {
coScope.launch{ coScope.launch{
outputText = service!!.execute(myContext.getString(R.string.dpm_activate_do_command)) outputText = service!!.execute(myContext.getString(R.string.dpm_activate_do_command))
outputTextScrollState.animateScrollTo(0, scrollAnim()) outputTextScrollState.animateScrollTo(0, scrollAnim())
delay(600) delay(500)
if(isDeviceOwner(myDpm)){backToHome=true} showDeviceOwnerButton = !isDeviceOwner(myDpm)
} }
}, },
enabled = enabled enabled = enabled
) { ) {
Text(text = stringResource(R.string.activate_device_owner)) Text(text = stringResource(R.string.activate_device_owner))
} }
}
} }
if( if(
VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent) VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&!myDpm.isOrganizationOwnedDeviceWithManagedProfile
){ ){
Column { AnimatedVisibility(showOrgProfileOwnerButton) {
Button( Button(
onClick = { onClick = {
coScope.launch{ coScope.launch{
@@ -165,6 +167,8 @@ fun ShizukuActivate(){
"dpm mark-profile-owner-on-organization-owned-device --user $userID com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver" "dpm mark-profile-owner-on-organization-owned-device --user $userID com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver"
) )
outputTextScrollState.animateScrollTo(0, scrollAnim()) outputTextScrollState.animateScrollTo(0, scrollAnim())
delay(500)
showOrgProfileOwnerButton = !myDpm.isOrganizationOwnedDeviceWithManagedProfile
} }
}, },
enabled = enabled enabled = enabled

View File

@@ -46,6 +46,7 @@
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">点击以激活</string> <string name="click_to_activate">点击以激活</string>
<string name="device_admin">Device admin</string> <string name="device_admin">Device admin</string>
<string name="activate_jump" tools:ignore="TypographyEllipsis">激活...</string>
<string name="profile_owner">Profile owner</string> <string name="profile_owner">Profile owner</string>
<string name="device_owner">Device owner</string> <string name="device_owner">Device owner</string>
<string name="activate_device_admin">激活Device admin</string> <string name="activate_device_admin">激活Device admin</string>
@@ -75,6 +76,11 @@
<string name="transform">转移</string> <string name="transform">转移</string>
<string name="activate_device_admin_here">在这里激活Device admin</string> <string name="activate_device_admin_here">在这里激活Device admin</string>
<!--Receiver-->
<string name="onEnabled">OwnDroid已启用</string>
<string name="onDisabled">OwnDroid已禁用</string>
<string name="create_work_profile_success">OwnDroid创建工作资料成功</string>
<!--Shizuku--> <!--Shizuku-->
<string name="check_shizuku">检查Shizuku</string> <string name="check_shizuku">检查Shizuku</string>
<string name="list_owners">列出Owners</string> <string name="list_owners">列出Owners</string>

View File

@@ -49,6 +49,7 @@
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">Click to activate</string> <string name="click_to_activate">Click to activate</string>
<string name="device_admin">Device admin</string> <string name="device_admin">Device admin</string>
<string name="activate_jump" tools:ignore="TypographyEllipsis">Activate...</string>
<string name="profile_owner">Profile owner</string> <string name="profile_owner">Profile owner</string>
<string name="device_owner">Device owner</string> <string name="device_owner">Device owner</string>
<string name="activate_device_admin">Activate Device admin</string> <string name="activate_device_admin">Activate Device admin</string>
@@ -82,6 +83,11 @@
<string name="transform">Transform</string> <string name="transform">Transform</string>
<string name="activate_device_admin_here">Activate Device admin here. </string> <string name="activate_device_admin_here">Activate Device admin here. </string>
<!--Receiver-->
<string name="onEnabled">OwnDroid: Enabled</string>
<string name="onDisabled">OwnDroid: Disabled</string>
<string name="create_work_profile_success">OwnDroid: Create work profile success</string>
<!--Shizuku--> <!--Shizuku-->
<string name="shizuku" translatable="false">Shizuku</string> <string name="shizuku" translatable="false">Shizuku</string>
<string name="check_shizuku">Check permission</string> <string name="check_shizuku">Check permission</string>