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:
@@ -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 ->
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<BlacklistAppItem>? = 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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,14 +35,12 @@ import kotlin.random.Random
|
||||
@Composable
|
||||
fun PermissionRationaleScreen(
|
||||
requiredPermissions: Set<PermissionType> = 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<Permission> = 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 ->
|
||||
|
||||
@@ -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<App> = 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))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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<ExodusTracker> = 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 ->
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<App>? = 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<App>,
|
||||
onNavigateToAppDetails: (packageName: String) -> Unit
|
||||
) {
|
||||
private fun AppDependencies(dependencies: List<App>, 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)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, PermissionInfo> = 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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<Review> = 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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<Artwork> = 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 ->
|
||||
|
||||
@@ -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<App>,
|
||||
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)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> = 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 = {
|
||||
|
||||
@@ -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<Download> = 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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<Favourite> = 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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<App> = 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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<SearchSuggestEntry> = emptyList(),
|
||||
results: LazyPagingItems<App> = 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
|
||||
)
|
||||
|
||||
@@ -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<SpoofPage> = 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() }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Update>): 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? {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -1,85 +1,90 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Aurora OSS
|
||||
* SPDX-FileCopyrightText: 2021 Rahul Kumar Patel <whyorean@gmail.com>
|
||||
* 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<StreamCluster> = MutableLiveData()
|
||||
var streamCluster: StreamCluster = StreamCluster()
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(browseUrl: String): ExpandedStreamBrowseViewModel
|
||||
}
|
||||
|
||||
fun getInitialCluster(expandedStreamUrl: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
try {
|
||||
val browseResponse = streamHelper.getBrowseStreamResponse(expandedStreamUrl)
|
||||
private val _title = MutableStateFlow("")
|
||||
val title = _title.asStateFlow()
|
||||
|
||||
private val _apps = MutableStateFlow<PagingData<App>>(PagingData.empty())
|
||||
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()) {
|
||||
streamCluster =
|
||||
streamHelper.getExpandedBrowseClusters(browseResponse.browseTab.listUrl)
|
||||
liveData.postValue(streamCluster)
|
||||
listUrl = browseResponse.browseTab.listUrl
|
||||
val cluster = streamHelper.getExpandedBrowseClusters(listUrl)
|
||||
_title.value = cluster.clusterTitle
|
||||
nextPageUrl = cluster.clusterNextPageUrl
|
||||
cluster.clusterAppList
|
||||
} else {
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun next() {
|
||||
Log.e(TAG, "NEXT CALED")
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
try {
|
||||
val newCluster = streamHelper.getExpandedBrowseClusters(
|
||||
streamCluster.clusterNextPageUrl
|
||||
)
|
||||
|
||||
streamCluster = streamCluster.copy(
|
||||
clusterAppList = streamCluster.clusterAppList + newCluster.clusterAppList,
|
||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
||||
)
|
||||
|
||||
liveData.postValue(streamCluster)
|
||||
|
||||
if (!streamCluster.hasNext()) {
|
||||
Log.i(TAG, "End of Bundle")
|
||||
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)
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "Failed to fetch apps for page $page", exception)
|
||||
emptyList()
|
||||
}
|
||||
PageResult(items)
|
||||
}.flow.distinctUntilChanged()
|
||||
.cachedIn(viewModelScope)
|
||||
.onEach { _apps.value = it }
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,12 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Aurora OSS
|
||||
* SPDX-FileCopyrightText: 2021 Rahul Kumar Patel <whyorean@gmail.com>
|
||||
* 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<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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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
|
||||
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) {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user