fix: 自动申请权限 + 图标消失 + 授权后自动切换
权限流程: - 启动时自动弹出系统授权对话框(不再需要用户手动点击) - 通过 SideEffect 桥接 Compose state,授权后自动切换到主界面 - 授权等待中显示加载指示器 图标修复: - 合并两个 intent-filter 为一个(LAUNCHER + LEANBACK_LAUNCHER) - 移除 singleTask launchMode 避免启动器图标异常 MainActivity 简化: - 移除 recreate() 循环触发 - 移除多余的回调闭包
This commit is contained in:
@@ -47,16 +47,12 @@
|
||||
android:name=".ui.MainActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:banner="@drawable/tv_banner"
|
||||
android:launchMode="singleTask">
|
||||
android:banner="@drawable/tv_banner">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import android.view.SurfaceView
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
@@ -30,50 +29,41 @@ import com.qwenpaw.miracast.ui.theme.*
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
}
|
||||
|
||||
private var service: AirDisplayService? = null
|
||||
private var serviceBound = false
|
||||
private var surfaceView: SurfaceView? = null
|
||||
|
||||
// Compose state 引用,供 Activity 层写入
|
||||
private var onPermissionGranted: (() -> Unit)? = null
|
||||
|
||||
private val serviceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
||||
service = (binder as AirDisplayService.LocalBinder).getService()
|
||||
serviceBound = true
|
||||
surfaceView?.let { service?.setSurface(it) }
|
||||
Log.i(TAG, "服务已绑定")
|
||||
}
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
serviceBound = false
|
||||
service = null
|
||||
serviceBound = false; service = null
|
||||
}
|
||||
}
|
||||
|
||||
private var surfaceView: SurfaceView? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val hasPerm = checkPermissions()
|
||||
val alreadyGranted = checkPermissions()
|
||||
|
||||
setContent {
|
||||
var permGranted by remember { mutableStateOf(hasPerm) }
|
||||
var permGranted by remember { mutableStateOf(alreadyGranted) }
|
||||
var serviceStarted by remember { mutableStateOf(false) }
|
||||
|
||||
val darkColors = darkColorScheme(
|
||||
primary = Primary,
|
||||
onPrimary = OnPrimary,
|
||||
secondary = Secondary,
|
||||
background = Background,
|
||||
surface = Surface,
|
||||
surfaceVariant = SurfaceVariant,
|
||||
onSurface = OnSurface,
|
||||
onSurfaceVariant = OnSurfaceVariant,
|
||||
error = Error
|
||||
)
|
||||
// 暴露 setter 给 Activity 层
|
||||
SideEffect { onPermissionGranted = { permGranted = true } }
|
||||
|
||||
MaterialTheme(colorScheme = darkColors) {
|
||||
MaterialTheme(colorScheme = darkColorScheme(
|
||||
primary = Primary, onPrimary = OnPrimary, secondary = Secondary,
|
||||
background = Background, surface = Surface, surfaceVariant = SurfaceVariant,
|
||||
onSurface = OnSurface, onSurfaceVariant = OnSurfaceVariant, error = Error
|
||||
)) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
AndroidView(
|
||||
factory = { ctx -> SurfaceView(ctx).also { surfaceView = it } },
|
||||
@@ -83,36 +73,29 @@ class MainActivity : ComponentActivity() {
|
||||
if (permGranted) {
|
||||
AppContent(
|
||||
serviceStarted = serviceStarted,
|
||||
onServiceStarted = {
|
||||
bindToService()
|
||||
startService()
|
||||
serviceStarted = true
|
||||
}
|
||||
onStart = { bindAndStart(); serviceStarted = true }
|
||||
)
|
||||
} else {
|
||||
PermissionScreen(
|
||||
onRequestPermissions = {
|
||||
requestPermission { permGranted = true }
|
||||
}
|
||||
)
|
||||
PermissionScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 首次启动自动弹出系统授权框
|
||||
if (!alreadyGranted) {
|
||||
permLauncher.launch(getRequiredPermission())
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppContent(serviceStarted: Boolean, onServiceStarted: () -> Unit) {
|
||||
LaunchedEffect(Unit) {
|
||||
if (!serviceStarted) {
|
||||
onServiceStarted()
|
||||
}
|
||||
}
|
||||
private fun AppContent(serviceStarted: Boolean, onStart: () -> Unit) {
|
||||
LaunchedEffect(Unit) { if (!serviceStarted) onStart() }
|
||||
|
||||
var currentScreen by remember { mutableStateOf("main") }
|
||||
val defaultState = AirDisplayManager.UiState()
|
||||
val state by service?.manager?.uiState?.collectAsStateWithLifecycle(defaultState)
|
||||
?: remember { mutableStateOf(defaultState) }
|
||||
val def = AirDisplayManager.UiState()
|
||||
val state by service?.manager?.uiState?.collectAsStateWithLifecycle(def)
|
||||
?: remember { mutableStateOf(def) }
|
||||
|
||||
when (currentScreen) {
|
||||
"settings" -> {
|
||||
@@ -125,11 +108,8 @@ class MainActivity : ComponentActivity() {
|
||||
else -> MainScreen(
|
||||
uiState = state,
|
||||
onStartStop = {
|
||||
if (state.serviceState is AirDisplayManager.ServiceState.Idle) {
|
||||
startService()
|
||||
} else {
|
||||
stopService()
|
||||
}
|
||||
if (state.serviceState is AirDisplayManager.ServiceState.Idle) startSvc()
|
||||
else stopSvc()
|
||||
},
|
||||
onNavigateToSettings = { currentScreen = "settings" }
|
||||
)
|
||||
@@ -138,96 +118,65 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
// ── 权限 ──
|
||||
|
||||
private fun checkPermissions(): Boolean {
|
||||
val perm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
private fun getRequiredPermission() =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
|
||||
Manifest.permission.NEARBY_WIFI_DEVICES
|
||||
} else {
|
||||
else
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
}
|
||||
return ContextCompat.checkSelfPermission(this, perm) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
private fun requestPermission(onGranted: () -> Unit) {
|
||||
val perm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
Manifest.permission.NEARBY_WIFI_DEVICES
|
||||
} else {
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
}
|
||||
permLauncher.launch(perm)
|
||||
// 存储回调,等下在 launcher 结果里调用
|
||||
pendingOnGranted = onGranted
|
||||
}
|
||||
|
||||
private var pendingOnGranted: (() -> Unit)? = null
|
||||
private fun checkPermissions() =
|
||||
ContextCompat.checkSelfPermission(this, getRequiredPermission()) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
private val permLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { granted ->
|
||||
if (granted || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
pendingOnGranted?.invoke()
|
||||
onPermissionGranted?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
// ── 服务 ──
|
||||
|
||||
private fun bindToService() {
|
||||
val intent = Intent(this, AirDisplayService::class.java)
|
||||
bindService(intent, serviceConnection, BIND_AUTO_CREATE)
|
||||
private fun bindAndStart() {
|
||||
bindService(Intent(this, AirDisplayService::class.java), serviceConnection, BIND_AUTO_CREATE)
|
||||
startSvc()
|
||||
}
|
||||
|
||||
private fun startService() {
|
||||
val intent = Intent(this, AirDisplayService::class.java).apply {
|
||||
action = AirDisplayService.ACTION_START
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(intent)
|
||||
} else {
|
||||
startService(intent)
|
||||
}
|
||||
private fun startSvc() {
|
||||
val i = Intent(this, AirDisplayService::class.java).apply { action = AirDisplayService.ACTION_START }
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startForegroundService(i) else startService(i)
|
||||
}
|
||||
|
||||
private fun stopService() {
|
||||
val intent = Intent(this, AirDisplayService::class.java).apply {
|
||||
action = AirDisplayService.ACTION_STOP
|
||||
}
|
||||
startService(intent)
|
||||
private fun stopSvc() {
|
||||
startService(Intent(this, AirDisplayService::class.java).apply { action = AirDisplayService.ACTION_STOP })
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (serviceBound) {
|
||||
unbindService(serviceConnection)
|
||||
serviceBound = false
|
||||
}
|
||||
if (serviceBound) { unbindService(serviceConnection); serviceBound = false }
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
// ── 权限界面 ──
|
||||
// ── 权限提示 ──
|
||||
|
||||
@Composable
|
||||
private fun PermissionScreen(onRequestPermissions: () -> Unit) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
private fun PermissionScreen() {
|
||||
Box(modifier = Modifier.fillMaxSize().padding(32.dp), contentAlignment = Alignment.Center) {
|
||||
Card(colors = CardDefaults.cardColors(containerColor = Surface)) {
|
||||
Column(
|
||||
modifier = Modifier.padding(32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
"需要 Wi-Fi Direct 权限",
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = OnSurface
|
||||
)
|
||||
Text("需要授权", fontSize = 20.sp, fontWeight = FontWeight.Bold, color = OnSurface)
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Text(
|
||||
"AirDisplay 需要邻近设备/位置权限才能进行 Wi-Fi Direct 无线投屏。",
|
||||
color = OnSurfaceVariant,
|
||||
fontSize = 14.sp
|
||||
"系统已弹出授权对话框,请选择「允许」以启用 Wi-Fi Direct 投屏功能。",
|
||||
color = OnSurfaceVariant, fontSize = 14.sp
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Button(onClick = onRequestPermissions) {
|
||||
Text("授予权限")
|
||||
}
|
||||
Spacer(Modifier.height(16.dp))
|
||||
CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text("等待授权…", color = OnSurfaceVariant, fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user