diff --git a/app/src/main/java/com/baidaidai/rootless_store/ShizukuActivity.kt b/app/src/main/java/com/baidaidai/rootless_store/ShizukuActivity.kt index b2d2f46..a6824d6 100644 --- a/app/src/main/java/com/baidaidai/rootless_store/ShizukuActivity.kt +++ b/app/src/main/java/com/baidaidai/rootless_store/ShizukuActivity.kt @@ -4,8 +4,11 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -21,7 +24,7 @@ import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class ShizukuActivity: ComponentActivity() { - @OptIn(ExperimentalMaterial3ExpressiveApi::class) + @OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -34,7 +37,15 @@ class ShizukuActivity: ComponentActivity() { } } RootlessStoreTheme { - Scaffold { contentPadding -> + Scaffold( + topBar = { + TopAppBar( + title = { + Text("Shizuku Auth") + } + ) + } + ) { contentPadding -> if (sharedEvent is RootlessStoreError){ StartScreenErrorDialog(shizukuAdbScreenViewModel, sharedEvent) } diff --git a/app/src/main/java/com/baidaidai/rootless_store/components/shizukuAdbScreen/ShizukuAdbScreenNecessaryComponents.kt b/app/src/main/java/com/baidaidai/rootless_store/components/shizukuAdbScreen/ShizukuAdbScreenNecessaryComponents.kt new file mode 100644 index 0000000..b04ef58 --- /dev/null +++ b/app/src/main/java/com/baidaidai/rootless_store/components/shizukuAdbScreen/ShizukuAdbScreenNecessaryComponents.kt @@ -0,0 +1,220 @@ +package com.baidaidai.rootless_store.components.shizukuAdbScreen + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.baidaidai.rootless_store.R + +object ShizukuAdbScreenNecessaryComponents { + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + fun ShizukuAdbScreenModelSheet( + remainderTime: Int, + onDismissRequest: ()-> Unit, + onCloseButtonClick: ()-> Unit, + onReturnButtonClick: ()-> Unit + ){ + ModalBottomSheet( + onDismissRequest = onDismissRequest, + sheetState = rememberModalBottomSheetState(), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(top = 8.dp, bottom = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .size(84.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.primaryContainer), + contentAlignment = Alignment.Center + ) { + Icon( + painterResource(R.drawable.material_symbols_check), + contentDescription = null, + tint = MaterialTheme.colorScheme.onPrimaryContainer, + modifier = Modifier.size(40.dp) + ) + } + + Spacer(Modifier.height(16.dp)) + + Text( + text = "All done", + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onSurface, + textAlign = TextAlign.Center + ) + + Spacer(Modifier.height(8.dp)) + + Text( + text = "Returning to Home in $remainderTime s", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center + ) + + Spacer(Modifier.height(20.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + OutlinedButton( + onClick = onCloseButtonClick, + modifier = Modifier.weight(1f) + ) { + Text("Close") + } + Button( + onClick = onReturnButtonClick, + modifier = Modifier.weight(1f) + ) { + Text("Return now") + } + } + } + } + } + + @OptIn(ExperimentalMaterial3ExpressiveApi::class) + @Composable + fun ShizukuAdbScreenActionCard( + step: String, + title: String, + description: String, + targetStatus: Boolean, + onClick: () -> Unit + ){ + Card( + modifier = Modifier + .fillMaxWidth(), + elevation = CardDefaults.cardElevation(), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer + ) + ) { + Column( + modifier = Modifier + .padding(20.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Column{ + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .height(60.dp) + ){ + Column{ + Text( + text = step, + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.primary + ) + Text( + text = title, + style = MaterialTheme.typography.titleMediumEmphasized + ) + } + Button( + onClick = onClick, + modifier = Modifier.size(48.dp), + contentPadding = PaddingValues(0.dp) + ) { + Icon( + painter = if (targetStatus){ + painterResource(R.drawable.material_symbols_check) + }else{ + painterResource(R.drawable.material_symbols_play_arrow) + }, + contentDescription = "Start", + modifier = Modifier.size(24.dp) + ) + } + } + Spacer(modifier = Modifier.height(15.dp)) + HorizontalDivider() + Spacer(modifier = Modifier.height(15.dp)) + Text( + text = description, + style = MaterialTheme.typography.bodyMediumEmphasized + ) + } + } + } + } + + @OptIn(ExperimentalMaterial3ExpressiveApi::class) + @Composable + fun ShizukuAdbScreenOverviewCard(){ + OutlinedCard( + modifier = Modifier + .fillMaxWidth(), + elevation = CardDefaults.cardElevation(), + ) { + Column( + modifier = Modifier + .padding(24.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Row( + modifier = Modifier + .height(50.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ){ + Text( + text = "Shizuku Access", + style = MaterialTheme.typography.titleLargeEmphasized + ) + Icon( + painterResource(R.drawable.material_shizuku_icon), + contentDescription = "Shizuku Icon", + tint = MaterialTheme.colorScheme.primary, + ) + } + Text( + text = "Rootless Store uses Shizuku’s ADB shell for some features so please request ADB authorization.\nFirst then connect to Shizuku.", + style = MaterialTheme.typography.bodyMedium + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/baidaidai/rootless_store/ui/screens/ShizukuAdbScreen.kt b/app/src/main/java/com/baidaidai/rootless_store/ui/screens/ShizukuAdbScreen.kt index 2913202..9818a89 100644 --- a/app/src/main/java/com/baidaidai/rootless_store/ui/screens/ShizukuAdbScreen.kt +++ b/app/src/main/java/com/baidaidai/rootless_store/ui/screens/ShizukuAdbScreen.kt @@ -4,33 +4,32 @@ import android.app.Activity import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.Button -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text +import androidx.compose.material3.LinearWavyProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import com.baidaidai.rootless_store.domain.error.RootlessStoreError +import com.baidaidai.rootless_store.components.shizukuAdbScreen.ShizukuAdbScreenNecessaryComponents.ShizukuAdbScreenActionCard +import com.baidaidai.rootless_store.components.shizukuAdbScreen.ShizukuAdbScreenNecessaryComponents.ShizukuAdbScreenModelSheet +import com.baidaidai.rootless_store.components.shizukuAdbScreen.ShizukuAdbScreenNecessaryComponents.ShizukuAdbScreenOverviewCard import com.baidaidai.rootless_store.ui.model.RootlessStoreShizukuAdbScreenViewModel -import com.baidaidai.rootless_store.ui.theme.RootlessStoreTheme +import kotlinx.coroutines.delay +@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class) @Composable fun ShizukuAdbScreen( contentPaddingValues: PaddingValues, @@ -42,124 +41,84 @@ fun ShizukuAdbScreen( val context = LocalContext.current val activity = context as? Activity + var sheetState by remember { mutableStateOf(false) } + var remainderTime by remember { mutableIntStateOf(6) } + LaunchedEffect(endpointActived) { if (endpointActived) { + sheetState = true + while (remainderTime > 0){ + delay(1000) + remainderTime-- + } activity?.finish() } } - LazyColumn( - modifier = Modifier - .padding(contentPaddingValues) - .padding(horizontal = 15.dp), - verticalArrangement = Arrangement.spacedBy(12.dp), - contentPadding = PaddingValues(vertical = 15.dp) - ) { - item { - ShizukuAdbScreenOverviewCard() - } - item { - ShizukuAdbScreenActionCard( - step = "Step 1", - title = "Request ADB Authorization", - description = "Grant Rootless Store the ADB authorization it needs before entering the shell flow. Finish this step first so the following connection step can continue normally.", - buttonText = if (shizukuActived) { - "Actived ✓" - } else { - "Request Authorization" - }, - onClick = { - shizukuAdbScreenViewModel.activeShizuku() - } - ) - } - item { - ShizukuAdbScreenActionCard( - step = "Step 2", - title = "Connect to Shizuku UserActivity", - description = "After ADB authorization is ready, open Shizuku's UserActivity and enter the ADB shell session used by Rootless Store. This is the final step before the user can continue with the shell-based workflow.", - buttonText = if (endpointActived) { - "Actived ✓" - } else { - "Connect to Shizuku" - }, - onClick = { - shizukuAdbScreenViewModel.activeShizukuEndpoint() - } - ) - } - } -} - -@Composable -private fun ShizukuAdbScreenOverviewCard(){ - Card( - modifier = Modifier - .fillMaxWidth(), - elevation = CardDefaults.cardElevation(), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer - ) - ) { - Column( - modifier = Modifier - .padding(24.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = "Rootless Store ADB", - style = MaterialTheme.typography.titleMedium - ) - Text( - text = "Some Rootless Store features depend on an ADB shell instead of a full root shell. This page helps the user complete the required setup in the correct order: request ADB authorization first, then connect into Shizuku's ADB shell environment.", - style = MaterialTheme.typography.bodyMedium - ) - } - } -} - -@Composable -private fun ShizukuAdbScreenActionCard( - step: String, - title: String, - description: String, - buttonText: String, - onClick: () -> Unit -){ - Card( - modifier = Modifier - .fillMaxWidth(), - elevation = CardDefaults.cardElevation(), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surfaceContainer + if (sheetState){ + ShizukuAdbScreenModelSheet( + remainderTime = remainderTime, + onDismissRequest = { sheetState = false}, + onCloseButtonClick = { sheetState = false }, + onReturnButtonClick = { activity?.finish() } ) - ) { - Column( + }else{ + LazyColumn( modifier = Modifier - .padding(24.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) + .padding(contentPaddingValues) + .padding(horizontal = 15.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + contentPadding = PaddingValues(vertical = 15.dp) ) { - Text( - text = step, - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.primary - ) - Text( - text = title, - style = MaterialTheme.typography.titleMedium - ) - Text( - text = description, - style = MaterialTheme.typography.bodyMedium - ) - HorizontalDivider() - Button( - onClick = onClick, - modifier = Modifier - .fillMaxWidth() - ) { - Text(buttonText) + item { + ShizukuAdbScreenOverviewCard() + } + item { + Column( + modifier = Modifier + .fillMaxWidth() + ) { + Spacer(modifier = Modifier.height(10.dp)) + LinearWavyProgressIndicator( + progress = { + if (endpointActived) { + 1f + }else if (shizukuActived){ + 0.5f + }else{ + 0.05f + } + }, + amplitude = {1f}, + waveSpeed = 10.dp, + modifier = Modifier + .fillMaxWidth() + ) + Spacer(modifier = Modifier.height(10.dp)) + } + } + item { + ShizukuAdbScreenActionCard( + step = "Step 1", + title = "Request Shizuku Auth", + description = "Grant ADB authorization so Rootless Store can start the shell workflow and unlock the next step", + targetStatus = shizukuActived, + onClick = { + shizukuAdbScreenViewModel.activeShizuku() + } + ) + } + item { + ShizukuAdbScreenActionCard( + step = "Step 2", + title = "Connect to Shizuku", + description = "After authorization is ready open Shizuku and enter the ADB shell session to finish setup and continue", + targetStatus = endpointActived, + onClick = { + shizukuAdbScreenViewModel.activeShizukuEndpoint() + } + ) } } } diff --git a/app/src/main/res/drawable/material_shizuku_icon.xml b/app/src/main/res/drawable/material_shizuku_icon.xml new file mode 100644 index 0000000..98c29ac --- /dev/null +++ b/app/src/main/res/drawable/material_shizuku_icon.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/material_symbols_check.xml b/app/src/main/res/drawable/material_symbols_check.xml new file mode 100644 index 0000000..280f0bd --- /dev/null +++ b/app/src/main/res/drawable/material_symbols_check.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/material_symbols_play_arrow.xml b/app/src/main/res/drawable/material_symbols_play_arrow.xml new file mode 100644 index 0000000..9bc6b5d --- /dev/null +++ b/app/src/main/res/drawable/material_symbols_play_arrow.xml @@ -0,0 +1,10 @@ + + +