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