From 0270dc6f392eb305b8583c6ceeb04e02ec38542a Mon Sep 17 00:00:00 2001 From: Rahul Patel Date: Tue, 19 May 2026 12:13:20 +0530 Subject: [PATCH] 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. --- .../store/compose/ui/about/AboutScreen.kt | 8 +- .../compose/ui/accounts/AccountsScreen.kt | 19 +-- .../compose/ui/blacklist/BlacklistScreen.kt | 11 +- .../ui/commons/CategoryBrowseScreen.kt | 65 +++++++++ .../ui/commons/ExpandedStreamBrowseScreen.kt | 98 ++++++++++++++ .../ui/commons/PermissionRationaleScreen.kt | 6 +- .../compose/ui/commons/StreamBrowseScreen.kt | 23 ++-- .../compose/ui/details/AppDetailsScreen.kt | 57 ++++---- .../store/compose/ui/details/ExodusScreen.kt | 13 +- .../ui/details/ManualDownloadScreen.kt | 11 +- .../store/compose/ui/details/MicroGScreen.kt | 12 +- .../store/compose/ui/details/MoreScreen.kt | 24 ++-- .../compose/ui/details/PermissionScreen.kt | 8 +- .../store/compose/ui/details/ReviewScreen.kt | 8 +- .../compose/ui/details/ScreenshotScreen.kt | 10 +- .../store/compose/ui/dev/DevProfileScreen.kt | 24 ++-- .../compose/ui/dispenser/DispenserScreen.kt | 7 +- .../compose/ui/downloads/DownloadsScreen.kt | 19 ++- .../compose/ui/favourite/FavouriteScreen.kt | 17 +-- .../compose/ui/installed/InstalledScreen.kt | 28 ++-- .../store/compose/ui/onboarding/MicroGPage.kt | 2 - .../store/compose/ui/search/SearchScreen.kt | 28 ++-- .../store/compose/ui/spoof/SpoofScreen.kt | 16 +-- .../data/providers/PermissionProvider.kt | 5 + .../com/aurora/store/util/NotificationUtil.kt | 58 ++++---- .../store/viewmodel/auth/AuthViewModel.kt | 4 + .../browse/ExpandedStreamBrowseViewModel.kt | 125 +++++++++--------- .../subcategory/CategoryStreamViewModel.kt | 118 ++++++++--------- .../viewmodel/topchart/TopChartViewModel.kt | 26 +++- 29 files changed, 478 insertions(+), 372 deletions(-) create mode 100644 app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt create mode 100644 app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt diff --git a/app/src/main/java/com/aurora/store/compose/ui/about/AboutScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/about/AboutScreen.kt index 79936b56b..e5cc278a3 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/about/AboutScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/about/AboutScreen.kt @@ -46,7 +46,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.data.model.Link @Composable -fun AboutScreen(onNavigateUp: () -> Unit) { +fun AboutScreen() { var shouldShowAboutDialog by rememberSaveable { mutableStateOf(false) } if (shouldShowAboutDialog) { @@ -54,13 +54,12 @@ fun AboutScreen(onNavigateUp: () -> Unit) { } ScreenContent( - onNavigateUp = onNavigateUp, onAboutAurora = { shouldShowAboutDialog = true } ) } @Composable -private fun ScreenContent(onNavigateUp: () -> Unit = {}, onAboutAurora: () -> Unit = {}) { +private fun ScreenContent(onAboutAurora: () -> Unit = {}) { val context = LocalContext.current val linkURLS = stringArrayResource(R.array.link_urls) @@ -94,8 +93,7 @@ private fun ScreenContent(onNavigateUp: () -> Unit = {}, onAboutAurora: () -> Un Scaffold( topBar = { TopAppBar( - title = stringResource(R.string.title_about), - onNavigateUp = onNavigateUp + title = stringResource(R.string.title_about) ) } ) { paddingValues -> diff --git a/app/src/main/java/com/aurora/store/compose/ui/accounts/AccountsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/accounts/AccountsScreen.kt index b8bb41717..1f0c7de50 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/accounts/AccountsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/accounts/AccountsScreen.kt @@ -48,19 +48,18 @@ import com.aurora.Constants.URL_TOS import com.aurora.extensions.browse import com.aurora.store.R 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.data.providers.AccountProvider import com.aurora.store.viewmodel.accounts.AccountsViewModel @Composable fun AccountsScreen( - onNavigateUp: () -> Unit, - onNavigateToSplash: () -> Unit, + onNavigateTo: (Destination) -> Unit, viewModel: AccountsViewModel = hiltViewModel() ) { val userProfile = viewModel.authProvider.authData?.userProfile val avatar = when { - viewModel.authProvider.isAnonymous -> R.mipmap.ic_launcher userProfile != null -> userProfile.artwork.url else -> R.mipmap.ic_launcher } @@ -70,7 +69,6 @@ fun AccountsScreen( else -> stringResource(R.string.account_anonymous) } val email = when { - viewModel.authProvider.isAnonymous -> stringResource(R.string.account_anonymous_email) userProfile != null -> userProfile.email else -> stringResource(R.string.account_anonymous_email) } @@ -83,14 +81,13 @@ fun AccountsScreen( onConfirm = { shouldShowLogoutDialog = false AccountProvider.logout(context) - onNavigateToSplash() + onNavigateTo(Destination.Splash) }, onDismiss = { shouldShowLogoutDialog = false } ) } ScreenContent( - onNavigateUp = onNavigateUp, avatar = avatar, name = name, email = email, @@ -100,17 +97,15 @@ fun AccountsScreen( @Composable private fun ScreenContent( - avatar: Any = R.mipmap.ic_launcher, + avatar: Any = R.drawable.ic_account, name: String = stringResource(R.string.account_anonymous), email: String = stringResource(R.string.account_anonymous_email), - onNavigateUp: () -> Unit = {}, onLogout: () -> Unit = {} ) { Scaffold( topBar = { TopAppBar( - title = stringResource(R.string.title_account_manager), - onNavigateUp = onNavigateUp + title = stringResource(R.string.title_account_manager) ) } ) { paddingValues -> @@ -118,7 +113,7 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(vertical = dimensionResource(R.dimen.padding_medium)), + .padding(vertical = dimensionResource(R.dimen.padding_small)), verticalArrangement = Arrangement.SpaceBetween ) { AssistHeader() @@ -126,7 +121,7 @@ private fun ScreenContent( Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) ) { AsyncImage( model = ImageRequest.Builder(LocalContext.current) diff --git a/app/src/main/java/com/aurora/store/compose/ui/blacklist/BlacklistScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/blacklist/BlacklistScreen.kt index f82cb0e0b..fd71e7272 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/blacklist/BlacklistScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/blacklist/BlacklistScreen.kt @@ -7,6 +7,8 @@ package com.aurora.store.compose.ui.blacklist import android.net.Uri +import androidx.activity.ComponentActivity +import androidx.activity.compose.LocalActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts 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.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.aurora.Constants @@ -72,13 +73,12 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @Composable -fun BlacklistScreen(onNavigateUp: () -> Unit, viewModel: BlacklistViewModel = hiltViewModel()) { +fun BlacklistScreen(viewModel: BlacklistViewModel = hiltViewModel()) { val context = LocalContext.current val packages by viewModel.filteredPackages.collectAsStateWithLifecycle() ScreenContent( packages = packages, - onNavigateUp = onNavigateUp, isPackageBlacklisted = { pkgName -> pkgName in viewModel.blacklist }, onBlacklistImport = { uri -> viewModel.importBlacklist(uri) @@ -99,7 +99,6 @@ fun BlacklistScreen(onNavigateUp: () -> Unit, viewModel: BlacklistViewModel = hi @Composable private fun ScreenContent( packages: List? = null, - onNavigateUp: () -> Unit = {}, isPackageBlacklisted: (packageName: String) -> Boolean = { false }, onBlacklistImport: (uri: Uri) -> Unit = {}, onBlacklistExport: (uri: Uri) -> Unit = {}, @@ -109,6 +108,7 @@ private fun ScreenContent( onWhitelistAll: () -> Unit = {}, onSearch: (query: String) -> Unit = {} ) { + val activity = LocalActivity.current as? ComponentActivity val context = LocalContext.current val textFieldState = rememberTextFieldState() val searchBarState = rememberSearchBarState() @@ -224,7 +224,7 @@ private fun ScreenContent( state = searchBarState, inputField = inputField, navigationIcon = { - IconButton(onClick = onNavigateUp) { + IconButton(onClick = { activity?.onBackPressedDispatcher?.onBackPressed() }) { Icon( painter = painterResource(R.drawable.ic_arrow_back), contentDescription = stringResource(R.string.action_back) @@ -333,7 +333,6 @@ private fun ScreenContent( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt new file mode 100644 index 000000000..036b19556 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt @@ -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() } + ) + } + } +} diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt new file mode 100644 index 000000000..cd696d810 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt @@ -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)) + } + ) + } + } + } + } + } + } + } +} diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/PermissionRationaleScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/PermissionRationaleScreen.kt index 1a27bb5c6..3aad2fc3d 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/PermissionRationaleScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/PermissionRationaleScreen.kt @@ -35,14 +35,12 @@ import kotlin.random.Random @Composable fun PermissionRationaleScreen( requiredPermissions: Set = emptySet(), - onNavigateUp: () -> Unit, onPermissionCallback: (type: PermissionType) -> Unit = {}, viewModel: PermissionRationaleViewModel = hiltViewModel() ) { val permissions by viewModel.permissions.collectAsStateWithLifecycle() ScreenContent( - onNavigateUp = onNavigateUp, permissions = permissions .filter { it.type in requiredPermissions } .map { permission -> permission.copy(optional = false) }, @@ -56,7 +54,6 @@ fun PermissionRationaleScreen( @Composable private fun ScreenContent( permissions: List = emptyList(), - onNavigateUp: () -> Unit = {}, onPermissionCallback: (type: PermissionType) -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { @@ -64,8 +61,7 @@ private fun ScreenContent( topBar = { TopAppBar( title = pluralStringResource(R.plurals.permissions_required, permissions.size), - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) } ) { paddingValues -> diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt index 34c2e6f8b..a3571e00c 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt @@ -5,12 +5,14 @@ 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.ui.Modifier +import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource 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.TopAppBar 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.ThemePreviewProvider import com.aurora.store.viewmodel.browse.StreamBrowseViewModel @@ -39,8 +42,7 @@ import kotlinx.coroutines.flow.MutableStateFlow @Composable fun StreamBrowseScreen( streamCluster: StreamCluster, - onNavigateUp: () -> Unit, - onNavigateToAppDetails: (packageName: String) -> Unit, + onNavigateTo: (Destination) -> Unit, viewModel: StreamBrowseViewModel = hiltViewModel( creationCallback = { factory: StreamBrowseViewModel.Factory -> factory.create(streamCluster) @@ -52,8 +54,7 @@ fun StreamBrowseScreen( ScreenContent( title = streamCluster.clusterTitle, apps = apps, - onNavigateUp = onNavigateUp, - onNavigateToAppDetails = onNavigateToAppDetails + onNavigateTo = onNavigateTo ) } @@ -61,12 +62,11 @@ fun StreamBrowseScreen( private fun ScreenContent( title: String = String(), apps: LazyPagingItems = emptyPagingItems(), - onNavigateToAppDetails: (packageName: String) -> Unit = {}, - onNavigateUp: () -> Unit = {} + onNavigateTo: (Destination) -> Unit = {} ) { Scaffold( topBar = { - TopAppBar(title = title, onNavigateUp = onNavigateUp) + TopAppBar(title = title) } ) { paddingValues -> when (apps.loadState.refresh) { @@ -91,7 +91,10 @@ private fun ScreenContent( LazyColumn( modifier = Modifier .fillMaxSize() - .padding(paddingValues) + .padding(paddingValues), + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.margin_medium) + ) ) { items( count = apps.itemCount, @@ -100,7 +103,9 @@ private fun ScreenContent( apps[index]?.let { app -> LargeAppListItem( app = app, - onClick = { onNavigateToAppDetails(app.packageName) } + onClick = { + onNavigateTo(Destination.AppDetails(app.packageName)) + } ) } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt index a4b9342fb..f36834149 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt @@ -50,7 +50,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.core.net.toUri import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel 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.TopAppBar 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.preview.AppPreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider @@ -110,8 +110,7 @@ import kotlinx.coroutines.launch @Composable fun AppDetailsScreen( packageName: String, - onNavigateUp: () -> Unit, - onNavigateToAppDetails: (packageName: String) -> Unit, + onNavigateTo: (Destination) -> Unit, viewModel: AppDetailsViewModel = hiltViewModel(key = packageName), forceSinglePane: Boolean = false ) { @@ -136,11 +135,10 @@ fun AppDetailsScreen( ) { currentState -> when { currentState is AppState.Loading || app == null -> - ScreenContentLoading(onNavigateUp = onNavigateUp) + ScreenContentLoading() currentState is AppState.Error -> ScreenContentError( - onNavigateUp = onNavigateUp, message = currentState.message ) @@ -156,8 +154,7 @@ fun AppDetailsScreen( plexusScores = plexusScores, dataSafetyReport = dataSafetyReport, exodusReport = exodusReport, - onNavigateUp = onNavigateUp, - onNavigateToAppDetails = onNavigateToAppDetails, + onNavigateTo = onNavigateTo, onDownload = { requestedApp -> viewModel.enqueueDownload(requestedApp) }, onFavorite = { viewModel.toggleFavourite(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 -private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) { +private fun ScreenContentLoading() { Scaffold( - topBar = { TopAppBar(onNavigateUp = onNavigateUp) } + topBar = { TopAppBar() } ) { 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 -private fun ScreenContentError(onNavigateUp: () -> Unit = {}, message: String? = null) { +private fun ScreenContentError(message: String? = null) { Scaffold( - topBar = { TopAppBar(onNavigateUp = onNavigateUp) } + topBar = { TopAppBar() } ) { paddingValues -> Error( modifier = Modifier.padding(paddingValues), @@ -234,8 +231,7 @@ private fun ScreenContentApp( plexusScores: Scores? = null, dataSafetyReport: DataSafetyReport? = null, exodusReport: Report? = null, - onNavigateUp: () -> Unit = {}, - onNavigateToAppDetails: (packageName: String) -> Unit = {}, + onNavigateTo: (Destination) -> Unit = {}, onDownload: (requestedApp: App) -> Unit = {}, onFavorite: () -> Unit = {}, onCancelDownload: () -> Unit = {}, @@ -412,7 +408,6 @@ private fun ScreenContentApp( Scaffold( topBar = { TopAppBar( - onNavigateUp = onNavigateUp, actions = { if (shouldShowMenuOnMainPane) SetupMenu() } ) } @@ -535,7 +530,6 @@ private fun ScreenContentApp( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } @@ -546,7 +540,10 @@ private fun ScreenContentApp( fun SupportingPane() { Scaffold( topBar = { - TopAppBar(actions = { if (!shouldShowMenuOnMainPane) SetupMenu() }) + TopAppBar( + showNavigationIcon = false, + actions = { if (!shouldShowMenuOnMainPane) SetupMenu() } + ) } ) { paddingValues -> if (suggestions.isNotEmpty()) { @@ -568,12 +565,15 @@ private fun ScreenContentApp( LazyColumn( modifier = Modifier .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 -> LargeAppListItem( app = app, - onClick = { onNavigateToAppDetails(app.packageName) } + onClick = { onNavigateTo(Destination.AppDetails(app.packageName)) } ) } } @@ -585,52 +585,43 @@ private fun ScreenContentApp( @Composable fun ExtraPane(screen: NavKey) = when (screen) { is ExtraScreen.Review -> ReviewScreen( - packageName = app.packageName, - onNavigateUp = ::onNavigateBack + packageName = app.packageName ) is ExtraScreen.Exodus -> ExodusScreen( - packageName = app.packageName, - onNavigateUp = ::onNavigateBack + packageName = app.packageName ) is ExtraScreen.More -> MoreScreen( packageName = app.packageName, - onNavigateUp = ::onNavigateBack, - onNavigateToAppDetails = onNavigateToAppDetails + onNavigateTo = onNavigateTo ) is ExtraScreen.Permission -> PermissionScreen( - packageName = app.packageName, - onNavigateUp = ::onNavigateBack + packageName = app.packageName ) is ExtraScreen.Screenshot -> ScreenshotScreen( packageName = app.packageName, - index = screen.index, - onNavigateUp = ::onNavigateBack + index = screen.index ) is ExtraScreen.ManualDownload -> ManualDownloadScreen( packageName = app.packageName, - onNavigateUp = ::onNavigateBack, onRequestInstall = { requestedApp -> onInstall(requestedApp) } ) is ExtraScreen.MicroG -> MicroGScreen( packageName = app.packageName, - onNavigateUp = ::onNavigateBack, onProceed = { onInstall(ignoreMicroG = true) } ) is Screen.DevProfile -> DevProfileScreen( publisherId = app.developerName, - onNavigateUp = ::onNavigateBack, - onNavigateToAppDetails = { onNavigateToAppDetails(it) } + onNavigateTo = onNavigateTo ) is Screen.PermissionRationale -> PermissionRationaleScreen( - onNavigateUp = ::onNavigateBack, requiredPermissions = screen.requiredPermissions, onPermissionCallback = { type -> val isStoragePermission = type == PermissionType.EXTERNAL_STORAGE || diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/ExodusScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/ExodusScreen.kt index f85046245..d6602fbce 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/ExodusScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/ExodusScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.aurora.Constants.EXODUS_REPORT_URL @@ -56,7 +55,6 @@ import com.aurora.store.viewmodel.details.ExodusViewModel @Composable fun ExodusScreen( packageName: String, - onNavigateUp: () -> Unit, appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), exodusViewModel: ExodusViewModel = hiltViewModel(key = "$packageName/exodus"), windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() @@ -80,7 +78,6 @@ fun ExodusScreen( null -> { ScreenContentError( topAppBarTitle = topAppBarTitle, - onNavigateUp = onNavigateUp, onRequestAnalysis = { context.browse("${EXODUS_SUBMIT_PAGE}${app!!.packageName}") } ) } @@ -90,7 +87,6 @@ fun ExodusScreen( topAppBarTitle = topAppBarTitle, report = report, trackers = trackers, - onNavigateUp = onNavigateUp, onRequestAnalysis = { context.browse("${EXODUS_SUBMIT_PAGE}${app!!.packageName}") } ) } @@ -102,7 +98,6 @@ private fun ScreenContentReport( topAppBarTitle: String? = null, report: Report? = null, trackers: List = emptyList(), - onNavigateUp: () -> Unit = {}, onRequestAnalysis: () -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { @@ -112,8 +107,7 @@ private fun ScreenContentReport( topBar = { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) }, floatingActionButton = { @@ -161,7 +155,6 @@ private fun ScreenContentReport( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } @@ -174,7 +167,6 @@ private fun ScreenContentReport( @Composable private fun ScreenContentError( topAppBarTitle: String? = null, - onNavigateUp: () -> Unit = {}, onRequestAnalysis: () -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { @@ -182,8 +174,7 @@ private fun ScreenContentError( topBar = { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) } ) { paddingValues -> diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/ManualDownloadScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/ManualDownloadScreen.kt index 81384ac1e..4ebd59b64 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/ManualDownloadScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/ManualDownloadScreen.kt @@ -6,6 +6,8 @@ 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.Column import androidx.compose.foundation.layout.Row @@ -69,7 +71,6 @@ import kotlinx.coroutines.launch @Composable fun ManualDownloadScreen( packageName: String, - onNavigateUp: () -> Unit, onRequestInstall: (requestedApp: App) -> Unit, viewModel: AppDetailsViewModel = hiltViewModel(key = packageName), windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() @@ -85,7 +86,6 @@ fun ManualDownloadScreen( state = state, topAppBarTitle = topAppBarTitle, currentVersionCode = app!!.versionCode, - onNavigateUp = onNavigateUp, onRequestInstall = { versionCode -> val requestedApp = app!!.copy( versionCode = versionCode, @@ -105,10 +105,10 @@ private fun ScreenContent( state: AppState = AppState.Unavailable, topAppBarTitle: String? = null, currentVersionCode: Long = 0L, - onNavigateUp: () -> Unit = {}, onRequestInstall: (versionCode: Long) -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { + val activity = LocalActivity.current as? ComponentActivity val coroutineScope = rememberCoroutineScope() val snackBarHostState = remember { SnackbarHostState() } val errorMessage = stringResource(R.string.manual_download_version_error) @@ -130,8 +130,7 @@ private fun ScreenContent( topBar = { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) }, snackbarHost = { SnackbarHost(hostState = snackBarHostState) } @@ -187,7 +186,7 @@ private fun ScreenContent( ) { FilledTonalButton( modifier = Modifier.weight(1F), - onClick = onNavigateUp + onClick = { activity?.onBackPressedDispatcher?.onBackPressed() } ) { Text( text = stringResource(R.string.action_close), diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt index 145c6cda9..bb5c7010e 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt @@ -6,6 +6,8 @@ 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.Box import androidx.compose.foundation.layout.Row @@ -55,7 +57,6 @@ import com.aurora.store.viewmodel.onboarding.MicroGViewModel @Composable fun MicroGScreen( packageName: String, - onNavigateUp: () -> Unit, onProceed: () -> Unit, appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), viewModel: MicroGViewModel = hiltViewModel(), @@ -70,7 +71,6 @@ fun MicroGScreen( ScreenContent( topAppBarTitle = topAppBarTitle, uiState = viewModel.uiState, - onNavigateUp = onNavigateUp, onInstall = { viewModel.downloadMicroG() }, onRetry = { viewModel.retryDownload() }, onProceed = onProceed @@ -81,12 +81,12 @@ fun MicroGScreen( private fun ScreenContent( topAppBarTitle: String? = null, uiState: MicroGUIState = MicroGUIState(), - onNavigateUp: () -> Unit = {}, onInstall: () -> Unit = {}, onRetry: () -> Unit = {}, onProceed: () -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { + val activity = LocalActivity.current as? ComponentActivity val context = LocalContext.current val listState = rememberLazyListState() @@ -94,8 +94,7 @@ private fun ScreenContent( topBar = { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) }, bottomBar = { @@ -112,7 +111,7 @@ private fun ScreenContent( ) { FilledTonalButton( modifier = Modifier.weight(1F), - onClick = onNavigateUp + onClick = { activity?.onBackPressedDispatcher?.onBackPressed() } ) { Text( text = stringResource(R.string.action_cancel), @@ -174,7 +173,6 @@ private fun ScreenContent( ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/MoreScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/MoreScreen.kt index f9a66ccaa..5212c40db 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/MoreScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/MoreScreen.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.text.fromHtml import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle 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.TopAppBar 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.ThemePreviewProvider import com.aurora.store.viewmodel.details.AppDetailsViewModel @@ -52,8 +52,7 @@ import com.aurora.store.viewmodel.details.MoreViewModel @Composable fun MoreScreen( packageName: String, - onNavigateUp: () -> Unit, - onNavigateToAppDetails: (packageName: String) -> Unit, + onNavigateTo: (Destination) -> Unit, appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), moreViewModel: MoreViewModel = hiltViewModel( key = "$packageName/more", @@ -68,8 +67,7 @@ fun MoreScreen( ScreenContent( app = app!!, dependencies = dependencies, - onNavigateUp = onNavigateUp, - onNavigateToAppDetails = onNavigateToAppDetails + onNavigateTo = onNavigateTo ) } @@ -77,8 +75,7 @@ fun MoreScreen( private fun ScreenContent( app: App, dependencies: List? = null, - onNavigateUp: () -> Unit = {}, - onNavigateToAppDetails: (packageName: String) -> Unit = {}, + onNavigateTo: (Destination) -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { val topAppBarTitle = when { @@ -90,8 +87,7 @@ private fun ScreenContent( topBar = { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) } ) { paddingValues -> @@ -126,7 +122,7 @@ private fun ScreenContent( if (dependencies != null) { AppDependencies( dependencies = dependencies, - onNavigateToAppDetails = onNavigateToAppDetails + onNavigateTo = onNavigateTo ) } } @@ -137,7 +133,6 @@ private fun ScreenContent( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } @@ -148,10 +143,7 @@ private fun ScreenContent( * Composable to show dependencies of an app */ @Composable -private fun AppDependencies( - dependencies: List, - onNavigateToAppDetails: (packageName: String) -> Unit -) { +private fun AppDependencies(dependencies: List, onNavigateTo: (Destination) -> Unit) { Header(title = stringResource(R.string.details_dependencies)) if (dependencies.isEmpty()) { Info( @@ -162,7 +154,7 @@ private fun AppDependencies( items(items = dependencies, key = { item -> item.id }) { app -> AppListItem( app = app, - onClick = { onNavigateToAppDetails(app.packageName) } + onClick = { onNavigateTo(Destination.AppDetails(app.packageName)) } ) } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/PermissionScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/PermissionScreen.kt index d3d52194f..1fc1e911d 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/PermissionScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/PermissionScreen.kt @@ -29,7 +29,6 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.aurora.extensions.adaptiveNavigationIcon @@ -47,7 +46,6 @@ import com.aurora.store.viewmodel.details.PermissionViewModel @Composable fun PermissionScreen( packageName: String, - onNavigateUp: () -> Unit, appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), permissionViewModel: PermissionViewModel = hiltViewModel( key = "$packageName/permission", @@ -67,7 +65,6 @@ fun PermissionScreen( ScreenContent( topAppBarTitle = topAppBarTitle, - onNavigateUp = onNavigateUp, permissionsInfo = permissionsInfo ) } @@ -76,7 +73,6 @@ fun PermissionScreen( private fun ScreenContent( topAppBarTitle: String? = null, permissionsInfo: Map = emptyMap(), - onNavigateUp: () -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { val packageManager = LocalContext.current.packageManager @@ -85,8 +81,7 @@ private fun ScreenContent( topBar = { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) } ) { paddingValues -> @@ -127,7 +122,6 @@ private fun ScreenContent( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt index 79b295803..f0fd4b088 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt @@ -36,7 +36,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.paging.LoadState @@ -64,7 +63,6 @@ import kotlinx.coroutines.flow.MutableStateFlow @Composable fun ReviewScreen( packageName: String, - onNavigateUp: () -> Unit, appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), reviewViewModel: ReviewViewModel = hiltViewModel( key = "$packageName/review", @@ -85,7 +83,6 @@ fun ReviewScreen( ScreenContent( topAppBarTitle = topAppBarTitle, reviews = reviews, - onNavigateUp = onNavigateUp, onFilter = { filter -> reviewViewModel.fetchReviews(filter) } ) } @@ -93,7 +90,6 @@ fun ReviewScreen( @Composable private fun ScreenContent( topAppBarTitle: String? = null, - onNavigateUp: () -> Unit = {}, reviews: LazyPagingItems = emptyPagingItems(), onFilter: (filter: Review.Filter) -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() @@ -103,8 +99,7 @@ private fun ScreenContent( Column { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) FilterHeader { filter -> onFilter(filter) } } @@ -142,7 +137,6 @@ private fun ScreenContent( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/ScreenshotScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/ScreenshotScreen.kt index ab808a78e..444b76e94 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/ScreenshotScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/ScreenshotScreen.kt @@ -17,7 +17,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue 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.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper @@ -36,7 +36,6 @@ import com.aurora.store.viewmodel.details.AppDetailsViewModel fun ScreenshotScreen( packageName: String, index: Int, - onNavigateUp: () -> Unit, viewModel: AppDetailsViewModel = hiltViewModel(key = packageName), windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { @@ -49,7 +48,6 @@ fun ScreenshotScreen( ScreenContent( topAppBarTitle = topAppBarTitle, - onNavigateUp = onNavigateUp, screenshots = app!!.screenshots, index = index ) @@ -58,12 +56,11 @@ fun ScreenshotScreen( @Composable private fun ScreenContent( topAppBarTitle: String? = null, - onNavigateUp: () -> Unit = {}, screenshots: List = emptyList(), index: Int = 0, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { - val displayMetrics = LocalContext.current.resources.displayMetrics + val displayMetrics = LocalResources.current.displayMetrics val pagerState = rememberPagerState(initialPage = index) { screenshots.size } LaunchedEffect(key1 = index) { @@ -74,8 +71,7 @@ private fun ScreenContent( topBar = { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) } ) { paddingValues -> diff --git a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt index 054f55c8f..aa2b3ac2d 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt @@ -6,6 +6,7 @@ package com.aurora.store.compose.ui.dev +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding 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.TopAppBar 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.ThemePreviewProvider import com.aurora.store.viewmodel.details.DevProfileViewModel @@ -48,8 +50,7 @@ import kotlinx.coroutines.flow.MutableStateFlow @Composable fun DevProfileScreen( developerId: String, - onNavigateUp: () -> Unit, - onNavigateToAppDetails: (packageName: String) -> Unit, + onNavigateTo: (Destination) -> Unit, viewModel: DevProfileViewModel = hiltViewModel() ) { // TODO: Implement when migrating logic for current DevProfileFragment @@ -62,8 +63,7 @@ fun DevProfileScreen( @Composable fun DevProfileScreen( publisherId: String, - onNavigateUp: () -> Unit, - onNavigateToAppDetails: (packageName: String) -> Unit, + onNavigateTo: (Destination) -> Unit, viewModel: SearchViewModel = hiltViewModel() ) { val apps = viewModel.apps.collectAsLazyPagingItems() @@ -73,8 +73,7 @@ fun DevProfileScreen( ScreenContent( apps = apps, topAppBarTitle = publisherId, - onNavigateUp = onNavigateUp, - onNavigateToAppDetails = onNavigateToAppDetails + onNavigateTo = onNavigateTo ) } @@ -82,16 +81,14 @@ fun DevProfileScreen( private fun ScreenContent( apps: LazyPagingItems, topAppBarTitle: String, - onNavigateUp: () -> Unit = {}, - onNavigateToAppDetails: (packageName: String) -> Unit = {}, + onNavigateTo: (Destination) -> Unit = {}, windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfoV2() ) { Scaffold( topBar = { TopAppBar( title = topAppBarTitle, - navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, - onNavigateUp = onNavigateUp + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon ) } ) { paddingValues -> @@ -111,13 +108,16 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .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 -> apps[index]?.let { app -> LargeAppListItem( app = app, - onClick = { onNavigateToAppDetails(app.packageName) } + onClick = { onNavigateTo(Destination.AppDetails(app.packageName)) } ) } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/dispenser/DispenserScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/dispenser/DispenserScreen.kt index 25645ad44..e5121bc7c 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/dispenser/DispenserScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/dispenser/DispenserScreen.kt @@ -35,7 +35,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.viewmodel.dispenser.DispenserViewModel @Composable -fun DispenserScreen(onNavigateUp: () -> Unit, viewModel: DispenserViewModel = hiltViewModel()) { +fun DispenserScreen(viewModel: DispenserViewModel = hiltViewModel()) { val dispensers by viewModel.dispensers.collectAsStateWithLifecycle() var shouldShowInputDialog by rememberSaveable { mutableStateOf(false) } @@ -63,7 +63,6 @@ fun DispenserScreen(onNavigateUp: () -> Unit, viewModel: DispenserViewModel = hi ScreenContent( dispensers = dispensers, - onNavigateUp = onNavigateUp, onAddDispenser = { shouldShowInputDialog = true }, onRemoveDispenser = { url -> shouldRemoveDispenser = url } ) @@ -71,7 +70,6 @@ fun DispenserScreen(onNavigateUp: () -> Unit, viewModel: DispenserViewModel = hi @Composable private fun ScreenContent( - onNavigateUp: () -> Unit = {}, dispensers: Set = emptySet(), onAddDispenser: () -> Unit = {}, onRemoveDispenser: (url: String) -> Unit = {} @@ -81,8 +79,7 @@ private fun ScreenContent( Scaffold( topBar = { TopAppBar( - title = stringResource(R.string.pref_dispenser_title), - onNavigateUp = onNavigateUp + title = stringResource(R.string.pref_dispenser_title) ) }, floatingActionButton = { diff --git a/app/src/main/java/com/aurora/store/compose/ui/downloads/DownloadsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/downloads/DownloadsScreen.kt index 068c5fe09..500059878 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/downloads/DownloadsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/downloads/DownloadsScreen.kt @@ -29,7 +29,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.paging.LoadState 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.ScrollHint 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.ThemePreviewProvider import com.aurora.store.compose.ui.downloads.menu.DownloadsMenu @@ -57,9 +57,8 @@ import kotlinx.coroutines.flow.MutableStateFlow @Composable fun DownloadsScreen( - onNavigateUp: () -> Unit, - viewModel: DownloadsViewModel = hiltViewModel(), - onNavigateToAppDetails: (packageName: String) -> Unit + onNavigateTo: (Destination) -> Unit, + viewModel: DownloadsViewModel = hiltViewModel() ) { val context = LocalContext.current val downloads = viewModel.downloads.collectAsLazyPagingItems() @@ -79,9 +78,8 @@ fun DownloadsScreen( ) ScreenContent( - onNavigateUp = onNavigateUp, downloads = downloads, - onNavigateToAppDetails = onNavigateToAppDetails, + onNavigateTo = onNavigateTo, onCancelAll = { viewModel.cancelAll() }, onForceClearAll = { viewModel.clearAll() }, onClearFinished = { viewModel.clearFinished() }, @@ -100,8 +98,7 @@ fun DownloadsScreen( @Composable private fun ScreenContent( downloads: LazyPagingItems = emptyPagingItems(), - onNavigateUp: () -> Unit = {}, - onNavigateToAppDetails: (packageName: String) -> Unit = {}, + onNavigateTo: (Destination) -> Unit = {}, onCancel: (packageName: String) -> Unit = {}, onClear: (download: Download) -> Unit = {}, onExport: (download: Download) -> Unit = {}, @@ -133,7 +130,6 @@ private fun ScreenContent( topBar = { TopAppBar( title = stringResource(R.string.title_download_manager), - onNavigateUp = onNavigateUp, actions = { if (downloads.itemCount != 0) SetupMenu() } ) } @@ -173,7 +169,9 @@ private fun ScreenContent( modifier = Modifier.animateItem(), download = download, onClick = { - onNavigateToAppDetails(download.packageName) + onNavigateTo( + Destination.AppDetails(download.packageName) + ) }, onClear = { onClear(download) }, onCancel = { onCancel(download.packageName) }, @@ -185,7 +183,6 @@ private fun ScreenContent( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/favourite/FavouriteScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/favourite/FavouriteScreen.kt index 75306c346..546693ed8 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/favourite/FavouriteScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/favourite/FavouriteScreen.kt @@ -29,7 +29,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.paging.LoadState 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.ScrollHint 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.ThemePreviewProvider import com.aurora.store.compose.ui.favourite.menu.FavouriteMenu @@ -57,8 +57,7 @@ import kotlinx.coroutines.flow.MutableStateFlow @Composable fun FavouriteScreen( - onNavigateUp: () -> Unit, - onNavigateToAppDetails: (packageName: String) -> Unit, + onNavigateTo: (Destination) -> Unit, viewModel: FavouriteViewModel = hiltViewModel() ) { val context = LocalContext.current @@ -90,8 +89,7 @@ fun FavouriteScreen( ScreenContent( favourites = favourites, - onNavigateUp = onNavigateUp, - onNavigateToAppDetails = onNavigateToAppDetails, + onNavigateTo = onNavigateTo, onRemoveFavourite = { packageName -> viewModel.removeFavourite(packageName) }, onImportFavourites = { documentOpenLauncher.launch(arrayOf(JSON_MIME_TYPE)) @@ -107,8 +105,7 @@ fun FavouriteScreen( @Composable private fun ScreenContent( favourites: LazyPagingItems = emptyPagingItems(), - onNavigateUp: () -> Unit = {}, - onNavigateToAppDetails: (packageName: String) -> Unit = {}, + onNavigateTo: (Destination) -> Unit = {}, onRemoveFavourite: (packageName: String) -> Unit = {}, onImportFavourites: () -> Unit = {}, onExportFavourites: () -> Unit = {} @@ -135,7 +132,6 @@ private fun ScreenContent( topBar = { TopAppBar( title = stringResource(R.string.title_favourites_manager), - onNavigateUp = onNavigateUp, actions = { SetupMenu() } ) } @@ -175,7 +171,9 @@ private fun ScreenContent( modifier = Modifier.animateItem(), favourite = favourite, onClick = { - onNavigateToAppDetails(favourite.packageName) + onNavigateTo( + Destination.AppDetails(favourite.packageName) + ) }, onClear = { onRemoveFavourite(favourite.packageName) } ) @@ -184,7 +182,6 @@ private fun ScreenContent( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/installed/InstalledScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/installed/InstalledScreen.kt index 84e8396e3..ed7d9f6f3 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/installed/InstalledScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/installed/InstalledScreen.kt @@ -6,6 +6,7 @@ package com.aurora.store.compose.ui.installed +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -20,12 +21,12 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment 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.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.paging.LoadState 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.TopAppBar 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.ThemePreviewProvider import com.aurora.store.viewmodel.all.InstalledViewModel @@ -48,24 +50,21 @@ import kotlinx.coroutines.flow.MutableStateFlow @Composable fun InstalledScreen( - onNavigateUp: () -> Unit, - onNavigateToAppDetails: (packageName: String) -> Unit, + onNavigateTo: (Destination) -> Unit, viewModel: InstalledViewModel = hiltViewModel() ) { val apps = viewModel.apps.collectAsLazyPagingItems() ScreenContent( apps = apps, - onNavigateUp = onNavigateUp, - onNavigateToAppDetails = onNavigateToAppDetails + onNavigateTo = onNavigateTo ) } @Composable private fun ScreenContent( - onNavigateUp: () -> Unit = {}, apps: LazyPagingItems = emptyPagingItems(), - onNavigateToAppDetails: (packageName: String) -> Unit = {} + onNavigateTo: (Destination) -> Unit = {} ) { /* * For some reason paging3 frequently out-of-nowhere invalidates the list which causes @@ -78,8 +77,7 @@ private fun ScreenContent( Scaffold( topBar = { TopAppBar( - title = stringResource(R.string.title_apps_games), - onNavigateUp = onNavigateUp + title = stringResource(R.string.title_apps_games) ) } ) { paddingValues -> @@ -106,7 +104,10 @@ private fun ScreenContent( val listState = rememberLazyListState() Box(modifier = Modifier.fillMaxSize()) { LazyColumn( - state = listState + state = listState, + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.margin_medium) + ) ) { items( count = apps.itemCount, @@ -115,14 +116,17 @@ private fun ScreenContent( apps[index]?.let { app -> LargeAppListItem( app = app, - onClick = { onNavigateToAppDetails(app.packageName) } + onClick = { + onNavigateTo( + Destination.AppDetails(app.packageName) + ) + } ) } } } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt index b0ff6d245..9b763682b 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt @@ -23,7 +23,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import com.aurora.extensions.toast import com.aurora.store.R @@ -105,7 +104,6 @@ private fun ScreenContent( ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt index b20cf151b..1a443f1ad 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt @@ -6,8 +6,9 @@ package com.aurora.store.compose.ui.search +import androidx.activity.ComponentActivity +import androidx.activity.compose.LocalActivity import androidx.annotation.StringRes -import androidx.compose.foundation.focusable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction 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.PreviewScreenSizes import androidx.compose.ui.tooling.preview.PreviewWrapper -import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle 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.SearchSuggestionListItem 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.ThemePreviewProvider import com.aurora.store.compose.ui.details.AppDetailsScreen @@ -91,7 +92,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @Composable -fun SearchScreen(onNavigateUp: () -> Unit, viewModel: SearchViewModel = hiltViewModel()) { +fun SearchScreen(viewModel: SearchViewModel = hiltViewModel()) { val suggestions by viewModel.suggestions.collectAsStateWithLifecycle() val results = viewModel.apps.collectAsLazyPagingItems() @@ -102,7 +103,6 @@ fun SearchScreen(onNavigateUp: () -> Unit, viewModel: SearchViewModel = hiltView ScreenContent( suggestions = suggestions, results = results, - onNavigateUp = onNavigateUp, onSearch = onSearchCallback, onFetchSuggestions = onFetchSuggestionsCallback, onFilter = { filter -> viewModel.filterResults(filter) }, @@ -114,12 +114,12 @@ fun SearchScreen(onNavigateUp: () -> Unit, viewModel: SearchViewModel = hiltView private fun ScreenContent( suggestions: List = emptyList(), results: LazyPagingItems = emptyPagingItems(), - onNavigateUp: () -> Unit = {}, onFetchSuggestions: (String) -> Unit = {}, onSearch: (String) -> Unit = {}, onFilter: (filter: SearchFilter) -> Unit = {}, isAnonymous: Boolean = true ) { + val activity = LocalActivity.current as? ComponentActivity val textFieldState = rememberTextFieldState() val searchBarState = rememberSearchBarState() var isSearching by rememberSaveable { mutableStateOf(false) } @@ -186,7 +186,7 @@ private fun ScreenContent( ) }, leadingIcon = { - IconButton(onClick = onNavigateUp) { + IconButton(onClick = { activity?.onBackPressedDispatcher?.onBackPressed() }) { Icon( painter = painterResource(R.drawable.ic_arrow_back), contentDescription = stringResource(R.string.action_back) @@ -231,10 +231,7 @@ private fun ScreenContent( @Composable fun ListPane() { - // TODO: https://issuetracker.google.com/issues/445720462 Scaffold( - modifier = Modifier - .focusable(), topBar = { SearchBar() } ) { paddingValues -> Column( @@ -272,7 +269,10 @@ private fun ScreenContent( Box(modifier = Modifier.fillMaxSize()) { LazyColumn( state = listState, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.margin_medium) + ) ) { items( count = results.itemCount, @@ -288,7 +288,6 @@ private fun ScreenContent( } ScrollHint( listState = listState, - bottomPadding = 5.dp, modifier = Modifier.align(Alignment.BottomCenter) ) } @@ -306,9 +305,10 @@ private fun ScreenContent( this != null -> { AppDetailsScreen( packageName = this, - onNavigateToAppDetails = { packageName -> showDetailPane(packageName) }, - onNavigateUp = { - coroutineScope.launch { scaffoldNavigator.navigateBack() } + onNavigateTo = { destination -> + if (destination is Destination.AppDetails) { + showDetailPane(destination.packageName) + } }, forceSinglePane = true ) diff --git a/app/src/main/java/com/aurora/store/compose/ui/spoof/SpoofScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/spoof/SpoofScreen.kt index 4c2f1e392..f43b42fa2 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/spoof/SpoofScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/spoof/SpoofScreen.kt @@ -36,6 +36,7 @@ import com.aurora.Constants import com.aurora.extensions.toast import com.aurora.store.R 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.SpoofMenu import com.aurora.store.compose.ui.spoof.navigation.SpoofPage @@ -44,14 +45,9 @@ import com.aurora.store.viewmodel.spoof.SpoofViewModel import kotlinx.coroutines.launch @Composable -fun SpoofScreen( - onNavigateUp: () -> Unit, - onNavigateToSplash: () -> Unit, - viewModel: SpoofViewModel = hiltViewModel() -) { +fun SpoofScreen(onNavigateTo: (Destination) -> Unit, viewModel: SpoofViewModel = hiltViewModel()) { ScreenContent( - onNavigateUp = onNavigateUp, - onNavigateToSplash = onNavigateToSplash, + onNavigateTo = onNavigateTo, onDeviceSpoofImport = { uri -> viewModel.importDeviceSpoof(uri) }, onDeviceSpoofExport = { uri -> viewModel.exportDeviceSpoof(uri) } ) @@ -60,8 +56,7 @@ fun SpoofScreen( @Composable private fun ScreenContent( pages: List = listOf(SpoofPage.DEVICE, SpoofPage.LOCALE), - onNavigateUp: () -> Unit = {}, - onNavigateToSplash: () -> Unit = {}, + onNavigateTo: (Destination) -> Unit = {}, onDeviceSpoofImport: (uri: Uri) -> Unit = {}, onDeviceSpoofExport: (uri: Uri) -> Unit = {} ) { @@ -101,7 +96,7 @@ private fun ScreenContent( when (result) { SnackbarResult.ActionPerformed -> { AccountProvider.logout(context) - onNavigateToSplash() + onNavigateTo(Destination.Splash) } else -> Unit @@ -133,7 +128,6 @@ private fun ScreenContent( topBar = { TopAppBar( title = stringResource(R.string.title_spoof_manager), - onNavigateUp = onNavigateUp, actions = { SetupMenu() } ) } diff --git a/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt b/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt index 9226be283..7c169b34d 100644 --- a/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt +++ b/app/src/main/java/com/aurora/store/data/providers/PermissionProvider.kt @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2026 Aurora OSS + * SPDX-License-Identifier: GPL-3.0-or-later + */ + package com.aurora.store.data.providers import android.Manifest diff --git a/app/src/main/java/com/aurora/store/util/NotificationUtil.kt b/app/src/main/java/com/aurora/store/util/NotificationUtil.kt index da2e95e64..99a6b9634 100644 --- a/app/src/main/java/com/aurora/store/util/NotificationUtil.kt +++ b/app/src/main/java/com/aurora/store/util/NotificationUtil.kt @@ -10,14 +10,13 @@ import android.graphics.Bitmap import android.graphics.Color import android.net.Uri import android.os.Build -import android.os.Bundle import androidx.core.app.NotificationCompat import androidx.core.app.PendingIntentCompat import androidx.core.content.getSystemService -import androidx.navigation.NavDeepLinkBuilder import com.aurora.Constants -import com.aurora.store.MainActivity +import com.aurora.store.ComposeActivity import com.aurora.store.R +import com.aurora.store.compose.navigation.Screen import com.aurora.store.data.activity.InstallActivity import com.aurora.store.data.helper.DownloadHelper import com.aurora.store.data.installer.AppInstaller @@ -213,16 +212,7 @@ object NotificationUtil { .build() fun getUpdateNotification(context: Context, updatesList: List): Notification { - val arguments = Bundle().apply { - 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() + val contentIntent = getContentIntentForMain(context, initialTab = 2) return NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_UPDATES) .setSmallIcon(R.drawable.ic_updates) @@ -297,7 +287,7 @@ object NotificationUtil { .setContentTitle(context.getString(R.string.authentication_required_title)) .setContentText(context.getString(R.string.authentication_required_unarchive)) .setAutoCancel(true) - .setContentIntent(getContentIntentForSplash(context, packageName)) + .setContentIntent(getContentIntentForDetails(context, packageName)) .setCategory(NotificationCompat.CATEGORY_ERROR) .setPriority(NotificationCompat.PRIORITY_HIGH) .build() @@ -359,28 +349,32 @@ object NotificationUtil { ) } - private fun getContentIntentForSplash(context: Context, packageName: String): PendingIntent { - val arguments = Bundle().apply { - putString("packageName", packageName) + private fun getContentIntentForDetails(context: Context, packageName: String): PendingIntent? { + val intent = Intent(context, ComposeActivity::class.java).apply { + 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) - .setGraph(R.navigation.mobile_navigation) - .setDestination(R.id.splashFragment) - .setComponentName(MainActivity::class.java) - .setArguments(arguments) - .createPendingIntent() + return PendingIntentCompat.getActivity( + context, + packageName.hashCode().absoluteValue, + intent, + PendingIntent.FLAG_UPDATE_CURRENT, + false + ) } - private fun getContentIntentForDetails(context: Context, packageName: String): PendingIntent { - val arguments = Bundle().apply { - putString("packageName", packageName) + private fun getContentIntentForMain(context: Context, initialTab: Int): PendingIntent? { + val intent = Intent(context, ComposeActivity::class.java).apply { + 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) - .setGraph(R.navigation.mobile_navigation) - .setDestination(R.id.splashFragment) - .setComponentName(MainActivity::class.java) - .setArguments(arguments) - .createPendingIntent() + return PendingIntentCompat.getActivity( + context, + initialTab, + intent, + PendingIntent.FLAG_UPDATE_CURRENT, + false + ) } private fun getInstallIntent(context: Context, download: Download): PendingIntent? { diff --git a/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt index 20a06c9b3..ed8ec0541 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt @@ -34,6 +34,7 @@ import com.aurora.store.data.model.AccountType import com.aurora.store.data.model.AuthState import com.aurora.store.data.providers.AccountProvider 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.PackageUtil import com.aurora.store.util.Preferences @@ -50,6 +51,7 @@ import kotlinx.coroutines.launch @HiltViewModel class AuthViewModel @Inject constructor( val authProvider: AuthProvider, + val networkProvider: NetworkProvider, @ApplicationContext private val context: Context, private val aC2DMTask: AC2DMTask ) : ViewModel() { @@ -117,6 +119,8 @@ class AuthViewModel @Inject constructor( } } + fun retry() = updateAuthState() + private fun updateAuthState() { if (_authState.value != AuthState.Fetching) { if (AccountProvider.isLoggedIn(context)) { diff --git a/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt index 9679aa0e0..9c503fc0b 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt @@ -1,85 +1,90 @@ /* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * + * SPDX-FileCopyrightText: 2024 Aurora OSS + * SPDX-FileCopyrightText: 2021 Rahul Kumar Patel + * SPDX-License-Identifier: GPL-3.0-or-later */ package com.aurora.store.viewmodel.browse import android.util.Log -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn 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.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 javax.inject.Inject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach -@HiltViewModel -class ExpandedStreamBrowseViewModel @Inject constructor( +@HiltViewModel(assistedFactory = ExpandedStreamBrowseViewModel.Factory::class) +class ExpandedStreamBrowseViewModel @AssistedInject constructor( + @Assisted val browseUrl: String, private val streamHelper: ExpandedBrowseHelper ) : ViewModel() { - val liveData: MutableLiveData = MutableLiveData() - var streamCluster: StreamCluster = StreamCluster() - - fun getInitialCluster(expandedStreamUrl: String) { - viewModelScope.launch(Dispatchers.IO) { - supervisorScope { - try { - val browseResponse = streamHelper.getBrowseStreamResponse(expandedStreamUrl) - if (browseResponse.hasBrowseTab()) { - streamCluster = - streamHelper.getExpandedBrowseClusters(browseResponse.browseTab.listUrl) - liveData.postValue(streamCluster) - } else { - } - } catch (_: Exception) { - } - } - } + @AssistedFactory + interface Factory { + fun create(browseUrl: String): ExpandedStreamBrowseViewModel } - fun next() { - Log.e(TAG, "NEXT CALED") - viewModelScope.launch(Dispatchers.IO) { - supervisorScope { - try { - val newCluster = streamHelper.getExpandedBrowseClusters( - streamCluster.clusterNextPageUrl - ) + private val _title = MutableStateFlow("") + val title = _title.asStateFlow() - streamCluster = streamCluster.copy( - clusterAppList = streamCluster.clusterAppList + newCluster.clusterAppList, - clusterNextPageUrl = newCluster.clusterNextPageUrl - ) + private val _apps = MutableStateFlow>(PagingData.empty()) + val apps = _apps.asStateFlow() - liveData.postValue(streamCluster) + init { + fetchApps() + } - if (!streamCluster.hasNext()) { - Log.i(TAG, "End of Bundle") + private fun fetchApps() { + var listUrl = "" + var nextPageUrl = "" + + manualPager { page -> + val items = try { + when (page) { + 1 -> { + val browseResponse = streamHelper.getBrowseStreamResponse(browseUrl) + if (browseResponse.hasBrowseTab()) { + listUrl = browseResponse.browseTab.listUrl + val cluster = streamHelper.getExpandedBrowseClusters(listUrl) + _title.value = cluster.clusterTitle + nextPageUrl = cluster.clusterNextPageUrl + cluster.clusterAppList + } else { + emptyList() + } + } + + else -> { + if (nextPageUrl.isNotBlank()) { + val cluster = streamHelper.getExpandedBrowseClusters(nextPageUrl) + nextPageUrl = cluster.clusterNextPageUrl + cluster.clusterAppList + } else { + emptyList() + } } - } catch (exception: Exception) { - Log.e(TAG, "Failed to fetch next stream", exception) } + } catch (exception: 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) } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/subcategory/CategoryStreamViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/subcategory/CategoryStreamViewModel.kt index 5976baf93..05cba8221 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/subcategory/CategoryStreamViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/subcategory/CategoryStreamViewModel.kt @@ -1,26 +1,12 @@ /* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * + * SPDX-FileCopyrightText: 2024 Aurora OSS + * SPDX-FileCopyrightText: 2021 Rahul Kumar Patel + * SPDX-License-Identifier: GPL-3.0-or-later */ package com.aurora.store.viewmodel.subcategory import android.util.Log -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope 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.web.WebCategoryStreamHelper 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 javax.inject.Inject 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.supervisorScope -@HiltViewModel -class CategoryStreamViewModel @Inject constructor( +@HiltViewModel(assistedFactory = CategoryStreamViewModel.Factory::class) +class CategoryStreamViewModel @AssistedInject constructor( + @Assisted val browseUrl: String, private val webCategoryStreamHelper: WebCategoryStreamHelper ) : ViewModel() { - val liveData: MutableLiveData = MutableLiveData() + @AssistedFactory + interface Factory { + fun create(browseUrl: String): CategoryStreamViewModel + } - private var stash: MutableMap = 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( + replay = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + val viewState: SharedFlow = _viewState.asSharedFlow() private val categoryStreamContract: CategoryStreamContract get() = webCategoryStreamHelper - fun getStreamBundle(browseUrl: String) { - liveData.postValue(ViewState.Loading) - observe(browseUrl) + private var streamBundle = StreamBundle() + + init { + fetchNextPage() } - fun observe(browseUrl: String) { - liveData.postValue(ViewState.Loading) + fun fetchNextPage() { viewModelScope.launch(Dispatchers.IO) { supervisorScope { - val bundle = targetBundle(browseUrl) - if (bundle.streamClusters.isNotEmpty()) { - liveData.postValue(ViewState.Success(stash)) + if (streamBundle.streamClusters.isNotEmpty()) { + _viewState.tryEmit(ViewState.Success(streamBundle)) } try { - if (!bundle.hasCluster() || bundle.hasNext()) { - // Fetch new stream bundle - val newBundle = if (bundle.streamClusters.isEmpty()) { + if (!streamBundle.hasCluster() || streamBundle.hasNext()) { + val newBundle = if (streamBundle.streamClusters.isEmpty()) { categoryStreamContract.fetch(browseUrl) } else { categoryStreamContract.nextStreamBundle( StreamContract.Category.NONE, - bundle.streamNextPageUrl + streamBundle.streamNextPageUrl ) } - // Update old bundle - val mergedBundle = bundle.copy( - streamClusters = bundle.streamClusters + newBundle.streamClusters, + streamBundle = streamBundle.copy( + streamClusters = streamBundle.streamClusters + newBundle.streamClusters, streamNextPageUrl = newBundle.streamNextPageUrl ) - stash[browseUrl] = mergedBundle - // Post updated to UI - liveData.postValue(ViewState.Success(stash)) + _viewState.tryEmit(ViewState.Success(streamBundle)) } else { Log.i(TAG, "End of Bundle") } } 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) { supervisorScope { try { @@ -101,36 +99,24 @@ class CategoryStreamViewModel @Inject constructor( val newCluster = categoryStreamContract.nextStreamCluster( streamCluster.clusterNextPageUrl ) - updateCluster(browseUrl, streamCluster.id, newCluster) - liveData.postValue(ViewState.Success(stash)) + val mergedCluster = streamCluster.copy( + 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 { Log.i(TAG, "End of cluster") } } 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 - } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/topchart/TopChartViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/topchart/TopChartViewModel.kt index 4fe8f9f07..256b9fc75 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/topchart/TopChartViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/topchart/TopChartViewModel.kt @@ -19,7 +19,6 @@ package com.aurora.store.viewmodel.topchart -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope 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 javax.inject.Inject 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.supervisorScope @@ -40,7 +43,14 @@ class TopChartViewModel @Inject constructor( private var stash: TopChartStash = mutableMapOf() - val liveData: MutableLiveData = 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( + replay = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + val state: SharedFlow = _state.asSharedFlow() private val topChartsContract: TopChartsContract get() = webTopChartsHelper @@ -48,14 +58,18 @@ class TopChartViewModel @Inject constructor( fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) { viewModelScope.launch(Dispatchers.IO) { 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 { val cluster = topChartsContract.getCluster(type.value, chart.value) updateCluster(type, chart, cluster) - liveData.postValue(ViewState.Success(stash)) - } catch (_: Exception) { + _state.tryEmit(ViewState.Success(targetCluster(type, chart))) + } catch (e: Exception) { + _state.tryEmit(ViewState.Error(e.message)) } } } @@ -72,7 +86,7 @@ class TopChartViewModel @Inject constructor( updateCluster(type, chart, newCluster) - liveData.postValue(ViewState.Success(stash)) + _state.tryEmit(ViewState.Success(targetCluster(type, chart))) } } catch (_: Exception) { }