mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
support become device owner with shizuku
This commit is contained in:
14
Guide.md
14
Guide.md
@@ -142,14 +142,22 @@ adb shell dpm remove-active-admin com.binbin.androidowner/com.binbin.androidowne
|
|||||||
|
|
||||||
### Shizuku
|
### Shizuku
|
||||||
|
|
||||||
可以用来
|
请自己学习如何启动[Shizuku](https://github.com/RikkaApps/Shizuku)
|
||||||
|
|
||||||
|
检查权限时如果返回“请更新Shizuku”,说明你的Shizuku版本小于v11,建议更新Shizuku。如果你的安卓版本不支持新的Shizuku,你仍然可以尝试使用下面这些功能
|
||||||
|
|
||||||
|
功能:
|
||||||
|
|
||||||
- 激活Device admin
|
- 激活Device admin
|
||||||
- 激活Profile owner
|
- 激活Profile owner
|
||||||
- 激活Device admin
|
- 激活Device admin
|
||||||
- 激活由组织拥有的工作资料
|
- 激活[由组织拥有的工作资料](#由组织拥有的工作资料)
|
||||||
|
|
||||||
局限性:不能在工作资料中使用
|
Shizuku的本质是ADB。在安卓10或以下,你还是要连接电脑激活Shizuku
|
||||||
|
|
||||||
|
不能在非主用户中使用,即使检查权限返回“已授权”
|
||||||
|
|
||||||
|
因为作者懒得研究Shizuku-API,所以直接套壳了rish。因为是套壳的rish,所以不支持Sui
|
||||||
|
|
||||||
### 设备唯一标识码
|
### 设备唯一标识码
|
||||||
|
|
||||||
|
|||||||
@@ -73,5 +73,5 @@ dependencies {
|
|||||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
|
androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
|
||||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.0")
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,5 @@
|
|||||||
#!/system/bin/sh
|
#!/system/bin/sh
|
||||||
BASEDIR=$(dirname "$0")
|
BASEDIR=$(dirname "$0")
|
||||||
DEX="$BASEDIR"/rish_shizuku.dex
|
DEX="$BASEDIR"/rish_shizuku.dex
|
||||||
|
|
||||||
if [ ! -f "$DEX" ]; then
|
|
||||||
echo "Cannot find $DEX, please check the tutorial in Shizuku app"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $(getprop ro.build.version.sdk) -ge 34 ]; then
|
|
||||||
if [ -w $DEX ]; then
|
|
||||||
echo "On Android 14+, app_process cannot load writable dex."
|
|
||||||
echo "Attempting to remove the write permission..."
|
|
||||||
echo "上面那两行是Shizuku的提示,可以忽略"
|
|
||||||
echo ""
|
|
||||||
chmod 400 $DEX
|
|
||||||
fi
|
|
||||||
if [ -w $DEX ]; then
|
|
||||||
echo "Cannot remove the write permission of $DEX."
|
|
||||||
echo "You can copy to file to terminal app's private directory (/data/data/<package>, so that remove write permission is possible"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -z "$RISH_APPLICATION_ID" ] && export RISH_APPLICATION_ID="com.binbin.androidowner"
|
[ -z "$RISH_APPLICATION_ID" ] && export RISH_APPLICATION_ID="com.binbin.androidowner"
|
||||||
/system/bin/app_process -Djava.class.path="$DEX" /system/bin --nice-name=rish rikka.shizuku.shell.ShizukuShellLoader "$@"
|
/system/bin/app_process -Djava.class.path="$DEX" /system/bin --nice-name=rish rikka.shizuku.shell.ShizukuShellLoader "$@"
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
MyScaffold()
|
MyScaffold()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteRish(applicationContext)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ fun ManagedProfile() {
|
|||||||
Text("跳转至个人应用")
|
Text("跳转至个人应用")
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(!myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)&&!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
|
if(!myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)&&!isDeviceOwner(myDpm)){
|
||||||
Button(
|
Button(
|
||||||
onClick = { myContext.startActivity(Intent("com.binbin.androidowner.MAIN_ACTION")) }, modifier = Modifier.fillMaxWidth()
|
onClick = { myContext.startActivity(Intent("com.binbin.androidowner.MAIN_ACTION")) }, modifier = Modifier.fillMaxWidth()
|
||||||
){
|
){
|
||||||
@@ -115,7 +115,7 @@ fun ManagedProfile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
||||||
Row(modifier = sections(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
|
Row(modifier = sections(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically){
|
||||||
var suspended by remember{mutableStateOf(false)}
|
var suspended by remember{mutableStateOf(false)}
|
||||||
suspended = myDpm.getPersonalAppsSuspendedReasons(myComponent)!=PERSONAL_APPS_NOT_SUSPENDED
|
suspended = myDpm.getPersonalAppsSuspendedReasons(myComponent)!=PERSONAL_APPS_NOT_SUSPENDED
|
||||||
@@ -130,7 +130,7 @@ fun ManagedProfile() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
||||||
Column(modifier = sections()){
|
Column(modifier = sections()){
|
||||||
var time by remember{mutableStateOf("")}
|
var time by remember{mutableStateOf("")}
|
||||||
time = myDpm.getManagedProfileMaximumTimeOff(myComponent).toString()
|
time = myDpm.getManagedProfileMaximumTimeOff(myComponent).toString()
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ fun DpmPermissions(navCtrl:NavHostController){
|
|||||||
modifier = sections(onClick = {navCtrl.navigate("ShizukuActivate")}, clickable = true),
|
modifier = sections(onClick = {navCtrl.navigate("ShizukuActivate")}, clickable = true),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically
|
horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically
|
||||||
){
|
){
|
||||||
Text(text = "Shizuku", style = typography.titleLarge, color = titleColor)
|
Text(text = "Shizuku", style = typography.titleLarge, color = titleColor, modifier = Modifier.padding(vertical = 2.dp))
|
||||||
Icon(imageVector = Icons.Default.KeyboardArrowRight,contentDescription = null, tint = colorScheme.onPrimaryContainer)
|
Icon(imageVector = Icons.Default.KeyboardArrowRight,contentDescription = null, tint = colorScheme.onPrimaryContainer)
|
||||||
}
|
}
|
||||||
if(!myDpm.isAdminActive(myComponent)&&isWear){
|
if(!myDpm.isAdminActive(myComponent)&&isWear){
|
||||||
@@ -74,6 +74,7 @@ fun DpmPermissions(navCtrl:NavHostController){
|
|||||||
}
|
}
|
||||||
if(!isWear)
|
if(!isWear)
|
||||||
if(isda){
|
if(isda){
|
||||||
|
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
myDpm.removeActiveAdmin(myComponent)
|
myDpm.removeActiveAdmin(myComponent)
|
||||||
@@ -82,6 +83,7 @@ fun DpmPermissions(navCtrl:NavHostController){
|
|||||||
) {
|
) {
|
||||||
Text("撤销")
|
Text("撤销")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
Button(onClick = { activateDeviceAdmin(myContext,myComponent) }) {
|
Button(onClick = { activateDeviceAdmin(myContext,myComponent) }) {
|
||||||
Text("激活")
|
Text("激活")
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ import android.content.ComponentName
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Context.MODE_PRIVATE
|
import android.content.Context.MODE_PRIVATE
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Binder
|
||||||
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.core.Spring
|
||||||
|
import androidx.compose.animation.core.SpringSpec
|
||||||
import androidx.compose.foundation.focusable
|
import androidx.compose.foundation.focusable
|
||||||
import androidx.compose.foundation.horizontalScroll
|
import androidx.compose.foundation.horizontalScroll
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@@ -32,12 +35,10 @@ import androidx.compose.ui.platform.LocalFocusManager
|
|||||||
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.delay
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import rikka.shizuku.Shizuku
|
import rikka.shizuku.Shizuku
|
||||||
import java.io.BufferedReader
|
import java.io.*
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ShizukuActivate(){
|
fun ShizukuActivate(){
|
||||||
@@ -49,49 +50,79 @@ 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
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())){
|
var launchExtractRish by remember{mutableStateOf(false)}
|
||||||
|
LaunchedEffect(launchExtractRish){ if(launchExtractRish){ extractRish(myContext);launchExtractRish=false } }
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
Column(modifier = Modifier.verticalScroll(scrollState)){
|
||||||
var outputText by remember{mutableStateOf("")}
|
var outputText by remember{mutableStateOf("")}
|
||||||
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
|
if(Binder.getCallingUid()/100000!=0){
|
||||||
Row(modifier = sections(colorScheme.errorContainer), verticalAlignment = Alignment.CenterVertically){
|
Row(modifier = sections(colorScheme.errorContainer), verticalAlignment = Alignment.CenterVertically){
|
||||||
Icon(imageVector = Icons.Rounded.Warning, contentDescription = null, tint = colorScheme.onErrorContainer)
|
Icon(imageVector = Icons.Rounded.Warning, contentDescription = null, tint = colorScheme.onErrorContainer)
|
||||||
Text(text = "暂不支持在工作资料中使用Shizuku", style = bodyTextStyle, color = colorScheme.onErrorContainer)
|
Text(text = "暂不支持在非主用户中使用Shizuku", style = bodyTextStyle, color = colorScheme.onErrorContainer)
|
||||||
}
|
|
||||||
}
|
|
||||||
Button(onClick = {outputText=checkPermission()}, enabled = VERSION.SDK_INT>=24, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
|
|
||||||
Text(text = "检查权限")
|
|
||||||
}
|
|
||||||
Column(modifier = sections()){
|
|
||||||
Text(text = "激活", style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
extractRish(myContext)
|
|
||||||
outputText = executeCommand("sh rish.sh", "dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Text(text = "Device admin")
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
extractRish(myContext)
|
|
||||||
outputText = executeCommand("sh rish.sh", "dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Text(text = "Profile owner")
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
extractRish(myContext)
|
|
||||||
outputText = executeCommand("sh rish.sh", "dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Text(text = "Device owner")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(VERSION.SDK_INT>=30&&!myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)){
|
var launchPermissionCheck by remember{mutableStateOf(false)}
|
||||||
|
LaunchedEffect(launchPermissionCheck){
|
||||||
|
if(launchPermissionCheck){
|
||||||
|
outputText = checkPermission()
|
||||||
|
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||||
|
launchPermissionCheck=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {launchPermissionCheck=true},
|
||||||
|
enabled = VERSION.SDK_INT>=24, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
|
||||||
|
) {
|
||||||
|
Text(text = "检查权限")
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isDeviceOwner(myDpm)&&!isProfileOwner(myDpm)){
|
||||||
|
Column(modifier = sections()){
|
||||||
|
Text(text = "激活", style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
|
||||||
|
|
||||||
|
if(!myDpm.isAdminActive(myComponent)){
|
||||||
|
var launchActivateDA by remember{mutableStateOf(false)}
|
||||||
|
LaunchedEffect(launchActivateDA){
|
||||||
|
if(launchActivateDA){
|
||||||
|
outputText = executeCommand("sh rish.sh", "dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
|
||||||
|
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||||
|
launchActivateDA=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(onClick = {launchActivateDA=true}, modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Text(text = "Device admin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var launchActivatePO by remember{mutableStateOf(false)}
|
||||||
|
LaunchedEffect(launchActivatePO){
|
||||||
|
if(launchActivatePO){
|
||||||
|
outputText = executeCommand("sh rish.sh", "dpm set-profile-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
|
||||||
|
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||||
|
launchActivatePO=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(onClick = {launchActivatePO=true}, modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Text(text = "Profile owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
var launchActivateDO by remember{mutableStateOf(false)}
|
||||||
|
LaunchedEffect(launchActivateDO){
|
||||||
|
if(launchActivateDO){
|
||||||
|
outputText = executeCommand("sh rish.sh", "dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver", null, filesDir)
|
||||||
|
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||||
|
launchActivateDO=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(onClick = {launchActivateDO=true}, modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Text(text = "Device owner")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(VERSION.SDK_INT>=30&&!isDeviceOwner(myDpm)&&!myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)&&!myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
||||||
Column(modifier = sections()){
|
Column(modifier = sections()){
|
||||||
Text(text = "组织拥有工作资料", style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
|
Text(text = "组织拥有工作资料", style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
|
||||||
Text(text = "请输入工作资料的UserID", style = bodyTextStyle)
|
Text(text = "请输入工作资料的UserID", style = bodyTextStyle)
|
||||||
@@ -103,14 +134,22 @@ 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)
|
||||||
)
|
)
|
||||||
Button(
|
var launchActivateOrgProfile by remember{mutableStateOf(false)}
|
||||||
onClick = {
|
LaunchedEffect(launchActivateOrgProfile){
|
||||||
extractRish(myContext)
|
if(launchActivateOrgProfile){
|
||||||
|
focusMgr.clearFocus()
|
||||||
outputText = executeCommand(
|
outputText = executeCommand(
|
||||||
"sh rish.sh",
|
"sh rish.sh",
|
||||||
"dpm mark-profile-owner-on-organization-owned-device --user $inputUserID com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
|
"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())
|
||||||
|
launchActivateOrgProfile=false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
launchActivateOrgProfile=true
|
||||||
if(myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
if(myDpm.isOrganizationOwnedDeviceWithManagedProfile){
|
||||||
Toast.makeText(myContext,"成功",Toast.LENGTH_SHORT).show()
|
Toast.makeText(myContext,"成功",Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
@@ -121,82 +160,106 @@ 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 = {
|
onClick = {launchListOwners=true}, modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
|
||||||
extractRish(myContext)
|
|
||||||
outputText = executeCommand("sh rish.sh", "pwd", null, filesDir)
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
) {
|
||||||
Text(text = "test")
|
Text(text = "列出Owners")
|
||||||
}
|
}
|
||||||
SelectionContainer(modifier = Modifier.padding(3.dp)){
|
|
||||||
Text(text = outputText, style = bodyTextStyle, softWrap = false, modifier = Modifier.horizontalScroll(rememberScrollState()))
|
var launchTest by remember{mutableStateOf(false)}
|
||||||
|
LaunchedEffect(launchTest){
|
||||||
|
if(launchTest){
|
||||||
|
outputText="下面应该出现一行包含“2000”或“0”的文本\n"
|
||||||
|
scrollState.animateScrollTo(scrollState.maxValue, scrollAnim())
|
||||||
|
outputText+=executeCommand("sh rish.sh","id",null,filesDir)
|
||||||
|
launchTest=false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {launchTest=true}, modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
|
) {
|
||||||
|
Text(text = "测试rish")
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState())){
|
||||||
|
Text(text = outputText, style = bodyTextStyle, softWrap = false, modifier = Modifier.padding(4.dp))
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(Modifier.padding(vertical = 30.dp))
|
Spacer(Modifier.padding(vertical = 30.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
fun <T> scrollAnim(
|
||||||
|
dampingRatio: Float = Spring.DampingRatioNoBouncy,
|
||||||
|
stiffness: Float = Spring.StiffnessMedium,
|
||||||
|
visibilityThreshold: T? = null
|
||||||
|
): SpringSpec<T> = SpringSpec(dampingRatio, stiffness, visibilityThreshold)
|
||||||
|
|
||||||
fun extractRish(myContext:Context){
|
fun extractRish(myContext:Context){
|
||||||
val assetsMgr = myContext.assets
|
val assetsMgr = myContext.assets
|
||||||
val fileList = myContext.fileList()
|
myContext.deleteFile("rish.sh")
|
||||||
if("rish.sh" !in fileList){
|
myContext.deleteFile("rish_shizuku.dex")
|
||||||
val shInput = assetsMgr.open("rish.sh")
|
val shInput = assetsMgr.open("rish.sh")
|
||||||
val shOutput = myContext.openFileOutput("rish.sh",MODE_PRIVATE)
|
val shOutput = myContext.openFileOutput("rish.sh",MODE_PRIVATE)
|
||||||
IOUtils.copy(shInput,shOutput)
|
IOUtils.copy(shInput,shOutput)
|
||||||
shOutput.close()
|
shOutput.close()
|
||||||
}
|
|
||||||
if("rish_shizuku.dex" !in fileList){
|
|
||||||
val dexInput = assetsMgr.open("rish_shizuku.dex")
|
val dexInput = assetsMgr.open("rish_shizuku.dex")
|
||||||
val dexOutput = myContext.openFileOutput("rish_shizuku.dex",MODE_PRIVATE)
|
val dexOutput = myContext.openFileOutput("rish_shizuku.dex",MODE_PRIVATE)
|
||||||
IOUtils.copy(dexInput,dexOutput)
|
IOUtils.copy(dexInput,dexOutput)
|
||||||
dexOutput.close()
|
dexOutput.close()
|
||||||
}
|
if(VERSION.SDK_INT>=34){ Runtime.getRuntime().exec("chmod 400 rish_shizuku.dex",null,myContext.filesDir) }
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteRish(myContext: Context){
|
|
||||||
myContext.deleteFile("rish.sh")
|
|
||||||
myContext.deleteFile("rish_shizuku.dex")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkPermission():String {
|
private fun checkPermission():String {
|
||||||
if(Shizuku.isPreV11()) {
|
return if(Shizuku.isPreV11()) {
|
||||||
return "有可能不支持v11以下的Shizuku\n你仍然可以尝试使用这些功能"
|
"请更新Shizuku"
|
||||||
}else{
|
}else{
|
||||||
try{
|
try{
|
||||||
if(Shizuku.checkSelfPermission()==PackageManager.PERMISSION_GRANTED) {
|
if(Shizuku.checkSelfPermission()==PackageManager.PERMISSION_GRANTED) {
|
||||||
val permission = when(Shizuku.getUid()){
|
val permission = when(Shizuku.getUid()){ 0->"Root"; 2000->"Shell"; else->"未知权限" }
|
||||||
0->"Root"
|
"Shizuku v${Shizuku.getVersion()}\n已授权($permission)"
|
||||||
2000->"Shell"
|
}else if(Shizuku.shouldShowRequestPermissionRationale()){ "用户拒绝" }
|
||||||
else->"未知权限"
|
else{ Shizuku.requestPermission(0); "请求授权" }
|
||||||
|
}catch(e: Throwable){ "服务未启动" }
|
||||||
}
|
}
|
||||||
return "Shizuku v${Shizuku.getVersion()}\n已授权($permission)"
|
|
||||||
} else if(Shizuku.shouldShowRequestPermissionRationale()) {
|
|
||||||
return "用户拒绝"
|
|
||||||
} else {
|
|
||||||
Shizuku.requestPermission(0)
|
|
||||||
}
|
|
||||||
} catch(e: Throwable) {
|
|
||||||
return "服务未启动"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "未知"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun executeCommand(command: String, subCommand:String, env: Array<String>?, dir:File): String {
|
fun executeCommand(command: String, subCommand:String, env: Array<String>?, dir:File?): String {
|
||||||
val output = StringBuilder()
|
var result = ""
|
||||||
|
val tunnel:ByteArrayInputStream
|
||||||
|
val process:Process
|
||||||
|
val outputStream:OutputStream
|
||||||
try {
|
try {
|
||||||
val tunnel = ByteArrayInputStream(subCommand.toByteArray())
|
tunnel = ByteArrayInputStream(subCommand.toByteArray())
|
||||||
val process = Runtime.getRuntime().exec(command,env,dir)
|
process = Runtime.getRuntime().exec(command,env,dir)
|
||||||
val outputStream = process.outputStream
|
outputStream = process.outputStream
|
||||||
IOUtils.copy(tunnel,outputStream)
|
IOUtils.copy(tunnel,outputStream)
|
||||||
outputStream.close()
|
outputStream.close()
|
||||||
process.waitFor()
|
val exitCode = process.waitFor()
|
||||||
val reader = BufferedReader(InputStreamReader(process.inputStream))
|
if(exitCode!=0){ result+="出错了!退出码:$exitCode" }
|
||||||
var line: String
|
|
||||||
while(reader.readLine().also {line = it}!=null) { output.append(line+"\n") }
|
|
||||||
}catch(e:Exception){
|
}catch(e:Exception){
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
return e.toString()
|
||||||
}
|
}
|
||||||
return output.toString()
|
try {
|
||||||
|
val outputReader = BufferedReader(InputStreamReader(process.inputStream))
|
||||||
|
var outputLine: String
|
||||||
|
while(outputReader.readLine().also {outputLine = it}!=null) { result+="$outputLine\n" }
|
||||||
|
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
|
||||||
|
var errorLine: String
|
||||||
|
while(errorReader.readLine().also {errorLine = it}!=null) { result+="$errorLine\n" }
|
||||||
|
} catch(e: NullPointerException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user