optimize shizuku coroutine

This commit is contained in:
BinTianqi
2024-03-03 13:55:23 +08:00
parent e27d4b41b2
commit f4fa42c4fc
2 changed files with 79 additions and 73 deletions

View File

@@ -36,6 +36,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import rikka.shizuku.Shizuku import rikka.shizuku.Shizuku
import java.io.* import java.io.*
@@ -50,9 +53,10 @@ fun ShizukuActivate(){
val isWear = sharedPref.getBoolean("isWear",false) val isWear = sharedPref.getBoolean("isWear",false)
val bodyTextStyle = if(isWear){ typography.bodyMedium }else{ typography.bodyLarge } val bodyTextStyle = if(isWear){ typography.bodyMedium }else{ typography.bodyLarge }
val filesDir = myContext.filesDir val filesDir = myContext.filesDir
var launchExtractRish by remember{mutableStateOf(true)} LaunchedEffect(Unit){ extractRish(myContext) }
LaunchedEffect(launchExtractRish){ if(launchExtractRish){ extractRish(myContext);launchExtractRish=false } } val coScope = rememberCoroutineScope()
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
val outputTextScrollState = rememberScrollState()
Column(modifier = Modifier.verticalScroll(scrollState)){ Column(modifier = Modifier.verticalScroll(scrollState)){
var outputText by remember{mutableStateOf("")} var outputText by remember{mutableStateOf("")}
if(Binder.getCallingUid()/100000!=0){ if(Binder.getCallingUid()/100000!=0){
@@ -82,40 +86,40 @@ fun ShizukuActivate(){
Text(text = stringResource(R.string.activate), style = typography.titleLarge, color = colorScheme.onPrimaryContainer) Text(text = stringResource(R.string.activate), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
if(!myDpm.isAdminActive(myComponent)){ if(!myDpm.isAdminActive(myComponent)){
var launchActivateDA by remember{mutableStateOf(false)} Button(
LaunchedEffect(launchActivateDA){ onClick = {
if(launchActivateDA){ coScope.launch{
outputText = executeCommand("sh rish.sh", "dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir) outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_da_command), null, filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim()) scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
launchActivateDA=false outputTextScrollState.animateScrollTo(0, scrollAnim())
} }
} },
Button(onClick = {launchActivateDA=true}, modifier = Modifier.fillMaxWidth()) { modifier = Modifier.fillMaxWidth()) {
Text(text = "Device admin") Text(text = "Device admin")
} }
} }
var launchActivatePO by remember{mutableStateOf(false)} Button(
LaunchedEffect(launchActivatePO){ onClick = {
if(launchActivatePO){ coScope.launch{
outputText = executeCommand("sh rish.sh", "dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir) outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_po_command), null, filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim()) scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
launchActivatePO=false outputTextScrollState.animateScrollTo(0, scrollAnim())
} }
} },
Button(onClick = {launchActivatePO=true}, modifier = Modifier.fillMaxWidth()) { modifier = Modifier.fillMaxWidth()) {
Text(text = "Profile owner") Text(text = "Profile owner")
} }
var launchActivateDO by remember{mutableStateOf(false)} Button(
LaunchedEffect(launchActivateDO){ onClick = {
if(launchActivateDO){ coScope.launch{
outputText = executeCommand("sh rish.sh", "dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir) outputText = executeCommand(myContext, "sh rish.sh", myContext.getString(R.string.dpm_activate_do_command), null, filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim()) scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
launchActivateDO=false outputTextScrollState.animateScrollTo(0, scrollAnim())
} }
} },
Button(onClick = {launchActivateDO=true}, modifier = Modifier.fillMaxWidth()) { modifier = Modifier.fillMaxWidth()) {
Text(text = "Device owner") Text(text = "Device owner")
} }
@@ -134,25 +138,20 @@ fun ShizukuActivate(){
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}), keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp) modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 2.dp)
) )
var launchActivateOrgProfile by remember{mutableStateOf(false)} Button(
LaunchedEffect(launchActivateOrgProfile){ onClick = {
if(launchActivateOrgProfile){ coScope.launch{
focusMgr.clearFocus() focusMgr.clearFocus()
outputText = executeCommand( outputText = executeCommand(
"sh rish.sh", myContext, "sh rish.sh", myContext.getString(R.string.activate_org_profile_command_with_user_id, inputUserID),
"dpm mark-profile-owner-on-organization-owned-device --user $inputUserID com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
null, filesDir null, filesDir
) )
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim()) scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
launchActivateOrgProfile=false outputTextScrollState.animateScrollTo(0, scrollAnim())
}
}
Button(
onClick = {
launchActivateOrgProfile=true
if(myDpm.isOrganizationOwnedDeviceWithManagedProfile){ if(myDpm.isOrganizationOwnedDeviceWithManagedProfile){
Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show() Toast.makeText(myContext, myContext.getString(R.string.success),Toast.LENGTH_SHORT).show()
} }
}
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -161,36 +160,35 @@ fun ShizukuActivate(){
} }
} }
var launchListOwners by remember{mutableStateOf(false)}
LaunchedEffect(launchListOwners){
if(launchListOwners){
outputText=executeCommand("sh rish.sh","dpm list-owners",null,filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
launchListOwners=false
}
}
Button( Button(
onClick = {launchListOwners=true}, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp) onClick = {
coScope.launch{
outputText=executeCommand(myContext, "sh rish.sh","dpm list-owners",null,filesDir)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
outputTextScrollState.animateScrollTo(0, scrollAnim())
}
},
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
) { ) {
Text(text = stringResource(R.string.list_owners)) Text(text = stringResource(R.string.list_owners))
} }
var launchTest by remember{mutableStateOf(false)} Button(
LaunchedEffect(launchTest){ onClick = {
if(launchTest){ coScope.launch {
outputText= myContext.getString(R.string.should_contain_2000_or_0) outputText= myContext.getString(R.string.should_contain_2000_or_0)
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim()) scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
outputText+=executeCommand("sh rish.sh","id",null,filesDir) outputTextScrollState.animateScrollTo(0, scrollAnim())
launchTest=false outputText+=executeCommand(myContext, "sh rish.sh","id",null,filesDir)
} }
} },
Button( modifier = Modifier.align(Alignment.CenterHorizontally)
onClick = {launchTest=true}, modifier = Modifier.align(Alignment.CenterHorizontally)
) { ) {
Text(text = stringResource(R.string.test_rish)) Text(text = stringResource(R.string.test_rish))
} }
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState())){ SelectionContainer(modifier = Modifier.horizontalScroll(outputTextScrollState)){
Text(text = outputText, style = bodyTextStyle, softWrap = false, modifier = Modifier.padding(4.dp)) Text(text = outputText, style = bodyTextStyle, softWrap = false, modifier = Modifier.padding(4.dp))
} }
@@ -227,25 +225,25 @@ private fun checkPermission(myContext: Context):String {
try{ try{
if(Shizuku.checkSelfPermission()==PackageManager.PERMISSION_GRANTED) { if(Shizuku.checkSelfPermission()==PackageManager.PERMISSION_GRANTED) {
val permission = when(Shizuku.getUid()){ 0->"Root"; 2000->"Shell"; else->myContext.getString(R.string.unknown) } val permission = when(Shizuku.getUid()){ 0->"Root"; 2000->"Shell"; else->myContext.getString(R.string.unknown) }
myContext.getString(R.string.shizuku_permission_granted, Shizuku.getVersion(), permission) myContext.getString(R.string.shizuku_permission_granted, Shizuku.getVersion().toString(), permission)
}else if(Shizuku.shouldShowRequestPermissionRationale()){ myContext.getString(R.string.denied) } }else if(Shizuku.shouldShowRequestPermissionRationale()){ myContext.getString(R.string.denied) }
else{ Shizuku.requestPermission(0); myContext.getString(R.string.request_permission) } else{ Shizuku.requestPermission(0); myContext.getString(R.string.request_permission) }
}catch(e: Throwable){ myContext.getString(R.string.shizuku_not_started) } }catch(e: Throwable){ myContext.getString(R.string.shizuku_not_started) }
} }
} }
fun executeCommand(command: String, subCommand:String, env: Array<String>?, dir:File?): String { suspend fun executeCommand(myContext: Context, command: String, subCommand:String, env: Array<String>?, dir:File?): String {
var result = "" var result = ""
val tunnel:ByteArrayInputStream val tunnel:ByteArrayInputStream
val process:Process val process:Process
val outputStream:OutputStream val outputStream:OutputStream
try { try {
tunnel = ByteArrayInputStream(subCommand.toByteArray()) tunnel = ByteArrayInputStream(subCommand.toByteArray())
process = Runtime.getRuntime().exec(command,env,dir) process = withContext(Dispatchers.IO){Runtime.getRuntime().exec(command, env, dir)}
outputStream = process.outputStream outputStream = process.outputStream
IOUtils.copy(tunnel,outputStream) IOUtils.copy(tunnel,outputStream)
outputStream.close() withContext(Dispatchers.IO){ outputStream.close() }
val exitCode = process.waitFor() val exitCode = withContext(Dispatchers.IO){ process.waitFor() }
if(exitCode!=0){ result+="Error: $exitCode" } if(exitCode!=0){ result+="Error: $exitCode" }
}catch(e:Exception){ }catch(e:Exception){
e.printStackTrace() e.printStackTrace()
@@ -254,12 +252,13 @@ fun executeCommand(command: String, subCommand:String, env: Array<String>?, dir:
try { try {
val outputReader = BufferedReader(InputStreamReader(process.inputStream)) val outputReader = BufferedReader(InputStreamReader(process.inputStream))
var outputLine: String var outputLine: String
while(outputReader.readLine().also {outputLine = it}!=null) { result+="$outputLine\n" } while(withContext(Dispatchers.IO){ outputReader.readLine() }.also {outputLine = it}!=null) { result+="$outputLine\n" }
val errorReader = BufferedReader(InputStreamReader(process.errorStream)) val errorReader = BufferedReader(InputStreamReader(process.errorStream))
var errorLine: String var errorLine: String
while(errorReader.readLine().also {errorLine = it}!=null) { result+="$errorLine\n" } while(withContext(Dispatchers.IO){ errorReader.readLine() }.also {errorLine = it}!=null) { result+="$errorLine\n" }
} catch(e: NullPointerException) { } catch(e: NullPointerException) {
e.printStackTrace() e.printStackTrace()
} }
if(result==""){ return myContext.getString(R.string.try_again) }
return result return result
} }

View File

@@ -39,6 +39,7 @@
<string name="unsupported">不支持</string> <string name="unsupported">不支持</string>
<string name="developing">功能开发中</string> <string name="developing">功能开发中</string>
<string name="place_holder" /> <string name="place_holder" />
<string name="try_again">请再试一次</string>
<!--Permissions--> <!--Permissions-->
<string name="activate_device_admin">激活Device admin</string> <string name="activate_device_admin">激活Device admin</string>
@@ -85,9 +86,15 @@
<string name="should_contain_2000_or_0">下面应该出现一行包含“2000”或“0”的文本\n</string> <string name="should_contain_2000_or_0">下面应该出现一行包含“2000”或“0”的文本\n</string>
<string name="test_rish">测试rish</string> <string name="test_rish">测试rish</string>
<string name="please_update_shizuku">请更新Shizuku</string> <string name="please_update_shizuku">请更新Shizuku</string>
<string name="shizuku_permission_granted">Shizuku v%1$i\n已授权%2$s</string> <string name="shizuku_permission_granted">Shizuku v%1$s\n已授权%2$s</string>
<string name="request_permission">请求授权</string> <string name="request_permission">请求授权</string>
<string name="shizuku_not_started">服务未启动</string> <string name="shizuku_not_started">服务未启动</string>
<string name="activate_org_profile_command_with_user_id" tools:ignore="TypographyDashes">
dpm mark-profile-owner-on-organization-owned-device --user %1$s com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver
</string>
<string name="dpm_activate_do_command">dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver</string>
<string name="dpm_activate_po_command">dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver</string>
<string name="dpm_activate_da_command">dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver</string>
<!--System--> <!--System-->
<string name="device_ctrl">系统</string> <string name="device_ctrl">系统</string>