compose: details: Setup permissions screen
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -60,4 +60,10 @@ sealed class Screen(
|
|||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data object DetailsReview : Screen()
|
data object DetailsReview : Screen()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child screen of [AppDetails]; Avoid navigating to this screen directly.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data object DetailsPermission : Screen()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ private fun ScreenContentApp(
|
|||||||
onNavigateToDetailsScreenshot = { showExtraPane(Screen.DetailsScreenshot(it)) },
|
onNavigateToDetailsScreenshot = { showExtraPane(Screen.DetailsScreenshot(it)) },
|
||||||
onNavigateToDetailsReview = { showExtraPane(Screen.DetailsReview) },
|
onNavigateToDetailsReview = { showExtraPane(Screen.DetailsReview) },
|
||||||
onNavigateToDetailsExodus = { showExtraPane(Screen.DetailsExodus) },
|
onNavigateToDetailsExodus = { showExtraPane(Screen.DetailsExodus) },
|
||||||
|
onNavigateToDetailsPermission = { showExtraPane(Screen.DetailsPermission) },
|
||||||
onDownload = onDownload,
|
onDownload = onDownload,
|
||||||
onManualDownload = onManualDownload,
|
onManualDownload = onManualDownload,
|
||||||
onCancelDownload = onCancelDownload,
|
onCancelDownload = onCancelDownload,
|
||||||
@@ -320,7 +321,9 @@ private fun ScreenContentApp(
|
|||||||
onNavigateUp = ::showMainPane,
|
onNavigateUp = ::showMainPane,
|
||||||
onNavigateToAppDetails = onNavigateToAppDetails
|
onNavigateToAppDetails = onNavigateToAppDetails
|
||||||
)
|
)
|
||||||
|
is Screen.DetailsPermission -> DetailsPermissionScreen(
|
||||||
|
onNavigateUp = ::showMainPane
|
||||||
|
)
|
||||||
is Screen.DetailsScreenshot -> DetailsScreenshotScreen(
|
is Screen.DetailsScreenshot -> DetailsScreenshotScreen(
|
||||||
index = screen.index,
|
index = screen.index,
|
||||||
onNavigateUp = ::showMainPane
|
onNavigateUp = ::showMainPane
|
||||||
@@ -359,6 +362,7 @@ private fun ScreenContentAppMainPane(
|
|||||||
onNavigateToDetailsScreenshot: (index: Int) -> Unit,
|
onNavigateToDetailsScreenshot: (index: Int) -> Unit,
|
||||||
onNavigateToDetailsReview: () -> Unit,
|
onNavigateToDetailsReview: () -> Unit,
|
||||||
onNavigateToDetailsExodus: () -> Unit,
|
onNavigateToDetailsExodus: () -> Unit,
|
||||||
|
onNavigateToDetailsPermission: () -> Unit,
|
||||||
onDownload: () -> Unit,
|
onDownload: () -> Unit,
|
||||||
onManualDownload: () -> Unit,
|
onManualDownload: () -> Unit,
|
||||||
onCancelDownload: () -> Unit,
|
onCancelDownload: () -> Unit,
|
||||||
@@ -480,7 +484,12 @@ private fun ScreenContentAppMainPane(
|
|||||||
|
|
||||||
HeaderComposable(
|
HeaderComposable(
|
||||||
title = stringResource(R.string.details_permission),
|
title = stringResource(R.string.details_permission),
|
||||||
subtitle = stringResource(R.string.permissions, app.permissions.size)
|
subtitle = if (app.permissions.isNotEmpty()) {
|
||||||
|
stringResource(R.string.permissions, app.permissions.size)
|
||||||
|
} else {
|
||||||
|
stringResource(R.string.details_no_permission)
|
||||||
|
},
|
||||||
|
onClick = if (app.permissions.isNotEmpty()) onNavigateToDetailsPermission else null
|
||||||
)
|
)
|
||||||
|
|
||||||
if (dataSafetyReport != null) AppDataSafety(report = dataSafetyReport)
|
if (dataSafetyReport != null) AppDataSafety(report = dataSafetyReport)
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.ui.details
|
||||||
|
|
||||||
|
import android.content.pm.PermissionInfo
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
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.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.PreviewParameter
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import androidx.window.core.layout.WindowWidthSizeClass
|
||||||
|
import com.aurora.extensions.adaptiveNavigationIcon
|
||||||
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.compose.composables.TopAppBarComposable
|
||||||
|
import com.aurora.store.compose.composables.preview.AppPreviewProvider
|
||||||
|
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||||
|
import com.aurora.store.viewmodel.details.DetailsPermissionViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DetailsPermissionScreen(
|
||||||
|
onNavigateUp: () -> Unit,
|
||||||
|
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(),
|
||||||
|
detailsPermissionViewModel: DetailsPermissionViewModel = hiltViewModel { factory: DetailsPermissionViewModel.Factory ->
|
||||||
|
factory.create(appDetailsViewModel.app.value!!.permissions)
|
||||||
|
},
|
||||||
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
|
) {
|
||||||
|
val app by appDetailsViewModel.app.collectAsStateWithLifecycle()
|
||||||
|
val permissionsInfo by detailsPermissionViewModel.permissionsInfo.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
val topAppBarTitle = when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) {
|
||||||
|
WindowWidthSizeClass.COMPACT -> app!!.displayName
|
||||||
|
else -> stringResource(R.string.details_permission)
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenContent(
|
||||||
|
topAppBarTitle = topAppBarTitle,
|
||||||
|
onNavigateUp = onNavigateUp,
|
||||||
|
permissionsInfo = permissionsInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ScreenContent(
|
||||||
|
topAppBarTitle: String? = null,
|
||||||
|
permissionsInfo: Map<String, PermissionInfo?> = emptyMap(),
|
||||||
|
onNavigateUp: () -> Unit = {},
|
||||||
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
|
) {
|
||||||
|
val packageManager = LocalContext.current.packageManager
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBarComposable(
|
||||||
|
title = topAppBarTitle,
|
||||||
|
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
||||||
|
onNavigateUp = onNavigateUp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = dimensionResource(R.dimen.padding_medium))
|
||||||
|
) {
|
||||||
|
items(items = permissionsInfo.keys.toList(), key = { it }) { permission ->
|
||||||
|
// Bail out if this is not a known permission for the OS
|
||||||
|
val permissionInfo = permissionsInfo.getValue(permission) ?: return@items
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = permissionInfo.loadLabel(packageManager).toString(),
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun DetailsPermissionScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||||
|
ScreenContent(
|
||||||
|
topAppBarTitle = app.displayName
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ class DetailsExodusViewModel @AssistedInject constructor(
|
|||||||
getExodusTrackersFromReport()
|
getExodusTrackersFromReport()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getExodusTrackersFromReport() {
|
private fun getExodusTrackersFromReport() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val trackerObjects = exodusReport.trackers.map {
|
val trackerObjects = exodusReport.trackers.map {
|
||||||
exodusTrackers.getJSONObject(it.toString())
|
exodusTrackers.getJSONObject(it.toString())
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.viewmodel.details
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.pm.PermissionInfo
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
|
||||||
|
@HiltViewModel(assistedFactory = DetailsPermissionViewModel.Factory::class)
|
||||||
|
class DetailsPermissionViewModel @AssistedInject constructor(
|
||||||
|
@Assisted private val permissions: List<String>,
|
||||||
|
@ApplicationContext private val context: Context
|
||||||
|
): ViewModel() {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(permissions: List<String>): DetailsPermissionViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _permissionsInfo = MutableStateFlow<Map<String, PermissionInfo?>>(emptyMap())
|
||||||
|
val permissionsInfo = _permissionsInfo.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
fetchPermissions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchPermissions() {
|
||||||
|
_permissionsInfo.value = permissions.associateWith { getPermissionInfo(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPermissionInfo(permissionName: String): PermissionInfo? {
|
||||||
|
return try {
|
||||||
|
context.packageManager.getPermissionInfo(permissionName, 0)
|
||||||
|
} catch (_: PackageManager.NameNotFoundException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user