Skip to content

Commit

Permalink
refactor: permission, refresh appList, divider
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Apr 29, 2024
1 parent 015af35 commit 6a9f413
Show file tree
Hide file tree
Showing 23 changed files with 822 additions and 610 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,5 @@ dependencies {
implementation(libs.exp4j)

implementation(libs.toaster)
implementation(libs.permissions)
}
48 changes: 26 additions & 22 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

<!-- 低版本Android截屏 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- com.tencent.bugly -->

<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" /> <!-- packageManager.getApplicationInfo -->

<!-- 国产ROM-获取应用列表权限 -->
<uses-permission android:name="com.android.permission.GET_INSTALLED_APPS" />

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Expand Down Expand Up @@ -87,8 +90,7 @@
android:name=".service.GkdAbService"
android:exported="false"
android:label="@string/app_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:stopWithTask="false">
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
Expand All @@ -98,37 +100,39 @@
android:resource="@xml/ab_desc" />
</service>
<service
android:name=".service.ManageService"
android:name=".debug.ScreenshotService"
android:exported="false"
android:foregroundServiceType="dataSync"
android:stopWithTask="false" />
android:foregroundServiceType="mediaProjection" />
<service
android:name=".debug.ScreenshotService"
android:name=".service.ManageService"
android:exported="false"
android:foregroundServiceType="mediaProjection"
android:stopWithTask="false" />
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Display the running state of the application" />
</service>
<service
android:name=".debug.HttpService"
android:exported="false"
android:foregroundServiceType="dataSync"
android:stopWithTask="false" />
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Enable the HTTP server to provide external browser connections and debugging" />
</service>
<service
android:name=".debug.FloatingService"
android:exported="false"
android:foregroundServiceType="dataSync"
android:stopWithTask="false" />
<service
android:name=".service.ShizukuService"
android:exported="false"
android:foregroundServiceType="dataSync"
android:stopWithTask="false" />
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Display a screenshot button for users to actively save screen information." />
</service>
<service
android:name=".debug.SnapshotTileService"
android:exported="true"
android:icon="@drawable/ic_capture"
android:label="@string/capture_label"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:stopWithTask="false">
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">

<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
Expand Down
26 changes: 12 additions & 14 deletions app/src/main/kotlin/li/songe/gkd/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.compose.rememberNavController
import com.dylanc.activityresult.launcher.PickContentLauncher
import com.dylanc.activityresult.launcher.RequestPermissionLauncher
import com.dylanc.activityresult.launcher.StartActivityLauncher
import com.ramcosta.composedestinations.DestinationsNavHost
import dagger.hilt.android.AndroidEntryPoint
Expand All @@ -18,18 +17,18 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import li.songe.gkd.composition.CompositionActivity
import li.songe.gkd.composition.CompositionExt.useLifeCycleLog
import li.songe.gkd.permission.AuthDialog
import li.songe.gkd.permission.updatePermissionState
import li.songe.gkd.service.ManageService
import li.songe.gkd.service.updateLauncherAppId
import li.songe.gkd.ui.NavGraphs
import li.songe.gkd.ui.component.ConfirmDialog
import li.songe.gkd.ui.theme.AppTheme
import li.songe.gkd.util.AuthDialog
import li.songe.gkd.util.LocalLauncher
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.LocalPickContentLauncher
import li.songe.gkd.util.LocalRequestPermissionLauncher
import li.songe.gkd.util.UpgradeDialog
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.initOrResetAppInfoCache
import li.songe.gkd.util.initFolder
import li.songe.gkd.util.map
import li.songe.gkd.util.storeFlow

Expand All @@ -39,7 +38,6 @@ class MainActivity : CompositionActivity({
useLifeCycleLog()
val launcher = StartActivityLauncher(this)
val pickContentLauncher = PickContentLauncher(this)
val requestPermissionLauncher = RequestPermissionLauncher(this)

lifecycleScope.launch {
storeFlow.map(lifecycleScope) { s -> s.excludeFromRecents }.collect {
Expand All @@ -56,12 +54,10 @@ class MainActivity : CompositionActivity({

setContent {
val navController = rememberNavController()

AppTheme {
CompositionLocalProvider(
LocalLauncher provides launcher,
LocalPickContentLauncher provides pickContentLauncher,
LocalRequestPermissionLauncher provides requestPermissionLauncher,
LocalNavController provides navController
) {
DestinationsNavHost(
Expand All @@ -85,13 +81,15 @@ class MainActivity : CompositionActivity({
override fun onResume() {
super.onResume()

// https://github.com/gkd-kit/gkd/issues/543
// ContextCompat.checkSelfPermission(app, Manifest.permission.QUERY_ALL_PACKAGES) always is GRANTED
if (appInfoCacheFlow.value.count { e -> !e.value.isSystem && !e.value.hidden } < 16) {
lifecycleScope.launch(Dispatchers.IO) {
initOrResetAppInfoCache()
}
// 每次切换页面更新记录桌面 appId
updateLauncherAppId()

// 在某些机型由于未知原因创建失败, 在此保证每次界面切换都能重新检测创建
appScope.launch(Dispatchers.IO) {
initFolder()
}

updatePermissionState()
}

override fun onStop() {
Expand Down
14 changes: 4 additions & 10 deletions app/src/main/kotlin/li/songe/gkd/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ package li.songe.gkd

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.LogUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import li.songe.gkd.data.RawSubscription
import li.songe.gkd.data.SubsItem
import li.songe.gkd.db.DbSet
import li.songe.gkd.service.updateLauncherAppId
import li.songe.gkd.util.authActionFlow
import li.songe.gkd.permission.authReasonFlow
import li.songe.gkd.util.checkUpdate
import li.songe.gkd.util.initFolder
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.logZipDir
import li.songe.gkd.util.newVersionApkDir
Expand All @@ -20,12 +19,6 @@ import li.songe.gkd.util.updateSubscription

class MainViewModel : ViewModel() {
init {
// 在某些机型由于未知原因创建失败
// 在此保证每次重新打开APP都能重新检测创建
initFolder()

// 每次打开页面更新记录桌面 appId
updateLauncherAppId()

val localSubsItem = SubsItem(
id = -2, order = -2, mtime = System.currentTimeMillis()
Expand Down Expand Up @@ -63,6 +56,7 @@ class MainViewModel : ViewModel() {
checkUpdate()
} catch (e: Exception) {
e.printStackTrace()
LogUtils.d(e)
}
}
}
Expand All @@ -71,6 +65,6 @@ class MainViewModel : ViewModel() {

override fun onCleared() {
super.onCleared()
authActionFlow.value = null
authReasonFlow.value = null
}
}
12 changes: 11 additions & 1 deletion app/src/main/kotlin/li/songe/gkd/data/SubsItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,24 @@ data class SubsItem(

) {

val isSafeRemote by lazy {
private val isSafeRemote by lazy {
if (updateUrl != null) {
isSafeUrl(updateUrl)
} else {
false
}
}

val sourceText by lazy {
if (id < 0) {
"本地来源"
} else if (isSafeRemote) {
"可信来源"
} else {
"未知来源"
}
}

suspend fun removeAssets() {
deleteSubscription(id)
DbSet.subsItemDao.delete(this)
Expand Down
97 changes: 97 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/permission/PermissionDialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package li.songe.gkd.permission

import android.app.Activity
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.XXPermissions
import kotlinx.coroutines.flow.MutableStateFlow
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

data class AuthReason(
val text: String,
val confirm: () -> Unit
)

val authReasonFlow = MutableStateFlow<AuthReason?>(null)

@Composable
fun AuthDialog() {
val authAction = authReasonFlow.collectAsState().value
if (authAction != null) {
AlertDialog(
title = {
Text(text = "权限请求")
},
text = {
Text(text = authAction.text)
},
onDismissRequest = { authReasonFlow.value = null },
confirmButton = {
TextButton(onClick = {
authReasonFlow.value = null
authAction.confirm()
}) {
Text(text = "确认")
}
},
dismissButton = {
TextButton(onClick = { authReasonFlow.value = null }) {
Text(text = "取消")
}
}
)
}
}

sealed class PermissionResult {
data object Granted : PermissionResult()
data class Denied(val doNotAskAgain: Boolean) : PermissionResult()
}

suspend fun asyncRequestPermission(
context: Activity,
permission: String,
): PermissionResult {
if (XXPermissions.isGranted(context, permission)) {
return PermissionResult.Granted
}
return suspendCoroutine { continuation ->
XXPermissions.with(context)
.unchecked()
.permission(permission)
.request(object : OnPermissionCallback {
override fun onGranted(permissions: MutableList<String>, allGranted: Boolean) {
if (allGranted) {
continuation.resume(PermissionResult.Granted)
} else {
continuation.resume(PermissionResult.Denied(false))
}
}

override fun onDenied(permissions: MutableList<String>, doNotAskAgain: Boolean) {
continuation.resume(PermissionResult.Denied(doNotAskAgain))
}
})
}
}

suspend fun checkOrRequestPermission(
context: Activity,
permissionState: PermissionState
): Boolean {
if (!permissionState.updateAndGet()) {
val result = permissionState.request?.let { it(context) } ?: return false
if (result is PermissionResult.Denied) {
if (result.doNotAskAgain) {
authReasonFlow.value = permissionState.reason
}
return false
}
}
return true
}
Loading

0 comments on commit 6a9f413

Please sign in to comment.