compose: onboarding: Initial permissions screen work
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -29,14 +29,12 @@ import com.aurora.store.data.model.PermissionType
|
||||
* Composable to display permission details in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param permission [Permission] to display
|
||||
* @param isGranted If the permission has been granted
|
||||
* @param onAction Callback when the user clicks the action button
|
||||
*/
|
||||
@Composable
|
||||
fun PermissionComposable(
|
||||
modifier: Modifier = Modifier,
|
||||
permission: Permission,
|
||||
isGranted: Boolean = false,
|
||||
onAction: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
@@ -58,9 +56,9 @@ fun PermissionComposable(
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
TextButton(onClick = onAction, enabled = !isGranted) {
|
||||
TextButton(onClick = onAction, enabled = !permission.isGranted) {
|
||||
Text(
|
||||
text = if (isGranted) {
|
||||
text = if (permission.isGranted) {
|
||||
stringResource(R.string.action_granted)
|
||||
} else {
|
||||
stringResource(R.string.action_grant)
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
package com.aurora.store.compose.composables
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -27,24 +25,21 @@ import com.aurora.store.R
|
||||
* @see ActionHeaderComposable
|
||||
*/
|
||||
@Composable
|
||||
fun TextDividerComposable(modifier: Modifier = Modifier, @StringRes title: Int) {
|
||||
Row(
|
||||
fun TextDividerComposable(modifier: Modifier = Modifier, title: String) {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_small))
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(title).uppercase(),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
text = title.uppercase(),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun TextDividerComposablePreview() {
|
||||
TextDividerComposable(title = R.string.item_optional)
|
||||
TextDividerComposable(title = stringResource(R.string.item_optional))
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator
|
||||
import com.aurora.store.compose.ui.commons.BlacklistScreen
|
||||
import com.aurora.store.compose.ui.details.AppDetailsScreen
|
||||
import com.aurora.store.compose.ui.dev.DevProfileScreen
|
||||
import com.aurora.store.compose.ui.onboarding.PermissionsScreen
|
||||
import com.aurora.store.compose.ui.search.SearchScreen
|
||||
|
||||
/**
|
||||
@@ -69,6 +70,12 @@ fun NavDisplay(startDestination: NavKey) {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Permissions> { screen ->
|
||||
PermissionsScreen(
|
||||
onNavigateUp = { onNavigateUp() },
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -32,4 +32,7 @@ sealed class Screen : NavKey, Parcelable {
|
||||
|
||||
@Serializable
|
||||
data object Search : Screen()
|
||||
|
||||
@Serializable
|
||||
data object Permissions : Screen()
|
||||
}
|
||||
|
||||
@@ -77,10 +77,12 @@ import com.aurora.store.compose.ui.details.components.Tags
|
||||
import com.aurora.store.compose.ui.details.components.Testing
|
||||
import com.aurora.store.compose.ui.details.navigation.ExtraScreen
|
||||
import com.aurora.store.compose.ui.dev.DevProfileScreen
|
||||
import com.aurora.store.compose.ui.onboarding.PermissionsScreen
|
||||
import com.aurora.store.data.installer.AppInstaller
|
||||
import com.aurora.store.data.model.AppState
|
||||
import com.aurora.store.data.model.Report
|
||||
import com.aurora.store.data.model.Scores
|
||||
import com.aurora.store.data.providers.PermissionProvider.Companion.isPermittedToInstall
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.ShortcutManagerUtil
|
||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||
@@ -237,6 +239,16 @@ private fun ScreenContentApp(
|
||||
}
|
||||
}
|
||||
|
||||
fun onInstall() {
|
||||
if (isPermittedToInstall(context, app)) {
|
||||
onDownload()
|
||||
onNavigateBack()
|
||||
} else {
|
||||
context.toast(R.string.permissions_denied)
|
||||
showExtraPane(Screen.Permissions)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SetupMenu() {
|
||||
AppDetailsMenu(isInstalled = app.isInstalled, isFavorite = isFavorite) { menuItem ->
|
||||
@@ -271,7 +283,7 @@ private fun ScreenContentApp(
|
||||
Actions(
|
||||
primaryActionDisplayName = stringResource(R.string.action_update),
|
||||
secondaryActionDisplayName = stringResource(R.string.action_uninstall),
|
||||
onPrimaryAction = onDownload,
|
||||
onPrimaryAction = ::onInstall,
|
||||
onSecondaryAction = onUninstall
|
||||
)
|
||||
}
|
||||
@@ -295,7 +307,7 @@ private fun ScreenContentApp(
|
||||
Actions(
|
||||
primaryActionDisplayName = primaryActionName,
|
||||
secondaryActionDisplayName = stringResource(R.string.title_manual_download),
|
||||
onPrimaryAction = onDownload,
|
||||
onPrimaryAction = ::onInstall,
|
||||
onSecondaryAction = { showExtraPane(ExtraScreen.ManualDownload) }
|
||||
)
|
||||
}
|
||||
@@ -476,6 +488,11 @@ private fun ScreenContentApp(
|
||||
onNavigateToAppDetails = { onNavigateToAppDetails(it) }
|
||||
)
|
||||
|
||||
is Screen.Permissions -> PermissionsScreen(
|
||||
onNavigateUp = ::onNavigateBack,
|
||||
onPermissionCallback = { onInstall() }
|
||||
)
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.onboarding
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import android.provider.Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.aurora.extensions.adaptiveNavigationIcon
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.BuildConfig
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composables.PermissionComposable
|
||||
import com.aurora.store.compose.composables.TextDividerComposable
|
||||
import com.aurora.store.compose.composables.TopAppBarComposable
|
||||
import com.aurora.store.data.model.Permission
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import com.aurora.store.data.providers.PermissionProvider.Companion.isGranted
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.viewmodel.onboarding.PermissionsViewModel
|
||||
import kotlin.random.Random
|
||||
|
||||
private const val TAG = "PermissionsScreen"
|
||||
|
||||
@Composable
|
||||
fun PermissionsScreen(
|
||||
isOnboarding: Boolean = false,
|
||||
onNavigateUp: () -> Unit,
|
||||
onPermissionCallback: (type: PermissionType) -> Unit = {},
|
||||
viewModel: PermissionsViewModel = hiltViewModel()
|
||||
) {
|
||||
val permissions by viewModel.permissions.collectAsStateWithLifecycle()
|
||||
|
||||
ScreenContent(
|
||||
isOnboarding = isOnboarding,
|
||||
permissions = permissions,
|
||||
onNavigateUp = onNavigateUp,
|
||||
onPermissionCallback = { type ->
|
||||
viewModel.refreshPermissionsList()
|
||||
onPermissionCallback(type)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(
|
||||
isOnboarding: Boolean = false,
|
||||
permissions: List<Permission> = emptyList(),
|
||||
onNavigateUp: () -> Unit = {},
|
||||
onPermissionCallback: (type: PermissionType) -> Unit = {},
|
||||
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var permissionRequested by rememberSaveable { mutableStateOf<PermissionType?>(null) }
|
||||
|
||||
@SuppressLint("InlinedApi", "BatteryLife")
|
||||
val intentMap = mapOf(
|
||||
PermissionType.STORAGE_MANAGER to PackageUtil.getStorageManagerIntent(context),
|
||||
PermissionType.INSTALL_UNKNOWN_APPS to PackageUtil.getInstallUnknownAppsIntent(),
|
||||
PermissionType.DOZE_WHITELIST to Intent(
|
||||
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||
"package:${BuildConfig.APPLICATION_ID}".toUri()
|
||||
),
|
||||
PermissionType.APP_LINKS to Intent(
|
||||
ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
|
||||
"package:${BuildConfig.APPLICATION_ID}".toUri()
|
||||
)
|
||||
)
|
||||
|
||||
val intentLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult(),
|
||||
onResult = { permissionRequested?.let { onPermissionCallback(it) } }
|
||||
)
|
||||
val permissionLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.RequestPermission(),
|
||||
onResult = {
|
||||
when (permissionRequested) {
|
||||
PermissionType.STORAGE_MANAGER -> {
|
||||
intentLauncher.launch(intentMap[PermissionType.STORAGE_MANAGER]!!)
|
||||
}
|
||||
|
||||
else -> permissionRequested?.let { onPermissionCallback(it) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fun requestPermission(type: PermissionType) {
|
||||
try {
|
||||
permissionRequested = type
|
||||
when (type) {
|
||||
PermissionType.EXTERNAL_STORAGE -> {
|
||||
permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
|
||||
PermissionType.POST_NOTIFICATIONS -> {
|
||||
if (isTAndAbove) {
|
||||
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
|
||||
PermissionType.STORAGE_MANAGER -> {
|
||||
if (!isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS)) {
|
||||
context.toast(R.string.toast_permission_installer_required)
|
||||
} else {
|
||||
permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val intent = intentMap[type] ?: return
|
||||
intentLauncher.launch(intent)
|
||||
}
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Error requesting permission", exception)
|
||||
permissionRequested = null
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBarComposable(
|
||||
title = stringResource(R.string.onboarding_title_permissions),
|
||||
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
||||
onNavigateUp = if (isOnboarding) null else onNavigateUp
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(paddingValues),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
) {
|
||||
permissions.sortedBy { it.optional }
|
||||
.groupBy { permission -> permission.optional }
|
||||
.forEach { (key, value) ->
|
||||
stickyHeader {
|
||||
TextDividerComposable(
|
||||
title = if (!key) {
|
||||
stringResource(R.string.item_required)
|
||||
} else {
|
||||
stringResource(R.string.item_optional)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
items(items = value, key = { p -> p.type.name }) { permission ->
|
||||
PermissionComposable(
|
||||
permission = permission,
|
||||
onAction = { requestPermission(permission.type) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PermissionsScreenPreview() {
|
||||
val permissions = PermissionType.entries.map { type ->
|
||||
Permission(
|
||||
type = type,
|
||||
title = LoremIpsum(3).values.first(),
|
||||
subtitle = LoremIpsum(7).values.first(),
|
||||
optional = Random.nextBoolean(),
|
||||
isGranted = Random.nextBoolean()
|
||||
)
|
||||
}
|
||||
ScreenContent(permissions = permissions)
|
||||
}
|
||||
@@ -24,6 +24,7 @@ data class Permission(
|
||||
val title: String,
|
||||
val subtitle: String,
|
||||
val optional: Boolean = false,
|
||||
val isGranted: Boolean = false
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when (other) {
|
||||
|
||||
@@ -19,7 +19,9 @@ import com.aurora.extensions.isDomainVerified
|
||||
import com.aurora.extensions.isExternalStorageAccessible
|
||||
import com.aurora.extensions.isIgnoringBatteryOptimizations
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.extensions.requiresObbDir
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.BuildConfig
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
@@ -28,6 +30,52 @@ import com.aurora.store.util.PackageUtil
|
||||
class PermissionProvider(private val fragment: Fragment) :
|
||||
ActivityResultCallback<ActivityResult> {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Checks if Aurora Store has permissions to install the given app
|
||||
*/
|
||||
fun isPermittedToInstall(context: Context, app: App): Boolean {
|
||||
if (!isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS)) return false
|
||||
return when {
|
||||
app.fileList.requiresObbDir() -> {
|
||||
return isGranted(context, PermissionType.STORAGE_MANAGER)
|
||||
}
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a known permission has been granted
|
||||
*/
|
||||
fun isGranted(context: Context, permissionType: PermissionType): Boolean {
|
||||
return when (permissionType) {
|
||||
PermissionType.EXTERNAL_STORAGE,
|
||||
PermissionType.STORAGE_MANAGER -> {
|
||||
context.isExternalStorageAccessible()
|
||||
}
|
||||
|
||||
PermissionType.INSTALL_UNKNOWN_APPS -> {
|
||||
PackageUtil.canRequestPackageInstalls(context)
|
||||
}
|
||||
|
||||
PermissionType.POST_NOTIFICATIONS -> {
|
||||
if (isTAndAbove) {
|
||||
context.checkManifestPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
PermissionType.DOZE_WHITELIST -> context.isIgnoringBatteryOptimizations()
|
||||
|
||||
PermissionType.APP_LINKS -> context.isDomainVerified("play.google.com") &&
|
||||
context.isDomainVerified("market.android.com")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val TAG = PermissionProvider::class.java.simpleName
|
||||
|
||||
private val context: Context
|
||||
@@ -47,7 +95,7 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
}
|
||||
|
||||
override fun onActivityResult(result: ActivityResult) {
|
||||
permissionRequested?.let { permissionCallback(isGranted(it)) }
|
||||
permissionRequested?.let { permissionCallback(isGranted(context, it)) }
|
||||
}
|
||||
|
||||
fun request(permissionType: PermissionType, callback: (Boolean) -> Unit = {}) {
|
||||
@@ -67,7 +115,7 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
}
|
||||
|
||||
PermissionType.STORAGE_MANAGER -> {
|
||||
if (!isGranted(PermissionType.INSTALL_UNKNOWN_APPS)) {
|
||||
if (!isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS)) {
|
||||
context.toast(R.string.toast_permission_installer_required)
|
||||
} else {
|
||||
/**
|
||||
@@ -100,30 +148,6 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
}
|
||||
}
|
||||
|
||||
fun isGranted(permissionType: PermissionType): Boolean {
|
||||
return when (permissionType) {
|
||||
PermissionType.EXTERNAL_STORAGE,
|
||||
PermissionType.STORAGE_MANAGER -> {
|
||||
context.isExternalStorageAccessible()
|
||||
}
|
||||
|
||||
PermissionType.INSTALL_UNKNOWN_APPS -> PackageUtil.canRequestPackageInstalls(context)
|
||||
|
||||
PermissionType.POST_NOTIFICATIONS -> {
|
||||
if (isTAndAbove) {
|
||||
context.checkManifestPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
PermissionType.DOZE_WHITELIST -> context.isIgnoringBatteryOptimizations()
|
||||
|
||||
PermissionType.APP_LINKS -> context.isDomainVerified("play.google.com") &&
|
||||
context.isDomainVerified("market.android.com")
|
||||
}
|
||||
}
|
||||
|
||||
fun unregister() {
|
||||
intentLauncher.unregister()
|
||||
permissionLauncher.unregister()
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.Permission
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import com.aurora.store.data.providers.PermissionProvider.Companion.isGranted
|
||||
import com.aurora.store.databinding.FragmentOnboardingPermissionsBinding
|
||||
import com.aurora.store.view.epoxy.views.TextDividerViewModel_
|
||||
import com.aurora.store.view.epoxy.views.preference.PermissionViewModel_
|
||||
@@ -160,7 +161,7 @@ class PermissionsFragment : BaseFragment<FragmentOnboardingPermissionsBinding>()
|
||||
return PermissionViewModel_()
|
||||
.id(permission.type.name)
|
||||
.permission(permission)
|
||||
.isGranted(permissionProvider.isGranted(permission.type))
|
||||
.isGranted(isGranted(requireContext(), permission.type))
|
||||
.click { _ ->
|
||||
permissionProvider.request(permission.type) {
|
||||
if (it) updateController()
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.preference.SwitchPreferenceCompat
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import com.aurora.store.data.model.UpdateMode
|
||||
import com.aurora.store.data.providers.PermissionProvider.Companion.isGranted
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY
|
||||
@@ -63,7 +64,7 @@ class UpdatesPreference : BasePreferenceFragment() {
|
||||
}
|
||||
|
||||
UpdateMode.CHECK_AND_NOTIFY -> {
|
||||
if (permissionProvider.isGranted(PermissionType.POST_NOTIFICATIONS)) {
|
||||
if (isGranted(requireContext(), PermissionType.POST_NOTIFICATIONS)) {
|
||||
handleAutoUpdateDependentPrefs(true)
|
||||
viewModel.updateHelper.scheduleAutomatedCheck()
|
||||
true
|
||||
@@ -81,7 +82,7 @@ class UpdatesPreference : BasePreferenceFragment() {
|
||||
}
|
||||
|
||||
UpdateMode.CHECK_AND_INSTALL -> {
|
||||
if (permissionProvider.isGranted(PermissionType.DOZE_WHITELIST)) {
|
||||
if (isGranted(requireContext(), PermissionType.DOZE_WHITELIST)) {
|
||||
handleAutoUpdateDependentPrefs(true)
|
||||
viewModel.updateHelper.scheduleAutomatedCheck()
|
||||
true
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.aurora.store.R
|
||||
import com.aurora.store.compose.navigation.Screen
|
||||
import com.aurora.store.data.model.MinimalApp
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import com.aurora.store.data.providers.PermissionProvider.Companion.isGranted
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.data.room.update.Update
|
||||
import com.aurora.store.databinding.FragmentUpdatesBinding
|
||||
@@ -192,7 +193,7 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
||||
|
||||
private fun updateSingle(update: Update) {
|
||||
if (update.fileList.requiresObbDir()) {
|
||||
if (permissionProvider.isGranted(PermissionType.STORAGE_MANAGER)) {
|
||||
if (isGranted(requireContext(), PermissionType.STORAGE_MANAGER)) {
|
||||
viewModel.download(update)
|
||||
} else {
|
||||
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
|
||||
@@ -207,7 +208,7 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
||||
private fun updateAll() {
|
||||
viewModel.updateAllEnqueued = true
|
||||
if (viewModel.updates.value?.any { it.fileList.requiresObbDir() } == true) {
|
||||
if (permissionProvider.isGranted(PermissionType.STORAGE_MANAGER)) {
|
||||
if (isGranted(requireContext(), PermissionType.STORAGE_MANAGER)) {
|
||||
viewModel.downloadAll()
|
||||
} else {
|
||||
permissionProvider.request(PermissionType.STORAGE_MANAGER) {
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.viewmodel.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.aurora.extensions.isOAndAbove
|
||||
import com.aurora.extensions.isRAndAbove
|
||||
import com.aurora.extensions.isSAndAbove
|
||||
import com.aurora.extensions.isTAndAbove
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.Permission
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
import com.aurora.store.data.providers.PermissionProvider.Companion.isGranted
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class PermissionsViewModel @Inject constructor(
|
||||
@ApplicationContext private val context: Context
|
||||
) : ViewModel() {
|
||||
|
||||
private val _permissions = MutableStateFlow(getPermissions(context))
|
||||
val permissions = _permissions.asStateFlow()
|
||||
|
||||
private fun getPermissions(context: Context): List<Permission> {
|
||||
val list = mutableListOf(
|
||||
Permission(
|
||||
type = PermissionType.INSTALL_UNKNOWN_APPS,
|
||||
title = context.getString(R.string.onboarding_permission_installer),
|
||||
subtitle = if (isOAndAbove) {
|
||||
context.getString(R.string.onboarding_permission_installer_desc)
|
||||
} else {
|
||||
context.getString(R.string.onboarding_permission_installer_legacy_desc)
|
||||
},
|
||||
optional = false,
|
||||
isGranted = isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS)
|
||||
),
|
||||
Permission(
|
||||
type = PermissionType.DOZE_WHITELIST,
|
||||
title = context.getString(R.string.onboarding_permission_doze),
|
||||
subtitle = context.getString(R.string.onboarding_permission_doze_desc),
|
||||
optional = true,
|
||||
isGranted = isGranted(context, PermissionType.DOZE_WHITELIST)
|
||||
)
|
||||
)
|
||||
|
||||
if (isRAndAbove) {
|
||||
list.add(
|
||||
Permission(
|
||||
type = PermissionType.STORAGE_MANAGER,
|
||||
title = context.getString(R.string.onboarding_permission_esm),
|
||||
subtitle = context.getString(R.string.onboarding_permission_esa_desc),
|
||||
optional = false,
|
||||
isGranted = isGranted(context, PermissionType.STORAGE_MANAGER)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
list.add(
|
||||
Permission(
|
||||
type = PermissionType.EXTERNAL_STORAGE,
|
||||
title = context.getString(R.string.onboarding_permission_esa),
|
||||
subtitle = context.getString(R.string.onboarding_permission_esa_desc),
|
||||
optional = false,
|
||||
isGranted = isGranted(context, PermissionType.EXTERNAL_STORAGE)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (isTAndAbove) {
|
||||
list.add(
|
||||
Permission(
|
||||
type = PermissionType.POST_NOTIFICATIONS,
|
||||
title = context.getString(R.string.onboarding_permission_notifications),
|
||||
subtitle = context.getString(R.string.onboarding_permission_notifications_desc),
|
||||
optional = true,
|
||||
isGranted = isGranted(context, PermissionType.POST_NOTIFICATIONS)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (isSAndAbove) {
|
||||
list.add(
|
||||
Permission(
|
||||
type = PermissionType.APP_LINKS,
|
||||
title = context.getString(R.string.app_links_title),
|
||||
subtitle = context.getString(R.string.app_links_desc),
|
||||
optional = true,
|
||||
isGranted = isGranted(context, PermissionType.APP_LINKS)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
fun refreshPermissionsList() {
|
||||
_permissions.value = _permissions.value.map { permission ->
|
||||
permission.copy(isGranted = isGranted(context, permission.type))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user