Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ android {
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "0.0.5"
versionName = "0.0.7"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/aidl/IShellService.aidl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
interface IShellService {
void exec(String pluginExecuteEntryPoint, String pluginPackageDirectory, IShellCallback callback);
boolean kill(int progressPid);
void command(String commandContent, IShellCallback callback);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.baidaidai.rootless_store.components.shellScreen

import com.baidaidai.rootless_store.R
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.painterResource
import kotlinx.coroutines.launch

object ShellScreenNecessaryComponents {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ShellScreenScreenTopAppBar(
onTopIconClick:suspend ()-> Unit = {},
onBottomIconClick:suspend ()-> Unit = {},
onDeleteIconClick:()-> Unit = {},
){
val coroutineScope = rememberCoroutineScope()

TopAppBar(
title = {
Text("ShellScreen")
},
actions = {
IconButton(
onClick = {
coroutineScope.launch {
onTopIconClick()
}
}
){
Icon(
painterResource(R.drawable.material_symbols_top),
contentDescription = "To Top"
)
}
IconButton(
onClick = {
coroutineScope.launch {
onBottomIconClick()
}
}
){
Icon(
painterResource(R.drawable.material_symbols_bottom),
contentDescription = "To Top"
)
}
IconButton(onClick = onDeleteIconClick){
Icon(
painterResource(R.drawable.material_symbols_delete),
contentDescription = "To Top"
)
}
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.baidaidai.rootless_store.data.shell.gateway

import android.util.Log
import com.baidaidai.rootless_store.data.shizuku.repository.ShizukuAdbRepositoryImpl
import com.baidaidai.rootless_store.data.shizuku.server.ShizukuEndpointCallback
import com.baidaidai.rootless_store.domain.execute.model.ExecuteResult
import com.baidaidai.rootless_store.domain.execute.model.ResultTag
import com.baidaidai.rootless_store.domain.shell.model.ShellResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import javax.inject.Inject

class ExecuteShellGatewayImpl @Inject constructor(
private val shizukuAdbRepositoryImpl: ShizukuAdbRepositoryImpl,
) {

fun runCommandByAppShell(commandContent: String): Flow<ShellResult> = callbackFlow {
val process = ProcessBuilder("sh", "-c", commandContent).start()

launch(Dispatchers.IO) {
process.inputStream.bufferedReader().useLines { lines ->
lines.forEach { result ->
send(
ShellResult(
resulTag = ResultTag.Normal,
command = "~ $commandContent",
content = result,
)
)
}
}
process.errorStream.bufferedReader().useLines { lines ->
lines.forEach { error ->
send(
ShellResult(
resulTag = ResultTag.RedLine,
command = "~ $commandContent",
content = error,
)
)
}
}
}

awaitClose {}

}.flowOn(Dispatchers.IO)

fun runCommandByADBShell(commandContent: String): Flow<ShellResult> = callbackFlow {
launch(Dispatchers.IO) {
val callback = ShizukuEndpointCallback(
onExecuteCallback = { session ->
trySend(
ShellResult(
resulTag = ResultTag.Normal,
command = "~ $commandContent",
content = session.toString(),
)
)
},
onErrorCallback = { error ->
trySend(
ShellResult(
resulTag = ResultTag.RedLine,
command = "~ $commandContent",
content = error.toString(),
)
)
}
)

Log.d("exam",(shizukuAdbRepositoryImpl.getShizukuEndpoint()==null).toString())

shizukuAdbRepositoryImpl.getShizukuEndpoint()
?.command(commandContent, callback)
}
awaitClose { }
}

fun runCommandByRootShell(commandContent: String): Flow<ShellResult> = callbackFlow {
val process = ProcessBuilder("su", "-c", commandContent).start()

launch(Dispatchers.IO) {
process.inputStream.bufferedReader().useLines { lines ->
lines.forEach { result ->
send(
ShellResult(
resulTag = ResultTag.Normal,
command = "# $commandContent",
content = result,
)
)
}
}
process.errorStream.bufferedReader().useLines { lines ->
lines.forEach { error ->
send(
ShellResult(
resulTag = ResultTag.RedLine,
command = "# $commandContent",
content = error,
)
)
}
}
}

awaitClose {}

}.flowOn(Dispatchers.IO)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.baidaidai.rootless_store.data.shell.repository

import com.baidaidai.rootless_store.data.shell.gateway.ExecuteShellGatewayImpl
import com.baidaidai.rootless_store.domain.shell.model.ShellCommandContainer
import com.baidaidai.rootless_store.domain.shell.model.ShellEnvironment
import com.baidaidai.rootless_store.domain.shell.model.ShellResult
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class ExecuteShellRepositoryImpl @Inject constructor(
private val executeShellGatewayImpl: ExecuteShellGatewayImpl
) {

private fun runCommandByAppShell(commandContent: String) = executeShellGatewayImpl.runCommandByAppShell(commandContent)

private fun runCommandByADBShell(commandContent: String) = executeShellGatewayImpl.runCommandByADBShell(commandContent)

private fun runCommandByRootShell(commandContent: String) = executeShellGatewayImpl.runCommandByRootShell(commandContent)

fun runCommand(shellCommandContainer: ShellCommandContainer): Flow<ShellResult>{
return when(shellCommandContainer.shellEnvironment){
ShellEnvironment.AppShell -> runCommandByAppShell(shellCommandContainer.commandContent)
ShellEnvironment.ADBShell -> runCommandByADBShell(shellCommandContainer.commandContent)
ShellEnvironment.RootShell -> runCommandByRootShell(shellCommandContainer.commandContent)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,26 @@ internal class ShizukuEndpointTemplate : IShellService.Stub() {
.waitFor()
return process == 0
}

override fun command(commandContent: String, callback: IShellCallback ){
val process = ProcessBuilder("sh","-c",commandContent).start()

process
.inputStream
.bufferedReader()
.useLines{ line ->
line.forEach {
callback.onExecute(it)
}
}

process
.errorStream
.bufferedReader()
.useLines{ line ->
line.forEach {
callback.onError(it)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,13 @@ class StoreStatusGatewayImpl @Inject constructor(
HosterOverallStatus.LIMITED
}
}

fun getRootStatus(): Boolean {
return Shell.getShell().isRoot
}

fun getShizukuStatus(): Boolean {
return shizukuEndpointManager.bind()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.baidaidai.rootless_store.domain.status.model.MemoryStatus
import com.baidaidai.rootless_store.domain.status.model.SELinuxStatus
import com.baidaidai.rootless_store.domain.status.model.StorageStatus
import com.baidaidai.rootless_store.domain.status.model.TempStatus
import com.topjohnwu.superuser.Shell
import jakarta.inject.Inject
import kotlinx.coroutines.flow.Flow

Expand All @@ -27,4 +28,8 @@ class StoreStatusRepositoryImpl @Inject constructor(
fun getAndroidAndAPIStatus(): AndroidAndAPIStatus = storeStatusGatewayImpl.getAndroidAndAPIStatus()

fun getOverallStatus(): HosterOverallStatus = storeStatusGatewayImpl.getHosterOverallStatus()

fun getRootStatus(): Boolean = storeStatusGatewayImpl.getRootStatus()

fun getShizukuStatus(): Boolean = storeStatusGatewayImpl.getShizukuStatus()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.baidaidai.rootless_store.domain.shell.model

data class ShellCommandContainer(
val shellEnvironment: ShellEnvironment,
val commandContent: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.baidaidai.rootless_store.domain.shell.model

enum class ShellEnvironment {
AppShell,ADBShell,RootShell
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.baidaidai.rootless_store.domain.shell.model

import com.baidaidai.rootless_store.domain.execute.model.ResultTag
import kotlin.String

data class ShellResult(
val resulTag: ResultTag,
val command: String?,
val content: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.baidaidai.rootless_store.domain.shell.usecase

import com.baidaidai.rootless_store.data.status.repository.StoreStatusRepositoryImpl
import javax.inject.Inject

class GetADBShellStatusUseCase @Inject constructor(
private val storeStatusRepositoryImpl: StoreStatusRepositoryImpl
) {
operator fun invoke () = storeStatusRepositoryImpl.getShizukuStatus()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.baidaidai.rootless_store.domain.shell.usecase

import com.baidaidai.rootless_store.data.status.repository.StoreStatusRepositoryImpl
import javax.inject.Inject

class GetRootShellStatusUseCase @Inject constructor(
private val storeStatusRepositoryImpl: StoreStatusRepositoryImpl
) {
operator fun invoke () = storeStatusRepositoryImpl.getRootStatus()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.baidaidai.rootless_store.domain.shell.usecase

import com.baidaidai.rootless_store.data.shell.repository.ExecuteShellRepositoryImpl
import com.baidaidai.rootless_store.domain.shell.model.ShellCommandContainer
import javax.inject.Inject

class RunCommandUseCase @Inject constructor(
private val executeShellRepositoryImpl: ExecuteShellRepositoryImpl
) {
operator fun invoke(shellCommandContainer: ShellCommandContainer) = executeShellRepositoryImpl.runCommand(shellCommandContainer)
}
Loading
Loading