feat(android): add device readiness checks
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.roubao.autopilot
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
@@ -30,6 +31,9 @@ import com.roubao.autopilot.agent.MobileAgent
|
||||
import com.roubao.autopilot.controller.AppScanner
|
||||
import com.roubao.autopilot.controller.DeviceController
|
||||
import com.roubao.autopilot.data.*
|
||||
import com.roubao.autopilot.readiness.DeviceReadinessChecker
|
||||
import com.roubao.autopilot.readiness.DeviceReadinessSnapshot
|
||||
import com.roubao.autopilot.readiness.PINDUODUO_PACKAGE
|
||||
import com.roubao.autopilot.ui.screens.*
|
||||
import com.roubao.autopilot.ui.theme.*
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
@@ -45,8 +49,8 @@ import android.util.Log
|
||||
private const val TAG = "MainActivity"
|
||||
|
||||
sealed class Screen(val route: String, val title: String, val icon: ImageVector, val selectedIcon: ImageVector) {
|
||||
object Device : Screen("device", "设备", Icons.Outlined.Build, Icons.Filled.Build)
|
||||
object Home : Screen("home", "肉包", Icons.Outlined.Home, Icons.Filled.Home)
|
||||
object Capabilities : Screen("capabilities", "能力", Icons.Outlined.Star, Icons.Filled.Star)
|
||||
object History : Screen("history", "记录", Icons.Outlined.List, Icons.Filled.List)
|
||||
object Settings : Screen("settings", "设置", Icons.Outlined.Settings, Icons.Filled.Settings)
|
||||
}
|
||||
@@ -56,9 +60,11 @@ class MainActivity : ComponentActivity() {
|
||||
private lateinit var deviceController: DeviceController
|
||||
private lateinit var settingsManager: SettingsManager
|
||||
private lateinit var executionRepository: ExecutionRepository
|
||||
private lateinit var readinessChecker: DeviceReadinessChecker
|
||||
|
||||
private val mobileAgent = mutableStateOf<MobileAgent?>(null)
|
||||
private var shizukuAvailable = mutableStateOf(false)
|
||||
private val readinessSnapshot = mutableStateOf(DeviceReadinessSnapshot.empty())
|
||||
|
||||
// 当前执行的协程 Job(用于停止任务)
|
||||
private var currentExecutionJob: kotlinx.coroutines.Job? = null
|
||||
@@ -113,6 +119,8 @@ class MainActivity : ComponentActivity() {
|
||||
deviceController.setCacheDir(cacheDir)
|
||||
settingsManager = SettingsManager(this)
|
||||
executionRepository = ExecutionRepository(this)
|
||||
readinessChecker = DeviceReadinessChecker(this)
|
||||
refreshReadiness()
|
||||
|
||||
// 加载执行记录
|
||||
lifecycleScope.launch {
|
||||
@@ -164,10 +172,9 @@ class MainActivity : ComponentActivity() {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainApp() {
|
||||
var currentScreen by remember { mutableStateOf<Screen>(Screen.Home) }
|
||||
var currentScreen by remember { mutableStateOf<Screen>(Screen.Device) }
|
||||
var selectedRecord by remember { mutableStateOf<ExecutionRecord?>(null) }
|
||||
var showShizukuHelpDialog by remember { mutableStateOf(false) }
|
||||
var hasShownShizukuHelp by remember { mutableStateOf(false) }
|
||||
|
||||
val settings by settingsManager.settings.collectAsState()
|
||||
val colors = BaoziTheme.colors
|
||||
@@ -179,6 +186,7 @@ class MainActivity : ComponentActivity() {
|
||||
val executing by remember { isExecuting }
|
||||
val navigateToRecord by remember { shouldNavigateToRecord }
|
||||
val recordId by remember { currentRecordId }
|
||||
val readiness by remember { readinessSnapshot }
|
||||
|
||||
// 监听跳转事件
|
||||
LaunchedEffect(navigateToRecord, recordId) {
|
||||
@@ -193,14 +201,6 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
// 首次进入且 Shizuku 未连接时,显示帮助引导(只显示一次)
|
||||
LaunchedEffect(Unit) {
|
||||
if (!isShizukuAvailable && settings.hasSeenOnboarding && !hasShownShizukuHelp) {
|
||||
hasShownShizukuHelp = true
|
||||
showShizukuHelpDialog = true
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.background(colors.background),
|
||||
containerColor = colors.background,
|
||||
@@ -211,7 +211,7 @@ class MainActivity : ComponentActivity() {
|
||||
contentColor = colors.textPrimary,
|
||||
tonalElevation = 0.dp
|
||||
) {
|
||||
listOf(Screen.Home, Screen.Capabilities, Screen.History, Screen.Settings).forEach { screen ->
|
||||
listOf(Screen.Device, Screen.Home, Screen.History, Screen.Settings).forEach { screen ->
|
||||
val selected = currentScreen == screen
|
||||
NavigationBarItem(
|
||||
icon = {
|
||||
@@ -262,6 +262,14 @@ class MainActivity : ComponentActivity() {
|
||||
label = "screen"
|
||||
) { screen ->
|
||||
when (screen) {
|
||||
Screen.Device -> DeviceReadinessScreen(
|
||||
snapshot = readiness,
|
||||
onRefresh = { refreshReadiness() },
|
||||
onOpenAccessibilitySettings = {
|
||||
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
|
||||
},
|
||||
onOpenPinduoduo = { openPinduoduo() }
|
||||
)
|
||||
Screen.Home -> {
|
||||
// 每次进入首页都检测 Shizuku 状态
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -291,7 +299,6 @@ class MainActivity : ComponentActivity() {
|
||||
isExecuting = executing
|
||||
)
|
||||
}
|
||||
Screen.Capabilities -> CapabilitiesScreen()
|
||||
Screen.History -> HistoryScreen(
|
||||
records = records,
|
||||
onRecordClick = { record -> selectedRecord = record },
|
||||
@@ -346,6 +353,13 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (::readinessChecker.isInitialized) {
|
||||
refreshReadiness()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Shizuku.removeBinderReceivedListener(binderReceivedListener)
|
||||
@@ -354,6 +368,20 @@ class MainActivity : ComponentActivity() {
|
||||
deviceController.unbindService()
|
||||
}
|
||||
|
||||
private fun refreshReadiness() {
|
||||
readinessSnapshot.value = readinessChecker.snapshot()
|
||||
}
|
||||
|
||||
private fun openPinduoduo() {
|
||||
val launchIntent = packageManager.getLaunchIntentForPackage(PINDUODUO_PACKAGE)
|
||||
if (launchIntent == null) {
|
||||
Toast.makeText(this, "未安装拼多多", Toast.LENGTH_SHORT).show()
|
||||
refreshReadiness()
|
||||
return
|
||||
}
|
||||
startActivity(launchIntent)
|
||||
}
|
||||
|
||||
private fun checkShizukuPermission(): Boolean {
|
||||
return try {
|
||||
val granted = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED
|
||||
@@ -365,7 +393,7 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAndUpdateShizukuStatus() {
|
||||
private fun checkAndUpdateShizukuStatus(requestPermission: Boolean = false) {
|
||||
Log.d(TAG, "checkAndUpdateShizukuStatus called")
|
||||
try {
|
||||
val binderAlive = Shizuku.pingBinder()
|
||||
@@ -379,7 +407,7 @@ class MainActivity : ComponentActivity() {
|
||||
if (hasPermission) {
|
||||
Log.d(TAG, "Binding Shizuku service")
|
||||
deviceController.bindService()
|
||||
} else {
|
||||
} else if (requestPermission) {
|
||||
Log.d(TAG, "Requesting Shizuku permission")
|
||||
requestShizukuPermission()
|
||||
}
|
||||
@@ -396,7 +424,7 @@ class MainActivity : ComponentActivity() {
|
||||
private fun refreshShizukuStatus() {
|
||||
Log.d(TAG, "refreshShizukuStatus called by user")
|
||||
Toast.makeText(this, "正在检查 Shizuku 状态...", Toast.LENGTH_SHORT).show()
|
||||
checkAndUpdateShizukuStatus()
|
||||
checkAndUpdateShizukuStatus(requestPermission = true)
|
||||
|
||||
if (shizukuAvailable.value && checkShizukuPermission()) {
|
||||
Toast.makeText(this, "Shizuku 已连接", Toast.LENGTH_SHORT).show()
|
||||
|
||||
Reference in New Issue
Block a user