compose: migrate remaining screens and fix category browse

CategoryBrowseScreen and ExpandedStreamBrowseScreen are added; the
details / dev / dispenser / downloads / favourite / installed /
search / spoof / blacklist / accounts / about screens, StreamBrowse,
the onboarding MicroG page, the AppDetailsScreen plus the affected
viewmodels (AuthViewModel, ExpandedStreamBrowse, CategoryStream,
TopChart) are reworked to feed the new Compose entry points.

Also fixes CategoryBrowse pagination and the cluster-header
navigation regressions surfaced during migration.
This commit is contained in:
Rahul Patel
2026-05-19 12:13:20 +05:30
parent f2474cfb91
commit 0270dc6f39
29 changed files with 478 additions and 372 deletions

View File

@@ -46,7 +46,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.data.model.Link import com.aurora.store.data.model.Link
@Composable @Composable
fun AboutScreen(onNavigateUp: () -> Unit) { fun AboutScreen() {
var shouldShowAboutDialog by rememberSaveable { mutableStateOf(false) } var shouldShowAboutDialog by rememberSaveable { mutableStateOf(false) }
if (shouldShowAboutDialog) { if (shouldShowAboutDialog) {
@@ -54,13 +54,12 @@ fun AboutScreen(onNavigateUp: () -> Unit) {
} }
ScreenContent( ScreenContent(
onNavigateUp = onNavigateUp,
onAboutAurora = { shouldShowAboutDialog = true } onAboutAurora = { shouldShowAboutDialog = true }
) )
} }
@Composable @Composable
private fun ScreenContent(onNavigateUp: () -> Unit = {}, onAboutAurora: () -> Unit = {}) { private fun ScreenContent(onAboutAurora: () -> Unit = {}) {
val context = LocalContext.current val context = LocalContext.current
val linkURLS = stringArrayResource(R.array.link_urls) val linkURLS = stringArrayResource(R.array.link_urls)
@@ -94,8 +93,7 @@ private fun ScreenContent(onNavigateUp: () -> Unit = {}, onAboutAurora: () -> Un
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_about), title = stringResource(R.string.title_about)
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->

View File

@@ -48,19 +48,18 @@ import com.aurora.Constants.URL_TOS
import com.aurora.extensions.browse import com.aurora.extensions.browse
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.data.providers.AccountProvider import com.aurora.store.data.providers.AccountProvider
import com.aurora.store.viewmodel.accounts.AccountsViewModel import com.aurora.store.viewmodel.accounts.AccountsViewModel
@Composable @Composable
fun AccountsScreen( fun AccountsScreen(
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
onNavigateToSplash: () -> Unit,
viewModel: AccountsViewModel = hiltViewModel() viewModel: AccountsViewModel = hiltViewModel()
) { ) {
val userProfile = viewModel.authProvider.authData?.userProfile val userProfile = viewModel.authProvider.authData?.userProfile
val avatar = when { val avatar = when {
viewModel.authProvider.isAnonymous -> R.mipmap.ic_launcher
userProfile != null -> userProfile.artwork.url userProfile != null -> userProfile.artwork.url
else -> R.mipmap.ic_launcher else -> R.mipmap.ic_launcher
} }
@@ -70,7 +69,6 @@ fun AccountsScreen(
else -> stringResource(R.string.account_anonymous) else -> stringResource(R.string.account_anonymous)
} }
val email = when { val email = when {
viewModel.authProvider.isAnonymous -> stringResource(R.string.account_anonymous_email)
userProfile != null -> userProfile.email userProfile != null -> userProfile.email
else -> stringResource(R.string.account_anonymous_email) else -> stringResource(R.string.account_anonymous_email)
} }
@@ -83,14 +81,13 @@ fun AccountsScreen(
onConfirm = { onConfirm = {
shouldShowLogoutDialog = false shouldShowLogoutDialog = false
AccountProvider.logout(context) AccountProvider.logout(context)
onNavigateToSplash() onNavigateTo(Destination.Splash)
}, },
onDismiss = { shouldShowLogoutDialog = false } onDismiss = { shouldShowLogoutDialog = false }
) )
} }
ScreenContent( ScreenContent(
onNavigateUp = onNavigateUp,
avatar = avatar, avatar = avatar,
name = name, name = name,
email = email, email = email,
@@ -100,17 +97,15 @@ fun AccountsScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
avatar: Any = R.mipmap.ic_launcher, avatar: Any = R.drawable.ic_account,
name: String = stringResource(R.string.account_anonymous), name: String = stringResource(R.string.account_anonymous),
email: String = stringResource(R.string.account_anonymous_email), email: String = stringResource(R.string.account_anonymous_email),
onNavigateUp: () -> Unit = {},
onLogout: () -> Unit = {} onLogout: () -> Unit = {}
) { ) {
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_account_manager), title = stringResource(R.string.title_account_manager)
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -118,7 +113,7 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)), .padding(vertical = dimensionResource(R.dimen.padding_small)),
verticalArrangement = Arrangement.SpaceBetween verticalArrangement = Arrangement.SpaceBetween
) { ) {
AssistHeader() AssistHeader()
@@ -126,7 +121,7 @@ private fun ScreenContent(
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
) { ) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)

View File

@@ -7,6 +7,8 @@
package com.aurora.store.compose.ui.blacklist package com.aurora.store.compose.ui.blacklist
import android.net.Uri import android.net.Uri
import androidx.activity.ComponentActivity
import androidx.activity.compose.LocalActivity
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -51,7 +53,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.Constants import com.aurora.Constants
@@ -72,13 +73,12 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun BlacklistScreen(onNavigateUp: () -> Unit, viewModel: BlacklistViewModel = hiltViewModel()) { fun BlacklistScreen(viewModel: BlacklistViewModel = hiltViewModel()) {
val context = LocalContext.current val context = LocalContext.current
val packages by viewModel.filteredPackages.collectAsStateWithLifecycle() val packages by viewModel.filteredPackages.collectAsStateWithLifecycle()
ScreenContent( ScreenContent(
packages = packages, packages = packages,
onNavigateUp = onNavigateUp,
isPackageBlacklisted = { pkgName -> pkgName in viewModel.blacklist }, isPackageBlacklisted = { pkgName -> pkgName in viewModel.blacklist },
onBlacklistImport = { uri -> onBlacklistImport = { uri ->
viewModel.importBlacklist(uri) viewModel.importBlacklist(uri)
@@ -99,7 +99,6 @@ fun BlacklistScreen(onNavigateUp: () -> Unit, viewModel: BlacklistViewModel = hi
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
packages: List<BlacklistAppItem>? = null, packages: List<BlacklistAppItem>? = null,
onNavigateUp: () -> Unit = {},
isPackageBlacklisted: (packageName: String) -> Boolean = { false }, isPackageBlacklisted: (packageName: String) -> Boolean = { false },
onBlacklistImport: (uri: Uri) -> Unit = {}, onBlacklistImport: (uri: Uri) -> Unit = {},
onBlacklistExport: (uri: Uri) -> Unit = {}, onBlacklistExport: (uri: Uri) -> Unit = {},
@@ -109,6 +108,7 @@ private fun ScreenContent(
onWhitelistAll: () -> Unit = {}, onWhitelistAll: () -> Unit = {},
onSearch: (query: String) -> Unit = {} onSearch: (query: String) -> Unit = {}
) { ) {
val activity = LocalActivity.current as? ComponentActivity
val context = LocalContext.current val context = LocalContext.current
val textFieldState = rememberTextFieldState() val textFieldState = rememberTextFieldState()
val searchBarState = rememberSearchBarState() val searchBarState = rememberSearchBarState()
@@ -224,7 +224,7 @@ private fun ScreenContent(
state = searchBarState, state = searchBarState,
inputField = inputField, inputField = inputField,
navigationIcon = { navigationIcon = {
IconButton(onClick = onNavigateUp) { IconButton(onClick = { activity?.onBackPressedDispatcher?.onBackPressed() }) {
Icon( Icon(
painter = painterResource(R.drawable.ic_arrow_back), painter = painterResource(R.drawable.ic_arrow_back),
contentDescription = stringResource(R.string.action_back) contentDescription = stringResource(R.string.action_back)
@@ -333,7 +333,6 @@ private fun ScreenContent(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }

View File

@@ -0,0 +1,65 @@
/*
* SPDX-FileCopyrightText: 2026 Aurora OSS
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.compose.ui.commons
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.store.R
import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.StreamCarousel
import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composition.collectForced
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.data.model.ViewState
import com.aurora.store.viewmodel.subcategory.CategoryStreamViewModel
@Composable
fun CategoryBrowseScreen(
title: String,
browseUrl: String,
onNavigateTo: (Destination) -> Unit,
viewModel: CategoryStreamViewModel = hiltViewModel(
creationCallback = { factory: CategoryStreamViewModel.Factory ->
factory.create(browseUrl)
}
)
) {
val uiState by viewModel.viewState.collectForced(ViewState.Loading)
Scaffold(
topBar = { TopAppBar(title = title) }
) { paddingValues ->
if (uiState is ViewState.Error) {
Error(
modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error)
)
} else {
val bundle = (uiState as? ViewState.Success<*>)?.data as? StreamBundle
StreamCarousel(
modifier = Modifier.padding(paddingValues),
streamBundle = bundle,
filterSingleAppClusters = false,
onHeaderClick = { cluster ->
if (cluster.clusterBrowseUrl.isNotBlank()) {
onNavigateTo(Destination.StreamBrowse(cluster))
}
},
onAppClick = { onNavigateTo(Destination.AppDetails(it.packageName)) },
onClusterScrolled = { viewModel.fetchNextCluster(it) },
onScrolledToEnd = { viewModel.fetchNextPage() }
)
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* SPDX-FileCopyrightText: 2026 Aurora OSS
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.compose.ui.commons
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState
import androidx.paging.compose.collectAsLazyPagingItems
import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.viewmodel.browse.ExpandedStreamBrowseViewModel
import kotlin.uuid.Uuid
@Composable
fun ExpandedStreamBrowseScreen(
browseUrl: String,
defaultTitle: String,
onNavigateTo: (Destination) -> Unit,
viewModel: ExpandedStreamBrowseViewModel = hiltViewModel(
creationCallback = { factory: ExpandedStreamBrowseViewModel.Factory ->
factory.create(browseUrl)
}
)
) {
val title by viewModel.title.collectAsStateWithLifecycle()
val apps = viewModel.apps.collectAsLazyPagingItems()
Scaffold(
topBar = {
TopAppBar(
title = title.ifBlank { defaultTitle }
)
}
) { paddingValues ->
when (apps.loadState.refresh) {
is LoadState.Loading -> ContainedLoadingIndicator()
is LoadState.Error -> {
Error(
modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error)
)
}
else -> {
if (apps.itemCount == 0) {
Error(
modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.no_apps_available)
)
} else {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium)
)
) {
items(
count = apps.itemCount,
key = { Uuid.random().toString() }
) { index ->
apps[index]?.let { app ->
LargeAppListItem(
app = app,
onClick = {
onNavigateTo(Destination.AppDetails(app.packageName))
}
)
}
}
}
}
}
}
}
}

View File

@@ -35,14 +35,12 @@ import kotlin.random.Random
@Composable @Composable
fun PermissionRationaleScreen( fun PermissionRationaleScreen(
requiredPermissions: Set<PermissionType> = emptySet(), requiredPermissions: Set<PermissionType> = emptySet(),
onNavigateUp: () -> Unit,
onPermissionCallback: (type: PermissionType) -> Unit = {}, onPermissionCallback: (type: PermissionType) -> Unit = {},
viewModel: PermissionRationaleViewModel = hiltViewModel() viewModel: PermissionRationaleViewModel = hiltViewModel()
) { ) {
val permissions by viewModel.permissions.collectAsStateWithLifecycle() val permissions by viewModel.permissions.collectAsStateWithLifecycle()
ScreenContent( ScreenContent(
onNavigateUp = onNavigateUp,
permissions = permissions permissions = permissions
.filter { it.type in requiredPermissions } .filter { it.type in requiredPermissions }
.map { permission -> permission.copy(optional = false) }, .map { permission -> permission.copy(optional = false) },
@@ -56,7 +54,6 @@ fun PermissionRationaleScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
permissions: List<Permission> = emptyList(), permissions: List<Permission> = emptyList(),
onNavigateUp: () -> Unit = {},
onPermissionCallback: (type: PermissionType) -> Unit = {}, onPermissionCallback: (type: PermissionType) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
@@ -64,8 +61,7 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = pluralStringResource(R.plurals.permissions_required, permissions.size), title = pluralStringResource(R.plurals.permissions_required, permissions.size),
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->

View File

@@ -5,12 +5,14 @@
package com.aurora.store.compose.ui.commons package com.aurora.store.compose.ui.commons
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@@ -29,6 +31,7 @@ import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.viewmodel.browse.StreamBrowseViewModel import com.aurora.store.viewmodel.browse.StreamBrowseViewModel
@@ -39,8 +42,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
@Composable @Composable
fun StreamBrowseScreen( fun StreamBrowseScreen(
streamCluster: StreamCluster, streamCluster: StreamCluster,
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit,
viewModel: StreamBrowseViewModel = hiltViewModel( viewModel: StreamBrowseViewModel = hiltViewModel(
creationCallback = { factory: StreamBrowseViewModel.Factory -> creationCallback = { factory: StreamBrowseViewModel.Factory ->
factory.create(streamCluster) factory.create(streamCluster)
@@ -52,8 +54,7 @@ fun StreamBrowseScreen(
ScreenContent( ScreenContent(
title = streamCluster.clusterTitle, title = streamCluster.clusterTitle,
apps = apps, apps = apps,
onNavigateUp = onNavigateUp, onNavigateTo = onNavigateTo
onNavigateToAppDetails = onNavigateToAppDetails
) )
} }
@@ -61,12 +62,11 @@ fun StreamBrowseScreen(
private fun ScreenContent( private fun ScreenContent(
title: String = String(), title: String = String(),
apps: LazyPagingItems<App> = emptyPagingItems(), apps: LazyPagingItems<App> = emptyPagingItems(),
onNavigateToAppDetails: (packageName: String) -> Unit = {}, onNavigateTo: (Destination) -> Unit = {}
onNavigateUp: () -> Unit = {}
) { ) {
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar(title = title, onNavigateUp = onNavigateUp) TopAppBar(title = title)
} }
) { paddingValues -> ) { paddingValues ->
when (apps.loadState.refresh) { when (apps.loadState.refresh) {
@@ -91,7 +91,10 @@ private fun ScreenContent(
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium)
)
) { ) {
items( items(
count = apps.itemCount, count = apps.itemCount,
@@ -100,7 +103,9 @@ private fun ScreenContent(
apps[index]?.let { app -> apps[index]?.let { app ->
LargeAppListItem( LargeAppListItem(
app = app, app = app,
onClick = { onNavigateToAppDetails(app.packageName) } onClick = {
onNavigateTo(Destination.AppDetails(app.packageName))
}
) )
} }
} }

View File

@@ -50,7 +50,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -72,6 +71,7 @@ import com.aurora.store.compose.composable.Header
import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.navigation.Screen import com.aurora.store.compose.navigation.Screen
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
@@ -110,8 +110,7 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun AppDetailsScreen( fun AppDetailsScreen(
packageName: String, packageName: String,
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit,
viewModel: AppDetailsViewModel = hiltViewModel(key = packageName), viewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
forceSinglePane: Boolean = false forceSinglePane: Boolean = false
) { ) {
@@ -136,11 +135,10 @@ fun AppDetailsScreen(
) { currentState -> ) { currentState ->
when { when {
currentState is AppState.Loading || app == null -> currentState is AppState.Loading || app == null ->
ScreenContentLoading(onNavigateUp = onNavigateUp) ScreenContentLoading()
currentState is AppState.Error -> currentState is AppState.Error ->
ScreenContentError( ScreenContentError(
onNavigateUp = onNavigateUp,
message = currentState.message message = currentState.message
) )
@@ -156,8 +154,7 @@ fun AppDetailsScreen(
plexusScores = plexusScores, plexusScores = plexusScores,
dataSafetyReport = dataSafetyReport, dataSafetyReport = dataSafetyReport,
exodusReport = exodusReport, exodusReport = exodusReport,
onNavigateUp = onNavigateUp, onNavigateTo = onNavigateTo,
onNavigateToAppDetails = onNavigateToAppDetails,
onDownload = { requestedApp -> viewModel.enqueueDownload(requestedApp) }, onDownload = { requestedApp -> viewModel.enqueueDownload(requestedApp) },
onFavorite = { viewModel.toggleFavourite(loadedApp) }, onFavorite = { viewModel.toggleFavourite(loadedApp) },
onCancelDownload = { viewModel.cancelDownload(loadedApp) }, onCancelDownload = { viewModel.cancelDownload(loadedApp) },
@@ -196,9 +193,9 @@ private fun stateKey(state: AppState, app: App?): String = when {
* Composable to show progress while fetching app details * Composable to show progress while fetching app details
*/ */
@Composable @Composable
private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) { private fun ScreenContentLoading() {
Scaffold( Scaffold(
topBar = { TopAppBar(onNavigateUp = onNavigateUp) } topBar = { TopAppBar() }
) { paddingValues -> ) { paddingValues ->
ContainedLoadingIndicator(modifier = Modifier.padding(paddingValues)) ContainedLoadingIndicator(modifier = Modifier.padding(paddingValues))
} }
@@ -208,9 +205,9 @@ private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) {
* Composable to display errors related to fetching app details * Composable to display errors related to fetching app details
*/ */
@Composable @Composable
private fun ScreenContentError(onNavigateUp: () -> Unit = {}, message: String? = null) { private fun ScreenContentError(message: String? = null) {
Scaffold( Scaffold(
topBar = { TopAppBar(onNavigateUp = onNavigateUp) } topBar = { TopAppBar() }
) { paddingValues -> ) { paddingValues ->
Error( Error(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
@@ -234,8 +231,7 @@ private fun ScreenContentApp(
plexusScores: Scores? = null, plexusScores: Scores? = null,
dataSafetyReport: DataSafetyReport? = null, dataSafetyReport: DataSafetyReport? = null,
exodusReport: Report? = null, exodusReport: Report? = null,
onNavigateUp: () -> Unit = {}, onNavigateTo: (Destination) -> Unit = {},
onNavigateToAppDetails: (packageName: String) -> Unit = {},
onDownload: (requestedApp: App) -> Unit = {}, onDownload: (requestedApp: App) -> Unit = {},
onFavorite: () -> Unit = {}, onFavorite: () -> Unit = {},
onCancelDownload: () -> Unit = {}, onCancelDownload: () -> Unit = {},
@@ -412,7 +408,6 @@ private fun ScreenContentApp(
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
onNavigateUp = onNavigateUp,
actions = { if (shouldShowMenuOnMainPane) SetupMenu() } actions = { if (shouldShowMenuOnMainPane) SetupMenu() }
) )
} }
@@ -535,7 +530,6 @@ private fun ScreenContentApp(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }
@@ -546,7 +540,10 @@ private fun ScreenContentApp(
fun SupportingPane() { fun SupportingPane() {
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar(actions = { if (!shouldShowMenuOnMainPane) SetupMenu() }) TopAppBar(
showNavigationIcon = false,
actions = { if (!shouldShowMenuOnMainPane) SetupMenu() }
)
} }
) { paddingValues -> ) { paddingValues ->
if (suggestions.isNotEmpty()) { if (suggestions.isNotEmpty()) {
@@ -568,12 +565,15 @@ private fun ScreenContentApp(
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)) .padding(vertical = dimensionResource(R.dimen.padding_medium)),
verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium)
)
) { ) {
items(items = suggestions, key = { item -> item.id }) { app -> items(items = suggestions, key = { item -> item.id }) { app ->
LargeAppListItem( LargeAppListItem(
app = app, app = app,
onClick = { onNavigateToAppDetails(app.packageName) } onClick = { onNavigateTo(Destination.AppDetails(app.packageName)) }
) )
} }
} }
@@ -585,52 +585,43 @@ private fun ScreenContentApp(
@Composable @Composable
fun ExtraPane(screen: NavKey) = when (screen) { fun ExtraPane(screen: NavKey) = when (screen) {
is ExtraScreen.Review -> ReviewScreen( is ExtraScreen.Review -> ReviewScreen(
packageName = app.packageName, packageName = app.packageName
onNavigateUp = ::onNavigateBack
) )
is ExtraScreen.Exodus -> ExodusScreen( is ExtraScreen.Exodus -> ExodusScreen(
packageName = app.packageName, packageName = app.packageName
onNavigateUp = ::onNavigateBack
) )
is ExtraScreen.More -> MoreScreen( is ExtraScreen.More -> MoreScreen(
packageName = app.packageName, packageName = app.packageName,
onNavigateUp = ::onNavigateBack, onNavigateTo = onNavigateTo
onNavigateToAppDetails = onNavigateToAppDetails
) )
is ExtraScreen.Permission -> PermissionScreen( is ExtraScreen.Permission -> PermissionScreen(
packageName = app.packageName, packageName = app.packageName
onNavigateUp = ::onNavigateBack
) )
is ExtraScreen.Screenshot -> ScreenshotScreen( is ExtraScreen.Screenshot -> ScreenshotScreen(
packageName = app.packageName, packageName = app.packageName,
index = screen.index, index = screen.index
onNavigateUp = ::onNavigateBack
) )
is ExtraScreen.ManualDownload -> ManualDownloadScreen( is ExtraScreen.ManualDownload -> ManualDownloadScreen(
packageName = app.packageName, packageName = app.packageName,
onNavigateUp = ::onNavigateBack,
onRequestInstall = { requestedApp -> onInstall(requestedApp) } onRequestInstall = { requestedApp -> onInstall(requestedApp) }
) )
is ExtraScreen.MicroG -> MicroGScreen( is ExtraScreen.MicroG -> MicroGScreen(
packageName = app.packageName, packageName = app.packageName,
onNavigateUp = ::onNavigateBack,
onProceed = { onInstall(ignoreMicroG = true) } onProceed = { onInstall(ignoreMicroG = true) }
) )
is Screen.DevProfile -> DevProfileScreen( is Screen.DevProfile -> DevProfileScreen(
publisherId = app.developerName, publisherId = app.developerName,
onNavigateUp = ::onNavigateBack, onNavigateTo = onNavigateTo
onNavigateToAppDetails = { onNavigateToAppDetails(it) }
) )
is Screen.PermissionRationale -> PermissionRationaleScreen( is Screen.PermissionRationale -> PermissionRationaleScreen(
onNavigateUp = ::onNavigateBack,
requiredPermissions = screen.requiredPermissions, requiredPermissions = screen.requiredPermissions,
onPermissionCallback = { type -> onPermissionCallback = { type ->
val isStoragePermission = type == PermissionType.EXTERNAL_STORAGE || val isStoragePermission = type == PermissionType.EXTERNAL_STORAGE ||

View File

@@ -31,7 +31,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.Constants.EXODUS_REPORT_URL import com.aurora.Constants.EXODUS_REPORT_URL
@@ -56,7 +55,6 @@ import com.aurora.store.viewmodel.details.ExodusViewModel
@Composable @Composable
fun ExodusScreen( fun ExodusScreen(
packageName: String, packageName: String,
onNavigateUp: () -> Unit,
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
exodusViewModel: ExodusViewModel = hiltViewModel(key = "$packageName/exodus"), exodusViewModel: ExodusViewModel = hiltViewModel(key = "$packageName/exodus"),
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
@@ -80,7 +78,6 @@ fun ExodusScreen(
null -> { null -> {
ScreenContentError( ScreenContentError(
topAppBarTitle = topAppBarTitle, topAppBarTitle = topAppBarTitle,
onNavigateUp = onNavigateUp,
onRequestAnalysis = { context.browse("${EXODUS_SUBMIT_PAGE}${app!!.packageName}") } onRequestAnalysis = { context.browse("${EXODUS_SUBMIT_PAGE}${app!!.packageName}") }
) )
} }
@@ -90,7 +87,6 @@ fun ExodusScreen(
topAppBarTitle = topAppBarTitle, topAppBarTitle = topAppBarTitle,
report = report, report = report,
trackers = trackers, trackers = trackers,
onNavigateUp = onNavigateUp,
onRequestAnalysis = { context.browse("${EXODUS_SUBMIT_PAGE}${app!!.packageName}") } onRequestAnalysis = { context.browse("${EXODUS_SUBMIT_PAGE}${app!!.packageName}") }
) )
} }
@@ -102,7 +98,6 @@ private fun ScreenContentReport(
topAppBarTitle: String? = null, topAppBarTitle: String? = null,
report: Report? = null, report: Report? = null,
trackers: List<ExodusTracker> = emptyList(), trackers: List<ExodusTracker> = emptyList(),
onNavigateUp: () -> Unit = {},
onRequestAnalysis: () -> Unit = {}, onRequestAnalysis: () -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
@@ -112,8 +107,7 @@ private fun ScreenContentReport(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
}, },
floatingActionButton = { floatingActionButton = {
@@ -161,7 +155,6 @@ private fun ScreenContentReport(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }
@@ -174,7 +167,6 @@ private fun ScreenContentReport(
@Composable @Composable
private fun ScreenContentError( private fun ScreenContentError(
topAppBarTitle: String? = null, topAppBarTitle: String? = null,
onNavigateUp: () -> Unit = {},
onRequestAnalysis: () -> Unit = {}, onRequestAnalysis: () -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
@@ -182,8 +174,7 @@ private fun ScreenContentError(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->

View File

@@ -6,6 +6,8 @@
package com.aurora.store.compose.ui.details package com.aurora.store.compose.ui.details
import androidx.activity.ComponentActivity
import androidx.activity.compose.LocalActivity
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -69,7 +71,6 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun ManualDownloadScreen( fun ManualDownloadScreen(
packageName: String, packageName: String,
onNavigateUp: () -> Unit,
onRequestInstall: (requestedApp: App) -> Unit, onRequestInstall: (requestedApp: App) -> Unit,
viewModel: AppDetailsViewModel = hiltViewModel(key = packageName), viewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
@@ -85,7 +86,6 @@ fun ManualDownloadScreen(
state = state, state = state,
topAppBarTitle = topAppBarTitle, topAppBarTitle = topAppBarTitle,
currentVersionCode = app!!.versionCode, currentVersionCode = app!!.versionCode,
onNavigateUp = onNavigateUp,
onRequestInstall = { versionCode -> onRequestInstall = { versionCode ->
val requestedApp = app!!.copy( val requestedApp = app!!.copy(
versionCode = versionCode, versionCode = versionCode,
@@ -105,10 +105,10 @@ private fun ScreenContent(
state: AppState = AppState.Unavailable, state: AppState = AppState.Unavailable,
topAppBarTitle: String? = null, topAppBarTitle: String? = null,
currentVersionCode: Long = 0L, currentVersionCode: Long = 0L,
onNavigateUp: () -> Unit = {},
onRequestInstall: (versionCode: Long) -> Unit = {}, onRequestInstall: (versionCode: Long) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
val activity = LocalActivity.current as? ComponentActivity
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val snackBarHostState = remember { SnackbarHostState() } val snackBarHostState = remember { SnackbarHostState() }
val errorMessage = stringResource(R.string.manual_download_version_error) val errorMessage = stringResource(R.string.manual_download_version_error)
@@ -130,8 +130,7 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
}, },
snackbarHost = { SnackbarHost(hostState = snackBarHostState) } snackbarHost = { SnackbarHost(hostState = snackBarHostState) }
@@ -187,7 +186,7 @@ private fun ScreenContent(
) { ) {
FilledTonalButton( FilledTonalButton(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F),
onClick = onNavigateUp onClick = { activity?.onBackPressedDispatcher?.onBackPressed() }
) { ) {
Text( Text(
text = stringResource(R.string.action_close), text = stringResource(R.string.action_close),

View File

@@ -6,6 +6,8 @@
package com.aurora.store.compose.ui.details package com.aurora.store.compose.ui.details
import androidx.activity.ComponentActivity
import androidx.activity.compose.LocalActivity
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -55,7 +57,6 @@ import com.aurora.store.viewmodel.onboarding.MicroGViewModel
@Composable @Composable
fun MicroGScreen( fun MicroGScreen(
packageName: String, packageName: String,
onNavigateUp: () -> Unit,
onProceed: () -> Unit, onProceed: () -> Unit,
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
viewModel: MicroGViewModel = hiltViewModel(), viewModel: MicroGViewModel = hiltViewModel(),
@@ -70,7 +71,6 @@ fun MicroGScreen(
ScreenContent( ScreenContent(
topAppBarTitle = topAppBarTitle, topAppBarTitle = topAppBarTitle,
uiState = viewModel.uiState, uiState = viewModel.uiState,
onNavigateUp = onNavigateUp,
onInstall = { viewModel.downloadMicroG() }, onInstall = { viewModel.downloadMicroG() },
onRetry = { viewModel.retryDownload() }, onRetry = { viewModel.retryDownload() },
onProceed = onProceed onProceed = onProceed
@@ -81,12 +81,12 @@ fun MicroGScreen(
private fun ScreenContent( private fun ScreenContent(
topAppBarTitle: String? = null, topAppBarTitle: String? = null,
uiState: MicroGUIState = MicroGUIState(), uiState: MicroGUIState = MicroGUIState(),
onNavigateUp: () -> Unit = {},
onInstall: () -> Unit = {}, onInstall: () -> Unit = {},
onRetry: () -> Unit = {}, onRetry: () -> Unit = {},
onProceed: () -> Unit = {}, onProceed: () -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
val activity = LocalActivity.current as? ComponentActivity
val context = LocalContext.current val context = LocalContext.current
val listState = rememberLazyListState() val listState = rememberLazyListState()
@@ -94,8 +94,7 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
}, },
bottomBar = { bottomBar = {
@@ -112,7 +111,7 @@ private fun ScreenContent(
) { ) {
FilledTonalButton( FilledTonalButton(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F),
onClick = onNavigateUp onClick = { activity?.onBackPressedDispatcher?.onBackPressed() }
) { ) {
Text( Text(
text = stringResource(R.string.action_cancel), text = stringResource(R.string.action_cancel),
@@ -174,7 +173,6 @@ private fun ScreenContent(
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }

View File

@@ -32,7 +32,6 @@ import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.extensions.adaptiveNavigationIcon
@@ -44,6 +43,7 @@ import com.aurora.store.compose.composable.Info
import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.AppListItem import com.aurora.store.compose.composable.app.AppListItem
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.viewmodel.details.AppDetailsViewModel import com.aurora.store.viewmodel.details.AppDetailsViewModel
@@ -52,8 +52,7 @@ import com.aurora.store.viewmodel.details.MoreViewModel
@Composable @Composable
fun MoreScreen( fun MoreScreen(
packageName: String, packageName: String,
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit,
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
moreViewModel: MoreViewModel = hiltViewModel( moreViewModel: MoreViewModel = hiltViewModel(
key = "$packageName/more", key = "$packageName/more",
@@ -68,8 +67,7 @@ fun MoreScreen(
ScreenContent( ScreenContent(
app = app!!, app = app!!,
dependencies = dependencies, dependencies = dependencies,
onNavigateUp = onNavigateUp, onNavigateTo = onNavigateTo
onNavigateToAppDetails = onNavigateToAppDetails
) )
} }
@@ -77,8 +75,7 @@ fun MoreScreen(
private fun ScreenContent( private fun ScreenContent(
app: App, app: App,
dependencies: List<App>? = null, dependencies: List<App>? = null,
onNavigateUp: () -> Unit = {}, onNavigateTo: (Destination) -> Unit = {},
onNavigateToAppDetails: (packageName: String) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
val topAppBarTitle = when { val topAppBarTitle = when {
@@ -90,8 +87,7 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -126,7 +122,7 @@ private fun ScreenContent(
if (dependencies != null) { if (dependencies != null) {
AppDependencies( AppDependencies(
dependencies = dependencies, dependencies = dependencies,
onNavigateToAppDetails = onNavigateToAppDetails onNavigateTo = onNavigateTo
) )
} }
} }
@@ -137,7 +133,6 @@ private fun ScreenContent(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }
@@ -148,10 +143,7 @@ private fun ScreenContent(
* Composable to show dependencies of an app * Composable to show dependencies of an app
*/ */
@Composable @Composable
private fun AppDependencies( private fun AppDependencies(dependencies: List<App>, onNavigateTo: (Destination) -> Unit) {
dependencies: List<App>,
onNavigateToAppDetails: (packageName: String) -> Unit
) {
Header(title = stringResource(R.string.details_dependencies)) Header(title = stringResource(R.string.details_dependencies))
if (dependencies.isEmpty()) { if (dependencies.isEmpty()) {
Info( Info(
@@ -162,7 +154,7 @@ private fun AppDependencies(
items(items = dependencies, key = { item -> item.id }) { app -> items(items = dependencies, key = { item -> item.id }) { app ->
AppListItem( AppListItem(
app = app, app = app,
onClick = { onNavigateToAppDetails(app.packageName) } onClick = { onNavigateTo(Destination.AppDetails(app.packageName)) }
) )
} }
} }

View File

@@ -29,7 +29,6 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.extensions.adaptiveNavigationIcon
@@ -47,7 +46,6 @@ import com.aurora.store.viewmodel.details.PermissionViewModel
@Composable @Composable
fun PermissionScreen( fun PermissionScreen(
packageName: String, packageName: String,
onNavigateUp: () -> Unit,
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
permissionViewModel: PermissionViewModel = hiltViewModel( permissionViewModel: PermissionViewModel = hiltViewModel(
key = "$packageName/permission", key = "$packageName/permission",
@@ -67,7 +65,6 @@ fun PermissionScreen(
ScreenContent( ScreenContent(
topAppBarTitle = topAppBarTitle, topAppBarTitle = topAppBarTitle,
onNavigateUp = onNavigateUp,
permissionsInfo = permissionsInfo permissionsInfo = permissionsInfo
) )
} }
@@ -76,7 +73,6 @@ fun PermissionScreen(
private fun ScreenContent( private fun ScreenContent(
topAppBarTitle: String? = null, topAppBarTitle: String? = null,
permissionsInfo: Map<String, PermissionInfo> = emptyMap(), permissionsInfo: Map<String, PermissionInfo> = emptyMap(),
onNavigateUp: () -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
val packageManager = LocalContext.current.packageManager val packageManager = LocalContext.current.packageManager
@@ -85,8 +81,7 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -127,7 +122,6 @@ private fun ScreenContent(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }

View File

@@ -36,7 +36,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState import androidx.paging.LoadState
@@ -64,7 +63,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
@Composable @Composable
fun ReviewScreen( fun ReviewScreen(
packageName: String, packageName: String,
onNavigateUp: () -> Unit,
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
reviewViewModel: ReviewViewModel = hiltViewModel( reviewViewModel: ReviewViewModel = hiltViewModel(
key = "$packageName/review", key = "$packageName/review",
@@ -85,7 +83,6 @@ fun ReviewScreen(
ScreenContent( ScreenContent(
topAppBarTitle = topAppBarTitle, topAppBarTitle = topAppBarTitle,
reviews = reviews, reviews = reviews,
onNavigateUp = onNavigateUp,
onFilter = { filter -> reviewViewModel.fetchReviews(filter) } onFilter = { filter -> reviewViewModel.fetchReviews(filter) }
) )
} }
@@ -93,7 +90,6 @@ fun ReviewScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
topAppBarTitle: String? = null, topAppBarTitle: String? = null,
onNavigateUp: () -> Unit = {},
reviews: LazyPagingItems<Review> = emptyPagingItems(), reviews: LazyPagingItems<Review> = emptyPagingItems(),
onFilter: (filter: Review.Filter) -> Unit = {}, onFilter: (filter: Review.Filter) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
@@ -103,8 +99,7 @@ private fun ScreenContent(
Column { Column {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
FilterHeader { filter -> onFilter(filter) } FilterHeader { filter -> onFilter(filter) }
} }
@@ -142,7 +137,6 @@ private fun ScreenContent(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }

View File

@@ -17,7 +17,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
@@ -36,7 +36,6 @@ import com.aurora.store.viewmodel.details.AppDetailsViewModel
fun ScreenshotScreen( fun ScreenshotScreen(
packageName: String, packageName: String,
index: Int, index: Int,
onNavigateUp: () -> Unit,
viewModel: AppDetailsViewModel = hiltViewModel(key = packageName), viewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
@@ -49,7 +48,6 @@ fun ScreenshotScreen(
ScreenContent( ScreenContent(
topAppBarTitle = topAppBarTitle, topAppBarTitle = topAppBarTitle,
onNavigateUp = onNavigateUp,
screenshots = app!!.screenshots, screenshots = app!!.screenshots,
index = index index = index
) )
@@ -58,12 +56,11 @@ fun ScreenshotScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
topAppBarTitle: String? = null, topAppBarTitle: String? = null,
onNavigateUp: () -> Unit = {},
screenshots: List<Artwork> = emptyList(), screenshots: List<Artwork> = emptyList(),
index: Int = 0, index: Int = 0,
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
val displayMetrics = LocalContext.current.resources.displayMetrics val displayMetrics = LocalResources.current.displayMetrics
val pagerState = rememberPagerState(initialPage = index) { screenshots.size } val pagerState = rememberPagerState(initialPage = index) { screenshots.size }
LaunchedEffect(key1 = index) { LaunchedEffect(key1 = index) {
@@ -74,8 +71,7 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->

View File

@@ -6,6 +6,7 @@
package com.aurora.store.compose.ui.dev package com.aurora.store.compose.ui.dev
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -34,6 +35,7 @@ import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.viewmodel.details.DevProfileViewModel import com.aurora.store.viewmodel.details.DevProfileViewModel
@@ -48,8 +50,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
@Composable @Composable
fun DevProfileScreen( fun DevProfileScreen(
developerId: String, developerId: String,
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit,
viewModel: DevProfileViewModel = hiltViewModel() viewModel: DevProfileViewModel = hiltViewModel()
) { ) {
// TODO: Implement when migrating logic for current DevProfileFragment // TODO: Implement when migrating logic for current DevProfileFragment
@@ -62,8 +63,7 @@ fun DevProfileScreen(
@Composable @Composable
fun DevProfileScreen( fun DevProfileScreen(
publisherId: String, publisherId: String,
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit,
viewModel: SearchViewModel = hiltViewModel() viewModel: SearchViewModel = hiltViewModel()
) { ) {
val apps = viewModel.apps.collectAsLazyPagingItems() val apps = viewModel.apps.collectAsLazyPagingItems()
@@ -73,8 +73,7 @@ fun DevProfileScreen(
ScreenContent( ScreenContent(
apps = apps, apps = apps,
topAppBarTitle = publisherId, topAppBarTitle = publisherId,
onNavigateUp = onNavigateUp, onNavigateTo = onNavigateTo
onNavigateToAppDetails = onNavigateToAppDetails
) )
} }
@@ -82,16 +81,14 @@ fun DevProfileScreen(
private fun ScreenContent( private fun ScreenContent(
apps: LazyPagingItems<App>, apps: LazyPagingItems<App>,
topAppBarTitle: String, topAppBarTitle: String,
onNavigateUp: () -> Unit = {}, onNavigateTo: (Destination) -> Unit = {},
onNavigateToAppDetails: (packageName: String) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2()
) { ) {
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -111,13 +108,16 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)) .padding(vertical = dimensionResource(R.dimen.padding_medium)),
verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium)
)
) { ) {
items(count = apps.itemCount, key = apps.itemKey { it.id }) { index -> items(count = apps.itemCount, key = apps.itemKey { it.id }) { index ->
apps[index]?.let { app -> apps[index]?.let { app ->
LargeAppListItem( LargeAppListItem(
app = app, app = app,
onClick = { onNavigateToAppDetails(app.packageName) } onClick = { onNavigateTo(Destination.AppDetails(app.packageName)) }
) )
} }
} }

View File

@@ -35,7 +35,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.viewmodel.dispenser.DispenserViewModel import com.aurora.store.viewmodel.dispenser.DispenserViewModel
@Composable @Composable
fun DispenserScreen(onNavigateUp: () -> Unit, viewModel: DispenserViewModel = hiltViewModel()) { fun DispenserScreen(viewModel: DispenserViewModel = hiltViewModel()) {
val dispensers by viewModel.dispensers.collectAsStateWithLifecycle() val dispensers by viewModel.dispensers.collectAsStateWithLifecycle()
var shouldShowInputDialog by rememberSaveable { mutableStateOf(false) } var shouldShowInputDialog by rememberSaveable { mutableStateOf(false) }
@@ -63,7 +63,6 @@ fun DispenserScreen(onNavigateUp: () -> Unit, viewModel: DispenserViewModel = hi
ScreenContent( ScreenContent(
dispensers = dispensers, dispensers = dispensers,
onNavigateUp = onNavigateUp,
onAddDispenser = { shouldShowInputDialog = true }, onAddDispenser = { shouldShowInputDialog = true },
onRemoveDispenser = { url -> shouldRemoveDispenser = url } onRemoveDispenser = { url -> shouldRemoveDispenser = url }
) )
@@ -71,7 +70,6 @@ fun DispenserScreen(onNavigateUp: () -> Unit, viewModel: DispenserViewModel = hi
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
onNavigateUp: () -> Unit = {},
dispensers: Set<String> = emptySet(), dispensers: Set<String> = emptySet(),
onAddDispenser: () -> Unit = {}, onAddDispenser: () -> Unit = {},
onRemoveDispenser: (url: String) -> Unit = {} onRemoveDispenser: (url: String) -> Unit = {}
@@ -81,8 +79,7 @@ private fun ScreenContent(
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.pref_dispenser_title), title = stringResource(R.string.pref_dispenser_title)
onNavigateUp = onNavigateUp
) )
}, },
floatingActionButton = { floatingActionButton = {

View File

@@ -29,7 +29,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.PagingData import androidx.paging.PagingData
@@ -45,6 +44,7 @@ import com.aurora.store.compose.composable.DownloadListItem
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.compose.ui.downloads.menu.DownloadsMenu import com.aurora.store.compose.ui.downloads.menu.DownloadsMenu
@@ -57,9 +57,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
@Composable @Composable
fun DownloadsScreen( fun DownloadsScreen(
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
viewModel: DownloadsViewModel = hiltViewModel(), viewModel: DownloadsViewModel = hiltViewModel()
onNavigateToAppDetails: (packageName: String) -> Unit
) { ) {
val context = LocalContext.current val context = LocalContext.current
val downloads = viewModel.downloads.collectAsLazyPagingItems() val downloads = viewModel.downloads.collectAsLazyPagingItems()
@@ -79,9 +78,8 @@ fun DownloadsScreen(
) )
ScreenContent( ScreenContent(
onNavigateUp = onNavigateUp,
downloads = downloads, downloads = downloads,
onNavigateToAppDetails = onNavigateToAppDetails, onNavigateTo = onNavigateTo,
onCancelAll = { viewModel.cancelAll() }, onCancelAll = { viewModel.cancelAll() },
onForceClearAll = { viewModel.clearAll() }, onForceClearAll = { viewModel.clearAll() },
onClearFinished = { viewModel.clearFinished() }, onClearFinished = { viewModel.clearFinished() },
@@ -100,8 +98,7 @@ fun DownloadsScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
downloads: LazyPagingItems<Download> = emptyPagingItems(), downloads: LazyPagingItems<Download> = emptyPagingItems(),
onNavigateUp: () -> Unit = {}, onNavigateTo: (Destination) -> Unit = {},
onNavigateToAppDetails: (packageName: String) -> Unit = {},
onCancel: (packageName: String) -> Unit = {}, onCancel: (packageName: String) -> Unit = {},
onClear: (download: Download) -> Unit = {}, onClear: (download: Download) -> Unit = {},
onExport: (download: Download) -> Unit = {}, onExport: (download: Download) -> Unit = {},
@@ -133,7 +130,6 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_download_manager), title = stringResource(R.string.title_download_manager),
onNavigateUp = onNavigateUp,
actions = { if (downloads.itemCount != 0) SetupMenu() } actions = { if (downloads.itemCount != 0) SetupMenu() }
) )
} }
@@ -173,7 +169,9 @@ private fun ScreenContent(
modifier = Modifier.animateItem(), modifier = Modifier.animateItem(),
download = download, download = download,
onClick = { onClick = {
onNavigateToAppDetails(download.packageName) onNavigateTo(
Destination.AppDetails(download.packageName)
)
}, },
onClear = { onClear(download) }, onClear = { onClear(download) },
onCancel = { onCancel(download.packageName) }, onCancel = { onCancel(download.packageName) },
@@ -185,7 +183,6 @@ private fun ScreenContent(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }

View File

@@ -29,7 +29,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.PagingData import androidx.paging.PagingData
@@ -45,6 +44,7 @@ import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.FavouriteListItem import com.aurora.store.compose.composable.FavouriteListItem
import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.FavouritePreviewProvider import com.aurora.store.compose.preview.FavouritePreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.compose.ui.favourite.menu.FavouriteMenu import com.aurora.store.compose.ui.favourite.menu.FavouriteMenu
@@ -57,8 +57,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
@Composable @Composable
fun FavouriteScreen( fun FavouriteScreen(
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit,
viewModel: FavouriteViewModel = hiltViewModel() viewModel: FavouriteViewModel = hiltViewModel()
) { ) {
val context = LocalContext.current val context = LocalContext.current
@@ -90,8 +89,7 @@ fun FavouriteScreen(
ScreenContent( ScreenContent(
favourites = favourites, favourites = favourites,
onNavigateUp = onNavigateUp, onNavigateTo = onNavigateTo,
onNavigateToAppDetails = onNavigateToAppDetails,
onRemoveFavourite = { packageName -> viewModel.removeFavourite(packageName) }, onRemoveFavourite = { packageName -> viewModel.removeFavourite(packageName) },
onImportFavourites = { onImportFavourites = {
documentOpenLauncher.launch(arrayOf(JSON_MIME_TYPE)) documentOpenLauncher.launch(arrayOf(JSON_MIME_TYPE))
@@ -107,8 +105,7 @@ fun FavouriteScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
favourites: LazyPagingItems<Favourite> = emptyPagingItems(), favourites: LazyPagingItems<Favourite> = emptyPagingItems(),
onNavigateUp: () -> Unit = {}, onNavigateTo: (Destination) -> Unit = {},
onNavigateToAppDetails: (packageName: String) -> Unit = {},
onRemoveFavourite: (packageName: String) -> Unit = {}, onRemoveFavourite: (packageName: String) -> Unit = {},
onImportFavourites: () -> Unit = {}, onImportFavourites: () -> Unit = {},
onExportFavourites: () -> Unit = {} onExportFavourites: () -> Unit = {}
@@ -135,7 +132,6 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_favourites_manager), title = stringResource(R.string.title_favourites_manager),
onNavigateUp = onNavigateUp,
actions = { SetupMenu() } actions = { SetupMenu() }
) )
} }
@@ -175,7 +171,9 @@ private fun ScreenContent(
modifier = Modifier.animateItem(), modifier = Modifier.animateItem(),
favourite = favourite, favourite = favourite,
onClick = { onClick = {
onNavigateToAppDetails(favourite.packageName) onNavigateTo(
Destination.AppDetails(favourite.packageName)
)
}, },
onClear = { onRemoveFavourite(favourite.packageName) } onClear = { onRemoveFavourite(favourite.packageName) }
) )
@@ -184,7 +182,6 @@ private fun ScreenContent(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }

View File

@@ -6,6 +6,7 @@
package com.aurora.store.compose.ui.installed package com.aurora.store.compose.ui.installed
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -20,12 +21,12 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.PagingData import androidx.paging.PagingData
@@ -40,6 +41,7 @@ import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.viewmodel.all.InstalledViewModel import com.aurora.store.viewmodel.all.InstalledViewModel
@@ -48,24 +50,21 @@ import kotlinx.coroutines.flow.MutableStateFlow
@Composable @Composable
fun InstalledScreen( fun InstalledScreen(
onNavigateUp: () -> Unit, onNavigateTo: (Destination) -> Unit,
onNavigateToAppDetails: (packageName: String) -> Unit,
viewModel: InstalledViewModel = hiltViewModel() viewModel: InstalledViewModel = hiltViewModel()
) { ) {
val apps = viewModel.apps.collectAsLazyPagingItems() val apps = viewModel.apps.collectAsLazyPagingItems()
ScreenContent( ScreenContent(
apps = apps, apps = apps,
onNavigateUp = onNavigateUp, onNavigateTo = onNavigateTo
onNavigateToAppDetails = onNavigateToAppDetails
) )
} }
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
onNavigateUp: () -> Unit = {},
apps: LazyPagingItems<App> = emptyPagingItems(), apps: LazyPagingItems<App> = emptyPagingItems(),
onNavigateToAppDetails: (packageName: String) -> Unit = {} onNavigateTo: (Destination) -> Unit = {}
) { ) {
/* /*
* For some reason paging3 frequently out-of-nowhere invalidates the list which causes * For some reason paging3 frequently out-of-nowhere invalidates the list which causes
@@ -78,8 +77,7 @@ private fun ScreenContent(
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_apps_games), title = stringResource(R.string.title_apps_games)
onNavigateUp = onNavigateUp
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -106,7 +104,10 @@ private fun ScreenContent(
val listState = rememberLazyListState() val listState = rememberLazyListState()
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
LazyColumn( LazyColumn(
state = listState state = listState,
verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium)
)
) { ) {
items( items(
count = apps.itemCount, count = apps.itemCount,
@@ -115,14 +116,17 @@ private fun ScreenContent(
apps[index]?.let { app -> apps[index]?.let { app ->
LargeAppListItem( LargeAppListItem(
app = app, app = app,
onClick = { onNavigateToAppDetails(app.packageName) } onClick = {
onNavigateTo(
Destination.AppDetails(app.packageName)
)
}
) )
} }
} }
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }

View File

@@ -23,7 +23,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.aurora.extensions.toast import com.aurora.extensions.toast
import com.aurora.store.R import com.aurora.store.R
@@ -105,7 +104,6 @@ private fun ScreenContent(
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }

View File

@@ -6,8 +6,9 @@
package com.aurora.store.compose.ui.search package com.aurora.store.compose.ui.search
import androidx.activity.ComponentActivity
import androidx.activity.compose.LocalActivity
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.focusable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -62,7 +63,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState import androidx.paging.LoadState
@@ -78,6 +78,7 @@ import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.SearchSuggestionListItem import com.aurora.store.compose.composable.SearchSuggestionListItem
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.compose.ui.details.AppDetailsScreen import com.aurora.store.compose.ui.details.AppDetailsScreen
@@ -91,7 +92,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun SearchScreen(onNavigateUp: () -> Unit, viewModel: SearchViewModel = hiltViewModel()) { fun SearchScreen(viewModel: SearchViewModel = hiltViewModel()) {
val suggestions by viewModel.suggestions.collectAsStateWithLifecycle() val suggestions by viewModel.suggestions.collectAsStateWithLifecycle()
val results = viewModel.apps.collectAsLazyPagingItems() val results = viewModel.apps.collectAsLazyPagingItems()
@@ -102,7 +103,6 @@ fun SearchScreen(onNavigateUp: () -> Unit, viewModel: SearchViewModel = hiltView
ScreenContent( ScreenContent(
suggestions = suggestions, suggestions = suggestions,
results = results, results = results,
onNavigateUp = onNavigateUp,
onSearch = onSearchCallback, onSearch = onSearchCallback,
onFetchSuggestions = onFetchSuggestionsCallback, onFetchSuggestions = onFetchSuggestionsCallback,
onFilter = { filter -> viewModel.filterResults(filter) }, onFilter = { filter -> viewModel.filterResults(filter) },
@@ -114,12 +114,12 @@ fun SearchScreen(onNavigateUp: () -> Unit, viewModel: SearchViewModel = hiltView
private fun ScreenContent( private fun ScreenContent(
suggestions: List<SearchSuggestEntry> = emptyList(), suggestions: List<SearchSuggestEntry> = emptyList(),
results: LazyPagingItems<App> = emptyPagingItems(), results: LazyPagingItems<App> = emptyPagingItems(),
onNavigateUp: () -> Unit = {},
onFetchSuggestions: (String) -> Unit = {}, onFetchSuggestions: (String) -> Unit = {},
onSearch: (String) -> Unit = {}, onSearch: (String) -> Unit = {},
onFilter: (filter: SearchFilter) -> Unit = {}, onFilter: (filter: SearchFilter) -> Unit = {},
isAnonymous: Boolean = true isAnonymous: Boolean = true
) { ) {
val activity = LocalActivity.current as? ComponentActivity
val textFieldState = rememberTextFieldState() val textFieldState = rememberTextFieldState()
val searchBarState = rememberSearchBarState() val searchBarState = rememberSearchBarState()
var isSearching by rememberSaveable { mutableStateOf(false) } var isSearching by rememberSaveable { mutableStateOf(false) }
@@ -186,7 +186,7 @@ private fun ScreenContent(
) )
}, },
leadingIcon = { leadingIcon = {
IconButton(onClick = onNavigateUp) { IconButton(onClick = { activity?.onBackPressedDispatcher?.onBackPressed() }) {
Icon( Icon(
painter = painterResource(R.drawable.ic_arrow_back), painter = painterResource(R.drawable.ic_arrow_back),
contentDescription = stringResource(R.string.action_back) contentDescription = stringResource(R.string.action_back)
@@ -231,10 +231,7 @@ private fun ScreenContent(
@Composable @Composable
fun ListPane() { fun ListPane() {
// TODO: https://issuetracker.google.com/issues/445720462
Scaffold( Scaffold(
modifier = Modifier
.focusable(),
topBar = { SearchBar() } topBar = { SearchBar() }
) { paddingValues -> ) { paddingValues ->
Column( Column(
@@ -272,7 +269,10 @@ private fun ScreenContent(
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
LazyColumn( LazyColumn(
state = listState, state = listState,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium)
)
) { ) {
items( items(
count = results.itemCount, count = results.itemCount,
@@ -288,7 +288,6 @@ private fun ScreenContent(
} }
ScrollHint( ScrollHint(
listState = listState, listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter) modifier = Modifier.align(Alignment.BottomCenter)
) )
} }
@@ -306,9 +305,10 @@ private fun ScreenContent(
this != null -> { this != null -> {
AppDetailsScreen( AppDetailsScreen(
packageName = this, packageName = this,
onNavigateToAppDetails = { packageName -> showDetailPane(packageName) }, onNavigateTo = { destination ->
onNavigateUp = { if (destination is Destination.AppDetails) {
coroutineScope.launch { scaffoldNavigator.navigateBack() } showDetailPane(destination.packageName)
}
}, },
forceSinglePane = true forceSinglePane = true
) )

View File

@@ -36,6 +36,7 @@ import com.aurora.Constants
import com.aurora.extensions.toast import com.aurora.extensions.toast
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.navigation.Destination
import com.aurora.store.compose.ui.spoof.menu.MenuItem import com.aurora.store.compose.ui.spoof.menu.MenuItem
import com.aurora.store.compose.ui.spoof.menu.SpoofMenu import com.aurora.store.compose.ui.spoof.menu.SpoofMenu
import com.aurora.store.compose.ui.spoof.navigation.SpoofPage import com.aurora.store.compose.ui.spoof.navigation.SpoofPage
@@ -44,14 +45,9 @@ import com.aurora.store.viewmodel.spoof.SpoofViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun SpoofScreen( fun SpoofScreen(onNavigateTo: (Destination) -> Unit, viewModel: SpoofViewModel = hiltViewModel()) {
onNavigateUp: () -> Unit,
onNavigateToSplash: () -> Unit,
viewModel: SpoofViewModel = hiltViewModel()
) {
ScreenContent( ScreenContent(
onNavigateUp = onNavigateUp, onNavigateTo = onNavigateTo,
onNavigateToSplash = onNavigateToSplash,
onDeviceSpoofImport = { uri -> viewModel.importDeviceSpoof(uri) }, onDeviceSpoofImport = { uri -> viewModel.importDeviceSpoof(uri) },
onDeviceSpoofExport = { uri -> viewModel.exportDeviceSpoof(uri) } onDeviceSpoofExport = { uri -> viewModel.exportDeviceSpoof(uri) }
) )
@@ -60,8 +56,7 @@ fun SpoofScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
pages: List<SpoofPage> = listOf(SpoofPage.DEVICE, SpoofPage.LOCALE), pages: List<SpoofPage> = listOf(SpoofPage.DEVICE, SpoofPage.LOCALE),
onNavigateUp: () -> Unit = {}, onNavigateTo: (Destination) -> Unit = {},
onNavigateToSplash: () -> Unit = {},
onDeviceSpoofImport: (uri: Uri) -> Unit = {}, onDeviceSpoofImport: (uri: Uri) -> Unit = {},
onDeviceSpoofExport: (uri: Uri) -> Unit = {} onDeviceSpoofExport: (uri: Uri) -> Unit = {}
) { ) {
@@ -101,7 +96,7 @@ private fun ScreenContent(
when (result) { when (result) {
SnackbarResult.ActionPerformed -> { SnackbarResult.ActionPerformed -> {
AccountProvider.logout(context) AccountProvider.logout(context)
onNavigateToSplash() onNavigateTo(Destination.Splash)
} }
else -> Unit else -> Unit
@@ -133,7 +128,6 @@ private fun ScreenContent(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_spoof_manager), title = stringResource(R.string.title_spoof_manager),
onNavigateUp = onNavigateUp,
actions = { SetupMenu() } actions = { SetupMenu() }
) )
} }

View File

@@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: 2026 Aurora OSS
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.data.providers package com.aurora.store.data.providers
import android.Manifest import android.Manifest

View File

@@ -10,14 +10,13 @@ import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.PendingIntentCompat import androidx.core.app.PendingIntentCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.navigation.NavDeepLinkBuilder
import com.aurora.Constants import com.aurora.Constants
import com.aurora.store.MainActivity import com.aurora.store.ComposeActivity
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.navigation.Screen
import com.aurora.store.data.activity.InstallActivity import com.aurora.store.data.activity.InstallActivity
import com.aurora.store.data.helper.DownloadHelper import com.aurora.store.data.helper.DownloadHelper
import com.aurora.store.data.installer.AppInstaller import com.aurora.store.data.installer.AppInstaller
@@ -213,16 +212,7 @@ object NotificationUtil {
.build() .build()
fun getUpdateNotification(context: Context, updatesList: List<Update>): Notification { fun getUpdateNotification(context: Context, updatesList: List<Update>): Notification {
val arguments = Bundle().apply { val contentIntent = getContentIntentForMain(context, initialTab = 2)
putInt("destinationId", R.id.updatesFragment)
}
val contentIntent = NavDeepLinkBuilder(context)
.setGraph(R.navigation.mobile_navigation)
.setDestination(R.id.splashFragment)
.setComponentName(MainActivity::class.java)
.setArguments(arguments)
.createPendingIntent()
return NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_UPDATES) return NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_UPDATES)
.setSmallIcon(R.drawable.ic_updates) .setSmallIcon(R.drawable.ic_updates)
@@ -297,7 +287,7 @@ object NotificationUtil {
.setContentTitle(context.getString(R.string.authentication_required_title)) .setContentTitle(context.getString(R.string.authentication_required_title))
.setContentText(context.getString(R.string.authentication_required_unarchive)) .setContentText(context.getString(R.string.authentication_required_unarchive))
.setAutoCancel(true) .setAutoCancel(true)
.setContentIntent(getContentIntentForSplash(context, packageName)) .setContentIntent(getContentIntentForDetails(context, packageName))
.setCategory(NotificationCompat.CATEGORY_ERROR) .setCategory(NotificationCompat.CATEGORY_ERROR)
.setPriority(NotificationCompat.PRIORITY_HIGH) .setPriority(NotificationCompat.PRIORITY_HIGH)
.build() .build()
@@ -359,28 +349,32 @@ object NotificationUtil {
) )
} }
private fun getContentIntentForSplash(context: Context, packageName: String): PendingIntent { private fun getContentIntentForDetails(context: Context, packageName: String): PendingIntent? {
val arguments = Bundle().apply { val intent = Intent(context, ComposeActivity::class.java).apply {
putString("packageName", packageName) putExtra(Screen.PARCEL_KEY, Screen.AppDetails(packageName) as android.os.Parcelable)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
} }
return NavDeepLinkBuilder(context) return PendingIntentCompat.getActivity(
.setGraph(R.navigation.mobile_navigation) context,
.setDestination(R.id.splashFragment) packageName.hashCode().absoluteValue,
.setComponentName(MainActivity::class.java) intent,
.setArguments(arguments) PendingIntent.FLAG_UPDATE_CURRENT,
.createPendingIntent() false
)
} }
private fun getContentIntentForDetails(context: Context, packageName: String): PendingIntent { private fun getContentIntentForMain(context: Context, initialTab: Int): PendingIntent? {
val arguments = Bundle().apply { val intent = Intent(context, ComposeActivity::class.java).apply {
putString("packageName", packageName) putExtra(Screen.PARCEL_KEY, Screen.Main(initialTab) as android.os.Parcelable)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
} }
return NavDeepLinkBuilder(context) return PendingIntentCompat.getActivity(
.setGraph(R.navigation.mobile_navigation) context,
.setDestination(R.id.splashFragment) initialTab,
.setComponentName(MainActivity::class.java) intent,
.setArguments(arguments) PendingIntent.FLAG_UPDATE_CURRENT,
.createPendingIntent() false
)
} }
private fun getInstallIntent(context: Context, download: Download): PendingIntent? { private fun getInstallIntent(context: Context, download: Download): PendingIntent? {

View File

@@ -34,6 +34,7 @@ import com.aurora.store.data.model.AccountType
import com.aurora.store.data.model.AuthState import com.aurora.store.data.model.AuthState
import com.aurora.store.data.providers.AccountProvider import com.aurora.store.data.providers.AccountProvider
import com.aurora.store.data.providers.AuthProvider import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.data.providers.NetworkProvider
import com.aurora.store.util.AC2DMTask import com.aurora.store.util.AC2DMTask
import com.aurora.store.util.PackageUtil import com.aurora.store.util.PackageUtil
import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences
@@ -50,6 +51,7 @@ import kotlinx.coroutines.launch
@HiltViewModel @HiltViewModel
class AuthViewModel @Inject constructor( class AuthViewModel @Inject constructor(
val authProvider: AuthProvider, val authProvider: AuthProvider,
val networkProvider: NetworkProvider,
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val aC2DMTask: AC2DMTask private val aC2DMTask: AC2DMTask
) : ViewModel() { ) : ViewModel() {
@@ -117,6 +119,8 @@ class AuthViewModel @Inject constructor(
} }
} }
fun retry() = updateAuthState()
private fun updateAuthState() { private fun updateAuthState() {
if (_authState.value != AuthState.Fetching) { if (_authState.value != AuthState.Fetching) {
if (AccountProvider.isLoggedIn(context)) { if (AccountProvider.isLoggedIn(context)) {

View File

@@ -1,85 +1,90 @@
/* /*
* Aurora Store * SPDX-FileCopyrightText: 2024 Aurora OSS
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com> * SPDX-FileCopyrightText: 2021 Rahul Kumar Patel <whyorean@gmail.com>
* * SPDX-License-Identifier: GPL-3.0-or-later
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
package com.aurora.store.viewmodel.browse package com.aurora.store.viewmodel.browse
import android.util.Log import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.aurora.extensions.TAG import com.aurora.extensions.TAG
import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.helpers.ExpandedBrowseHelper import com.aurora.gplayapi.helpers.ExpandedBrowseHelper
import com.aurora.store.data.PageResult
import com.aurora.store.data.paging.GenericPagingSource.Companion.manualPager
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@HiltViewModel @HiltViewModel(assistedFactory = ExpandedStreamBrowseViewModel.Factory::class)
class ExpandedStreamBrowseViewModel @Inject constructor( class ExpandedStreamBrowseViewModel @AssistedInject constructor(
@Assisted val browseUrl: String,
private val streamHelper: ExpandedBrowseHelper private val streamHelper: ExpandedBrowseHelper
) : ViewModel() { ) : ViewModel() {
val liveData: MutableLiveData<StreamCluster> = MutableLiveData() @AssistedFactory
var streamCluster: StreamCluster = StreamCluster() interface Factory {
fun create(browseUrl: String): ExpandedStreamBrowseViewModel
}
fun getInitialCluster(expandedStreamUrl: String) { private val _title = MutableStateFlow("")
viewModelScope.launch(Dispatchers.IO) { val title = _title.asStateFlow()
supervisorScope {
try { private val _apps = MutableStateFlow<PagingData<App>>(PagingData.empty())
val browseResponse = streamHelper.getBrowseStreamResponse(expandedStreamUrl) val apps = _apps.asStateFlow()
init {
fetchApps()
}
private fun fetchApps() {
var listUrl = ""
var nextPageUrl = ""
manualPager { page ->
val items = try {
when (page) {
1 -> {
val browseResponse = streamHelper.getBrowseStreamResponse(browseUrl)
if (browseResponse.hasBrowseTab()) { if (browseResponse.hasBrowseTab()) {
streamCluster = listUrl = browseResponse.browseTab.listUrl
streamHelper.getExpandedBrowseClusters(browseResponse.browseTab.listUrl) val cluster = streamHelper.getExpandedBrowseClusters(listUrl)
liveData.postValue(streamCluster) _title.value = cluster.clusterTitle
nextPageUrl = cluster.clusterNextPageUrl
cluster.clusterAppList
} else { } else {
} emptyList()
} catch (_: Exception) {
}
}
} }
} }
fun next() { else -> {
Log.e(TAG, "NEXT CALED") if (nextPageUrl.isNotBlank()) {
viewModelScope.launch(Dispatchers.IO) { val cluster = streamHelper.getExpandedBrowseClusters(nextPageUrl)
supervisorScope { nextPageUrl = cluster.clusterNextPageUrl
try { cluster.clusterAppList
val newCluster = streamHelper.getExpandedBrowseClusters( } else {
streamCluster.clusterNextPageUrl emptyList()
) }
}
streamCluster = streamCluster.copy(
clusterAppList = streamCluster.clusterAppList + newCluster.clusterAppList,
clusterNextPageUrl = newCluster.clusterNextPageUrl
)
liveData.postValue(streamCluster)
if (!streamCluster.hasNext()) {
Log.i(TAG, "End of Bundle")
} }
} catch (exception: Exception) { } catch (exception: Exception) {
Log.e(TAG, "Failed to fetch next stream", exception) Log.e(TAG, "Failed to fetch apps for page $page", exception)
} emptyList()
} }
} PageResult(items)
}.flow.distinctUntilChanged()
.cachedIn(viewModelScope)
.onEach { _apps.value = it }
.launchIn(viewModelScope)
} }
} }

View File

@@ -1,26 +1,12 @@
/* /*
* Aurora Store * SPDX-FileCopyrightText: 2024 Aurora OSS
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com> * SPDX-FileCopyrightText: 2021 Rahul Kumar Patel <whyorean@gmail.com>
* * SPDX-License-Identifier: GPL-3.0-or-later
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
package com.aurora.store.viewmodel.subcategory package com.aurora.store.viewmodel.subcategory
import android.util.Log import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.aurora.extensions.TAG import com.aurora.extensions.TAG
@@ -30,70 +16,82 @@ import com.aurora.gplayapi.helpers.contracts.CategoryStreamContract
import com.aurora.gplayapi.helpers.contracts.StreamContract import com.aurora.gplayapi.helpers.contracts.StreamContract
import com.aurora.gplayapi.helpers.web.WebCategoryStreamHelper import com.aurora.gplayapi.helpers.web.WebCategoryStreamHelper
import com.aurora.store.data.model.ViewState import com.aurora.store.data.model.ViewState
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
@HiltViewModel @HiltViewModel(assistedFactory = CategoryStreamViewModel.Factory::class)
class CategoryStreamViewModel @Inject constructor( class CategoryStreamViewModel @AssistedInject constructor(
@Assisted val browseUrl: String,
private val webCategoryStreamHelper: WebCategoryStreamHelper private val webCategoryStreamHelper: WebCategoryStreamHelper
) : ViewModel() { ) : ViewModel() {
val liveData: MutableLiveData<ViewState> = MutableLiveData() @AssistedFactory
interface Factory {
fun create(browseUrl: String): CategoryStreamViewModel
}
private var stash: MutableMap<String, StreamBundle> = mutableMapOf() // SharedFlow (instead of StateFlow) because StreamBundle/StreamCluster override equals
// to compare only id, which is preserved by copy(). StateFlow would conflate every update
// and break paginated scroll loading.
private val _viewState = MutableSharedFlow<ViewState>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val viewState: SharedFlow<ViewState> = _viewState.asSharedFlow()
private val categoryStreamContract: CategoryStreamContract private val categoryStreamContract: CategoryStreamContract
get() = webCategoryStreamHelper get() = webCategoryStreamHelper
fun getStreamBundle(browseUrl: String) { private var streamBundle = StreamBundle()
liveData.postValue(ViewState.Loading)
observe(browseUrl) init {
fetchNextPage()
} }
fun observe(browseUrl: String) { fun fetchNextPage() {
liveData.postValue(ViewState.Loading)
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
supervisorScope { supervisorScope {
val bundle = targetBundle(browseUrl) if (streamBundle.streamClusters.isNotEmpty()) {
if (bundle.streamClusters.isNotEmpty()) { _viewState.tryEmit(ViewState.Success(streamBundle))
liveData.postValue(ViewState.Success(stash))
} }
try { try {
if (!bundle.hasCluster() || bundle.hasNext()) { if (!streamBundle.hasCluster() || streamBundle.hasNext()) {
// Fetch new stream bundle val newBundle = if (streamBundle.streamClusters.isEmpty()) {
val newBundle = if (bundle.streamClusters.isEmpty()) {
categoryStreamContract.fetch(browseUrl) categoryStreamContract.fetch(browseUrl)
} else { } else {
categoryStreamContract.nextStreamBundle( categoryStreamContract.nextStreamBundle(
StreamContract.Category.NONE, StreamContract.Category.NONE,
bundle.streamNextPageUrl streamBundle.streamNextPageUrl
) )
} }
// Update old bundle streamBundle = streamBundle.copy(
val mergedBundle = bundle.copy( streamClusters = streamBundle.streamClusters + newBundle.streamClusters,
streamClusters = bundle.streamClusters + newBundle.streamClusters,
streamNextPageUrl = newBundle.streamNextPageUrl streamNextPageUrl = newBundle.streamNextPageUrl
) )
stash[browseUrl] = mergedBundle
// Post updated to UI _viewState.tryEmit(ViewState.Success(streamBundle))
liveData.postValue(ViewState.Success(stash))
} else { } else {
Log.i(TAG, "End of Bundle") Log.i(TAG, "End of Bundle")
} }
} catch (e: Exception) { } catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message)) _viewState.tryEmit(ViewState.Error(e.message))
} }
} }
} }
} }
fun observeCluster(browseUrl: String, streamCluster: StreamCluster) { fun fetchNextCluster(streamCluster: StreamCluster) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
supervisorScope { supervisorScope {
try { try {
@@ -101,36 +99,24 @@ class CategoryStreamViewModel @Inject constructor(
val newCluster = categoryStreamContract.nextStreamCluster( val newCluster = categoryStreamContract.nextStreamCluster(
streamCluster.clusterNextPageUrl streamCluster.clusterNextPageUrl
) )
updateCluster(browseUrl, streamCluster.id, newCluster) val mergedCluster = streamCluster.copy(
liveData.postValue(ViewState.Success(stash)) clusterNextPageUrl = newCluster.clusterNextPageUrl,
clusterAppList =
streamCluster.clusterAppList + newCluster.clusterAppList
)
val newClusters = streamBundle.streamClusters.toMutableMap().apply {
this[streamCluster.id] = mergedCluster
}
streamBundle = streamBundle.copy(streamClusters = newClusters)
_viewState.tryEmit(ViewState.Success(streamBundle))
} else { } else {
Log.i(TAG, "End of cluster") Log.i(TAG, "End of cluster")
} }
} catch (e: Exception) { } catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message)) Log.e(TAG, "Failed to fetch next cluster", e)
} }
} }
} }
} }
private fun updateCluster(browseUrl: String, clusterID: Int, newCluster: StreamCluster) {
val bundle = targetBundle(browseUrl)
bundle.streamClusters[clusterID]?.let { oldCluster ->
val mergedCluster = oldCluster.copy(
clusterNextPageUrl = newCluster.clusterNextPageUrl,
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
)
val newStreamClusters = bundle.streamClusters.toMutableMap().apply {
this[clusterID] = mergedCluster
}
stash.put(browseUrl, bundle.copy(streamClusters = newStreamClusters))
}
}
private fun targetBundle(browseUrl: String): StreamBundle {
val streamBundle = stash.getOrPut(browseUrl) { StreamBundle() }
return streamBundle
}
} }

View File

@@ -19,7 +19,6 @@
package com.aurora.store.viewmodel.topchart package com.aurora.store.viewmodel.topchart
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.data.models.StreamCluster
@@ -30,6 +29,10 @@ import com.aurora.store.data.model.ViewState
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
@@ -40,7 +43,14 @@ class TopChartViewModel @Inject constructor(
private var stash: TopChartStash = mutableMapOf() private var stash: TopChartStash = mutableMapOf()
val liveData: MutableLiveData<ViewState> = MutableLiveData() // SharedFlow (instead of StateFlow) because StreamCluster overrides equals to compare
// only id, which is preserved by copy(). StateFlow would conflate paginated updates and
// break scroll loading. See CategoryStreamViewModel for the same fix.
private val _state = MutableSharedFlow<ViewState>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val state: SharedFlow<ViewState> = _state.asSharedFlow()
private val topChartsContract: TopChartsContract private val topChartsContract: TopChartsContract
get() = webTopChartsHelper get() = webTopChartsHelper
@@ -48,14 +58,18 @@ class TopChartViewModel @Inject constructor(
fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) { fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
if (targetCluster(type, chart).clusterAppList.isNotEmpty()) { if (targetCluster(type, chart).clusterAppList.isNotEmpty()) {
liveData.postValue(ViewState.Success(stash)) _state.tryEmit(ViewState.Success(targetCluster(type, chart)))
return@launch
} }
_state.tryEmit(ViewState.Loading)
try { try {
val cluster = topChartsContract.getCluster(type.value, chart.value) val cluster = topChartsContract.getCluster(type.value, chart.value)
updateCluster(type, chart, cluster) updateCluster(type, chart, cluster)
liveData.postValue(ViewState.Success(stash)) _state.tryEmit(ViewState.Success(targetCluster(type, chart)))
} catch (_: Exception) { } catch (e: Exception) {
_state.tryEmit(ViewState.Error(e.message))
} }
} }
} }
@@ -72,7 +86,7 @@ class TopChartViewModel @Inject constructor(
updateCluster(type, chart, newCluster) updateCluster(type, chart, newCluster)
liveData.postValue(ViewState.Success(stash)) _state.tryEmit(ViewState.Success(targetCluster(type, chart)))
} }
} catch (_: Exception) { } catch (_: Exception) {
} }