From 097f5a73752cd3e36e216168d6c1f925d762f983 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Mon, 28 Apr 2025 14:16:26 +0800 Subject: [PATCH] compose: Initial migration of AppDetails* logic to compose [2/*] Deprecate setting to hide similar and related apps as they will be always listed in the suggestions pane on widescreen devices Signed-off-by: Aayush Gupta --- app/build.gradle.kts | 3 + .../view/ui/onboarding/OnboardingFragment.kt | 2 - .../aurora/extensions/WindowAdaptiveInfo.kt | 22 + .../compose/composables/InfoComposable.kt | 4 +- .../composables/TopAppBarComposable.kt | 14 +- .../app/AnimatedAppIconComposable.kt | 119 +++++ .../composables/app/NoAppAltComposable.kt | 47 -- .../composables/app/NoAppComposable.kt | 1 - .../store/compose/navigation/NavGraph.kt | 49 +- .../aurora/store/compose/navigation/Screen.kt | 15 + .../compose/ui/details/AppDetailsScreen.kt | 433 ++++++++++++++---- .../compose/ui/details/DetailsExodusScreen.kt | 32 +- .../compose/ui/details/DetailsMoreScreen.kt | 155 +++++-- .../compose/ui/details/DetailsReviewScreen.kt | 35 +- .../ui/details/DetailsScreenshotScreen.kt | 28 +- .../store/compose/ui/dev/DevProfileScreen.kt | 75 +++ .../com/aurora/store/data/event/BusEvent.kt | 1 - .../java/com/aurora/store/util/Preferences.kt | 1 - .../controller/DeveloperCarouselController.kt | 54 --- .../view/epoxy/groups/DeveloperModelGroup.kt | 128 ------ .../view/epoxy/views/details/ExodusView.kt | 58 --- .../view/epoxy/views/details/FileView.kt | 47 -- .../views/details/LargeScreenshotView.kt | 52 --- .../epoxy/views/details/MiniScreenshotView.kt | 102 ----- .../view/epoxy/views/details/ReviewView.kt | 71 --- .../epoxy/views/details/ScreenshotView.kt | 102 ----- .../store/view/ui/commons/BaseFragment.kt | 9 - .../commons/ExpandedStreamBrowseFragment.kt | 28 -- .../view/ui/details/AppDetailsFragment.kt | 89 ---- .../view/ui/details/DetailsExodusFragment.kt | 98 ---- .../view/ui/details/DetailsMoreFragment.kt | 172 ------- .../view/ui/details/DetailsReviewFragment.kt | 126 ----- .../view/ui/details/DevProfileFragment.kt | 4 - .../view/ui/details/ScreenshotFragment.kt | 73 --- .../view/ui/sheets/ManualDownloadSheet.kt | 106 ----- .../viewmodel/details/AppDetailsViewModel.kt | 167 ++++--- .../details/DetailsExodusViewModel.kt | 53 ++- .../viewmodel/details/DetailsMoreViewModel.kt | 44 +- .../review/DetailsReviewViewModel.kt | 77 ++++ .../store/viewmodel/review/ReviewViewModel.kt | 72 --- .../sheets/ManualDownloadViewModel.kt | 43 -- app/src/main/res/drawable/ic_experiment.xml | 14 + app/src/main/res/drawable/ic_suggestions.xml | 14 + app/src/main/res/layout/fragment_details.xml | 99 ++-- .../main/res/layout/fragment_details_more.xml | 106 ----- .../main/res/layout/fragment_screenshot.xml | 43 -- .../layout/model_developer_carousel_group.xml | 40 -- .../main/res/layout/sheet_manual_download.xml | 140 ------ app/src/main/res/layout/view_exodus.xml | 59 --- app/src/main/res/layout/view_file.xml | 48 -- app/src/main/res/layout/view_review.xml | 81 ---- app/src/main/res/layout/view_screenshot.xml | 29 -- .../main/res/layout/view_screenshot_large.xml | 28 -- .../main/res/layout/view_screenshot_mini.xml | 29 -- .../main/res/navigation/mobile_navigation.xml | 70 +-- app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-ast/strings.xml | 1 - app/src/main/res/values-az/strings.xml | 1 - app/src/main/res/values-be/strings.xml | 1 - app/src/main/res/values-bg/strings.xml | 1 - app/src/main/res/values-ca/strings.xml | 1 - app/src/main/res/values-cs/strings.xml | 1 - app/src/main/res/values-da/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-el/strings.xml | 1 - app/src/main/res/values-eo/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-et/strings.xml | 1 - app/src/main/res/values-eu/strings.xml | 1 - app/src/main/res/values-fa/strings.xml | 1 - app/src/main/res/values-fi/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-gl/strings.xml | 1 - app/src/main/res/values-he/strings.xml | 1 - app/src/main/res/values-hi/strings.xml | 1 - app/src/main/res/values-hr/strings.xml | 1 - app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-kmr/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-lt/strings.xml | 1 - app/src/main/res/values-lv/strings.xml | 1 - app/src/main/res/values-nb-rNO/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-pa/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-pt/strings.xml | 1 - app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sc/strings.xml | 1 - app/src/main/res/values-si/strings.xml | 1 - app/src/main/res/values-sk/strings.xml | 1 - app/src/main/res/values-sl/strings.xml | 1 - app/src/main/res/values-so/strings.xml | 1 - app/src/main/res/values-sq/strings.xml | 1 - app/src/main/res/values-sr/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-ta/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values/strings.xml | 2 - app/src/main/res/xml/preferences_ui.xml | 7 - .../view/ui/onboarding/OnboardingFragment.kt | 2 - .../view/ui/onboarding/OnboardingFragment.kt | 2 - gradle/libs.versions.toml | 4 + 111 files changed, 1108 insertions(+), 2571 deletions(-) create mode 100644 app/src/main/java/com/aurora/extensions/WindowAdaptiveInfo.kt create mode 100644 app/src/main/java/com/aurora/store/compose/composables/app/AnimatedAppIconComposable.kt delete mode 100644 app/src/main/java/com/aurora/store/compose/composables/app/NoAppAltComposable.kt create mode 100644 app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/controller/DeveloperCarouselController.kt delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/groups/DeveloperModelGroup.kt delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/views/details/ExodusView.kt delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/views/details/FileView.kt delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/views/details/LargeScreenshotView.kt delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/views/details/MiniScreenshotView.kt delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/views/details/ReviewView.kt delete mode 100644 app/src/main/java/com/aurora/store/view/epoxy/views/details/ScreenshotView.kt delete mode 100644 app/src/main/java/com/aurora/store/view/ui/details/DetailsExodusFragment.kt delete mode 100644 app/src/main/java/com/aurora/store/view/ui/details/DetailsMoreFragment.kt delete mode 100644 app/src/main/java/com/aurora/store/view/ui/details/DetailsReviewFragment.kt delete mode 100644 app/src/main/java/com/aurora/store/view/ui/details/ScreenshotFragment.kt delete mode 100644 app/src/main/java/com/aurora/store/view/ui/sheets/ManualDownloadSheet.kt create mode 100644 app/src/main/java/com/aurora/store/viewmodel/review/DetailsReviewViewModel.kt delete mode 100644 app/src/main/java/com/aurora/store/viewmodel/review/ReviewViewModel.kt delete mode 100644 app/src/main/java/com/aurora/store/viewmodel/sheets/ManualDownloadViewModel.kt create mode 100644 app/src/main/res/drawable/ic_experiment.xml create mode 100644 app/src/main/res/drawable/ic_suggestions.xml delete mode 100644 app/src/main/res/layout/fragment_details_more.xml delete mode 100644 app/src/main/res/layout/fragment_screenshot.xml delete mode 100644 app/src/main/res/layout/model_developer_carousel_group.xml delete mode 100644 app/src/main/res/layout/sheet_manual_download.xml delete mode 100644 app/src/main/res/layout/view_exodus.xml delete mode 100644 app/src/main/res/layout/view_file.xml delete mode 100644 app/src/main/res/layout/view_review.xml delete mode 100644 app/src/main/res/layout/view_screenshot.xml delete mode 100644 app/src/main/res/layout/view_screenshot_large.xml delete mode 100644 app/src/main/res/layout/view_screenshot_mini.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3e882266c..05421bd9d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -194,6 +194,9 @@ dependencies { implementation(libs.androidx.work.runtime.ktx) implementation(libs.androidx.paging.runtime) + implementation(libs.androidx.adaptive.core) + implementation(libs.androidx.adaptive.navigation) + implementation(libs.androidx.adaptive.layout) implementation(libs.androidx.paging.compose) implementation(libs.androidx.navigation.compose) implementation(libs.kotlinx.serialization.json) diff --git a/app/src/huawei/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt b/app/src/huawei/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt index 2bcb5a2da..14e5d0f1d 100644 --- a/app/src/huawei/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt +++ b/app/src/huawei/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt @@ -30,7 +30,6 @@ import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID -import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR import com.aurora.store.util.Preferences.PREFERENCE_THEME_STYLE import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED @@ -57,7 +56,6 @@ class OnboardingFragment : BaseFlavouredOnboardingFragment() { save(PREFERENCE_THEME_STYLE, 0) save(PREFERENCE_DEFAULT_SELECTED_TAB, 0) save(PREFERENCE_FOR_YOU, true) - save(PREFERENCE_SIMILAR, true) /*Installer*/ save(PREFERENCE_AUTO_DELETE, true) diff --git a/app/src/main/java/com/aurora/extensions/WindowAdaptiveInfo.kt b/app/src/main/java/com/aurora/extensions/WindowAdaptiveInfo.kt new file mode 100644 index 000000000..df9e598a0 --- /dev/null +++ b/app/src/main/java/com/aurora/extensions/WindowAdaptiveInfo.kt @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2025 The Calyx Institute + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.extensions + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.window.core.layout.WindowWidthSizeClass + +/** + * Returns navigation icon for adaptive screens such as extra pane + */ +val WindowAdaptiveInfo.adaptiveNavigationIcon: ImageVector + get() = when (windowSizeClass.windowWidthSizeClass) { + WindowWidthSizeClass.COMPACT -> Icons.AutoMirrored.Filled.ArrowBack + else -> Icons.Default.Close + } diff --git a/app/src/main/java/com/aurora/store/compose/composables/InfoComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/InfoComposable.kt index fe33d13a0..0821b7688 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/InfoComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/InfoComposable.kt @@ -32,6 +32,7 @@ import com.aurora.store.compose.composables.preview.AppPreviewProvider /** * Composable to show some information + * @param modifier Modifier to change the composable * @param title Title of the information * @param description Information to show * @param icon Optional icon representing the information @@ -39,13 +40,14 @@ import com.aurora.store.compose.composables.preview.AppPreviewProvider */ @Composable fun InfoComposable( + modifier: Modifier = Modifier, title: AnnotatedString, description: AnnotatedString? = null, @DrawableRes icon: Int? = null, onClick: (() -> Unit)? = null ) { Row( - modifier = Modifier + modifier = modifier .fillMaxWidth() .clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null) .padding( diff --git a/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt index 927dc3da8..234c45ec9 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/TopAppBarComposable.kt @@ -14,6 +14,7 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.aurora.store.R @@ -21,6 +22,7 @@ import com.aurora.store.R /** * A top app bar composable to be used with Scaffold in different Screen * @param title Title of the screen + * @param navigationIcon Icon for the navigation button * @param onNavigateUp Action when user clicks the navigation icon * @param actions Actions to display on the top app bar (for e.g. menu) */ @@ -28,17 +30,17 @@ import com.aurora.store.R @OptIn(ExperimentalMaterial3Api::class) fun TopAppBarComposable( title: String? = null, - onNavigateUp: () -> Unit, + navigationIcon: ImageVector = Icons.AutoMirrored.Filled.ArrowBack, + onNavigateUp: (() -> Unit)? = null, actions: @Composable (RowScope.() -> Unit) = {} ) { TopAppBar( title = { if (title != null) Text(text = title) }, navigationIcon = { - IconButton(onClick = onNavigateUp) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = null - ) + if (onNavigateUp != null) { + IconButton(onClick = onNavigateUp) { + Icon(imageVector = navigationIcon, contentDescription = null) + } } }, actions = actions diff --git a/app/src/main/java/com/aurora/store/compose/composables/app/AnimatedAppIconComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/app/AnimatedAppIconComposable.kt new file mode 100644 index 000000000..5b1da5ce4 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/composables/app/AnimatedAppIconComposable.kt @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2025 The Calyx Institute + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.store.compose.composables.app + +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import coil3.annotation.ExperimentalCoilApi +import coil3.compose.AsyncImage +import coil3.compose.LocalAsyncImagePreviewHandler +import coil3.request.ImageRequest +import coil3.request.crossfade +import com.aurora.gplayapi.data.models.App +import com.aurora.store.R +import com.aurora.store.compose.composables.preview.AppPreviewProvider +import com.aurora.store.compose.composables.preview.coilPreviewProvider + +/** + * Composable to show icon for an app that can be animated to also show install progress + * @param modifier Modifier to alter the composable + * @param iconUrl URL of the app icon + * @param progress Progress to show, for e.g. download or install + * @param inProgress Whether to show indeterminate or determinate progress bar + */ +@Composable +fun AnimatedAppIconComposable( + modifier: Modifier = Modifier, + iconUrl: String, + progress: Float = 0F, + inProgress: Boolean = false +) { + val animatedScale by animateFloatAsState( + targetValue = if (inProgress) 0.75F else 1F, + animationSpec = tween(durationMillis = 300) + ) + val clip = when { + inProgress -> CircleShape + else -> RoundedCornerShape(dimensionResource(R.dimen.radius_medium)) + } + + Box(modifier = modifier, contentAlignment = Alignment.Center) { + if (inProgress) { + val indicatorModifier = Modifier.fillMaxSize() + if (progress > 0) { + CircularProgressIndicator( + modifier = indicatorModifier, + progress = { progress / 100 } + ) + } else { + CircularProgressIndicator(modifier = indicatorModifier) + } + } + + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(iconUrl) + .crossfade(true) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxSize() + .graphicsLayer(scaleX = animatedScale, scaleY = animatedScale) + .clip(clip) + ) + } +} + +private class ProgressProvider: PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf(0F, 50F) +} + +@Preview(showBackground = true) +@Composable +@OptIn(ExperimentalCoilApi::class) +private fun AnimatedAppIconPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { + CompositionLocalProvider(LocalAsyncImagePreviewHandler provides coilPreviewProvider) { + AnimatedAppIconComposable( + modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)), + iconUrl = app.iconArtwork.url + ) + } +} + +@Preview(showBackground = true) +@Composable +@OptIn(ExperimentalCoilApi::class) +private fun AnimatedAppIconPreview(@PreviewParameter(ProgressProvider::class) progress: Float) { + CompositionLocalProvider(LocalAsyncImagePreviewHandler provides coilPreviewProvider) { + AnimatedAppIconComposable( + modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)), + iconUrl = "", + inProgress = true, + progress = progress + ) + } +} diff --git a/app/src/main/java/com/aurora/store/compose/composables/app/NoAppAltComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/app/NoAppAltComposable.kt deleted file mode 100644 index 9578f8357..000000000 --- a/app/src/main/java/com/aurora/store/compose/composables/app/NoAppAltComposable.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 The Calyx Institute - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.aurora.store.compose.composables.app - -import androidx.annotation.StringRes -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import com.aurora.store.R - -/** - * Composable to show error message when no apps are available for a request - * @param message Message for error - * @see NoAppAltComposable - */ -@Composable -fun NoAppAltComposable(@StringRes message: Int) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(dimensionResource(R.dimen.padding_xsmall)) - ) { - Text( - text = stringResource(message), - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } -} - -@Preview(showBackground = true) -@Composable -private fun NoAppAltComposablePreview() { - NoAppAltComposable(message = R.string.details_no_dependencies) -} diff --git a/app/src/main/java/com/aurora/store/compose/composables/app/NoAppComposable.kt b/app/src/main/java/com/aurora/store/compose/composables/app/NoAppComposable.kt index 4be59d5f2..c373fb6de 100644 --- a/app/src/main/java/com/aurora/store/compose/composables/app/NoAppComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composables/app/NoAppComposable.kt @@ -31,7 +31,6 @@ import com.aurora.store.R * @param message Message for error * @param actionMessage Message to show on action button; defaults to null with button not visible * @param onAction Callback when action button is clicked - * @see NoAppAltComposable */ @Composable fun NoAppComposable( diff --git a/app/src/main/java/com/aurora/store/compose/navigation/NavGraph.kt b/app/src/main/java/com/aurora/store/compose/navigation/NavGraph.kt index ed376b063..56f8c7795 100644 --- a/app/src/main/java/com/aurora/store/compose/navigation/NavGraph.kt +++ b/app/src/main/java/com/aurora/store/compose/navigation/NavGraph.kt @@ -11,13 +11,9 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.toRoute -import com.aurora.extensions.parentViewModel import com.aurora.store.compose.ui.commons.BlacklistScreen import com.aurora.store.compose.ui.details.AppDetailsScreen -import com.aurora.store.compose.ui.details.DetailsExodusScreen -import com.aurora.store.compose.ui.details.DetailsMoreScreen -import com.aurora.store.compose.ui.details.DetailsReviewScreen -import com.aurora.store.compose.ui.details.DetailsScreenshotScreen +import com.aurora.store.compose.ui.dev.DevProfileScreen /** * Navigation graph for compose screens @@ -45,46 +41,21 @@ fun NavGraph(navHostController: NavHostController, startDestination: Screen) { val appDetails = backstackEntry.toRoute() AppDetailsScreen( packageName = appDetails.packageName, - viewModel = backstackEntry.parentViewModel(navHostController), onNavigateUp = { onNavigateUp() }, - onNavigateToDetailsMore = { navHostController.navigate(Screen.DetailsMore) }, - onNavigateToDetailsReview = { navHostController.navigate(Screen.DetailsReview) }, - onNavigateToDetailsExodus = { navHostController.navigate(Screen.DetailsExodus) }, - onNavigateToDetailsScreenshot = { index -> - navHostController.navigate( - Screen.DetailsScreenshot(index) - ) + onNavigateToAppDetails = { packageName -> + navHostController.navigate(Screen.AppDetails(packageName)) } ) } - composable { backstackEntry -> - DetailsExodusScreen( + composable { backstackEntry -> + val devProfile = backstackEntry.toRoute() + DevProfileScreen( + developerId = devProfile.developerId, onNavigateUp = { onNavigateUp() }, - viewModel = backstackEntry.parentViewModel(navHostController) - ) - } - - composable { backstackEntry -> - DetailsMoreScreen( - onNavigateUp = { onNavigateUp() }, - viewModel = backstackEntry.parentViewModel(navHostController) - ) - } - - composable { backstackEntry -> - val screenshotDetails = backstackEntry.toRoute() - DetailsScreenshotScreen( - index = screenshotDetails.index, - onNavigateUp = { onNavigateUp() }, - viewModel = backstackEntry.parentViewModel(navHostController) - ) - } - - composable { backstackEntry -> - DetailsReviewScreen( - onNavigateUp = { onNavigateUp() }, - viewModel = backstackEntry.parentViewModel(navHostController) + onNavigateToAppDetails = { packageName -> + navHostController.navigate(Screen.AppDetails(packageName)) + } ) } } diff --git a/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt b/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt index e40c3f541..dfa5b2abd 100644 --- a/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt +++ b/app/src/main/java/com/aurora/store/compose/navigation/Screen.kt @@ -31,18 +31,33 @@ sealed class Screen( @Serializable data object Blacklist : Screen(label = R.string.title_blacklist_manager) + @Serializable + data class DevProfile(val developerId: String): Screen() + @Serializable data class AppDetails(val packageName: String) : Screen() + /** + * Child screen of [AppDetails]; Avoid navigating to this screen directly. + */ @Serializable data object DetailsMore : Screen() + /** + * Child screen of [AppDetails]; Avoid navigating to this screen directly. + */ @Serializable data class DetailsScreenshot(val index: Int) : Screen() + /** + * Child screen of [AppDetails]; Avoid navigating to this screen directly. + */ @Serializable data object DetailsExodus : Screen() + /** + * Child screen of [AppDetails]; Avoid navigating to this screen directly. + */ @Serializable data object DetailsReview : Screen() } 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 93a5c0e1f..6c8866c06 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 @@ -18,6 +18,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.safeContentPadding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState @@ -25,36 +28,44 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo +import androidx.compose.material3.adaptive.layout.AnimatedPane +import androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldRole +import androidx.compose.material3.adaptive.navigation.NavigableSupportingPaneScaffold +import androidx.compose.material3.adaptive.navigation.rememberSupportingPaneScaffoldNavigator import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope 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.draw.clip -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.fromHtml import androidx.compose.ui.text.style.TextOverflow 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.util.fastForEach import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.window.core.layout.WindowWidthSizeClass import coil3.annotation.ExperimentalCoilApi -import coil3.compose.AsyncImage import coil3.compose.LocalAsyncImagePreviewHandler -import coil3.request.ImageRequest -import coil3.request.crossfade import com.aurora.extensions.bodyVerySmall import com.aurora.extensions.browse import com.aurora.extensions.copyToClipBoard @@ -68,6 +79,8 @@ import com.aurora.store.R import com.aurora.store.compose.composables.HeaderComposable import com.aurora.store.compose.composables.InfoComposable import com.aurora.store.compose.composables.TopAppBarComposable +import com.aurora.store.compose.composables.app.AnimatedAppIconComposable +import com.aurora.store.compose.composables.app.AppListComposable import com.aurora.store.compose.composables.app.AppProgressComposable import com.aurora.store.compose.composables.app.AppTagComposable import com.aurora.store.compose.composables.app.NoAppComposable @@ -75,6 +88,8 @@ import com.aurora.store.compose.composables.details.RatingComposable import com.aurora.store.compose.composables.details.ScreenshotComposable import com.aurora.store.compose.composables.preview.AppPreviewProvider import com.aurora.store.compose.composables.preview.coilPreviewProvider +import com.aurora.store.compose.navigation.Screen +import com.aurora.store.compose.ui.dev.DevProfileScreen import com.aurora.store.compose.ui.dialogs.ManualDownloadDialog import com.aurora.store.data.installer.AppInstaller import com.aurora.store.data.model.Report @@ -84,17 +99,16 @@ import com.aurora.store.util.CommonUtil import com.aurora.store.util.PackageUtil import com.aurora.store.util.PackageUtil.PACKAGE_NAME_GMS import com.aurora.store.viewmodel.details.AppDetailsViewModel +import kotlinx.coroutines.launch import java.util.Locale +import kotlin.random.Random import com.aurora.gplayapi.data.models.datasafety.Report as DataSafetyReport @Composable fun AppDetailsScreen( packageName: String, onNavigateUp: () -> Unit, - onNavigateToDetailsMore: () -> Unit, - onNavigateToDetailsScreenshot: (index: Int) -> Unit, - onNavigateToDetailsReview: () -> Unit, - onNavigateToDetailsExodus: () -> Unit, + onNavigateToAppDetails: (packageName: String) -> Unit, viewModel: AppDetailsViewModel = hiltViewModel() ) { val context = LocalContext.current @@ -104,17 +118,23 @@ fun AppDetailsScreen( val dataSafetyReport by viewModel.dataSafetyReport.collectAsStateWithLifecycle() val plexusScores by viewModel.plexusScores.collectAsStateWithLifecycle() val download by viewModel.download.collectAsStateWithLifecycle() + val installProgress by viewModel.installProgress.collectAsStateWithLifecycle() + val suggestions by viewModel.suggestions.collectAsStateWithLifecycle() var shouldShowManualDownloadDialog by rememberSaveable { mutableStateOf(false) } LaunchedEffect(key1 = Unit) { viewModel.fetchAppDetails(packageName) } LaunchedEffect(key1 = Unit) { viewModel.purchaseStatus.collect { success -> - if (success) { - shouldShowManualDownloadDialog = false - context.toast(R.string.toast_manual_available) + if (shouldShowManualDownloadDialog) { + if (success) { + shouldShowManualDownloadDialog = false + context.toast(R.string.toast_manual_available) + } else { + context.toast(R.string.toast_manual_unavailable) + } } else { - context.toast(R.string.toast_manual_unavailable) + context.toast(R.string.download_failed) } } } @@ -135,19 +155,18 @@ fun AppDetailsScreen( if (this.packageName.isBlank()) { ScreenContentLoading(onNavigateUp = onNavigateUp) } else { - ScreenContent( + ScreenContentApp( app = this, + isAnonymous = viewModel.authProvider.isAnonymous, + suggestions = suggestions, download = download, + installProgress = installProgress, plexusScores = plexusScores, dataSafetyReport = dataSafetyReport, exodusReport = exodusReport, hasValidUpdate = viewModel.hasValidUpdate, - showSimilarApps = viewModel.showSimilarApps, onNavigateUp = onNavigateUp, - onNavigateToDetailsMore = onNavigateToDetailsMore, - onNavigateToDetailsScreenshot = onNavigateToDetailsScreenshot, - onNavigateToDetailsReview = onNavigateToDetailsReview, - onNavigateToDetailsExodus = onNavigateToDetailsExodus, + onNavigateToAppDetails = onNavigateToAppDetails, onDownload = { viewModel.download(this) }, onManualDownload = { shouldShowManualDownloadDialog = true }, onCancelDownload = { viewModel.cancelDownload(this) }, @@ -160,6 +179,9 @@ fun AppDetailsScreen( } catch (exception: ActivityNotFoundException) { context.toast(context.getString(R.string.unable_to_open)) } + }, + onTestingSubscriptionChange = { subscribe -> + viewModel.updateTestingProgramStatus(packageName, subscribe) } ) } @@ -171,6 +193,9 @@ fun AppDetailsScreen( } } +/** + * Composable to show progress while fetching app details + */ @Composable private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) { Scaffold( @@ -180,6 +205,9 @@ private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) { } } +/** + * Composable to display errors related to fetching app details + */ @Composable private fun ScreenContentError(onNavigateUp: () -> Unit = {}) { Scaffold( @@ -193,28 +221,186 @@ private fun ScreenContentError(onNavigateUp: () -> Unit = {}) { } } +/** + * Composable to display app details and suggestions + */ @Composable -private fun ScreenContent( +@OptIn(ExperimentalMaterial3AdaptiveApi::class) +private fun ScreenContentApp( app: App, + isAnonymous: Boolean = true, + suggestions: List = emptyList(), download: Download? = null, + installProgress: Float? = null, plexusScores: Scores? = null, dataSafetyReport: DataSafetyReport? = null, exodusReport: Report? = null, hasValidUpdate: Boolean = false, - showSimilarApps: Boolean = false, onNavigateUp: () -> Unit = {}, - onNavigateToDetailsMore: () -> Unit = {}, - onNavigateToDetailsScreenshot: (index: Int) -> Unit = {}, - onNavigateToDetailsReview: () -> Unit = {}, - onNavigateToDetailsExodus: () -> Unit = {}, + onNavigateToAppDetails: (packageName: String) -> Unit = {}, onDownload: () -> Unit = {}, onManualDownload: () -> Unit = {}, onCancelDownload: () -> Unit = {}, onUninstall: () -> Unit = {}, - onOpen: () -> Unit = {} + onOpen: () -> Unit = {}, + onTestingSubscriptionChange: (subscribe: Boolean) -> Unit = {} +) { + val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator() + val coroutineScope = rememberCoroutineScope() + + fun showMainPane() { + coroutineScope.launch { + scaffoldNavigator.navigateBack() + } + } + + fun showExtraPane(screen: Screen) { + coroutineScope.launch { + scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Extra, screen) + } + } + + NavigableSupportingPaneScaffold( + navigator = scaffoldNavigator, + mainPane = { + AnimatedPane { + ScreenContentAppMainPane( + app = app, + isAnonymous = isAnonymous, + download = download, + installProgress = installProgress, + plexusScores = plexusScores, + dataSafetyReport = dataSafetyReport, + exodusReport = exodusReport, + hasValidUpdate = hasValidUpdate, + onNavigateUp = onNavigateUp, + onDownload = onDownload, + onManualDownload = onManualDownload, + onCancelDownload = onCancelDownload, + onUninstall = onUninstall, + onOpen = onOpen, + onNavigateToDetailsDevProfile = { showExtraPane(Screen.DevProfile(it)) }, + onNavigateToDetailsMore = { showExtraPane(Screen.DetailsMore) }, + onNavigateToDetailsReview = { showExtraPane(Screen.DetailsReview) }, + onNavigateToDetailsExodus = { showExtraPane(Screen.DetailsExodus) }, + onNavigateToDetailsScreenshot = { showExtraPane(Screen.DetailsScreenshot(it)) }, + onTestingSubscriptionChange = onTestingSubscriptionChange + ) + } + }, + supportingPane = { + AnimatedPane(modifier = Modifier.safeContentPadding()) { + ScreenContentAppSupportingPane( + suggestions = suggestions, + onNavigateToAppDetails = onNavigateToAppDetails + ) + } + }, + extraPane = { + scaffoldNavigator.currentDestination?.contentKey?.let { screen -> + AnimatedPane { + when (screen) { + is Screen.DetailsReview -> DetailsReviewScreen(onNavigateUp = ::showMainPane) + is Screen.DetailsExodus -> DetailsExodusScreen(onNavigateUp = ::showMainPane) + is Screen.DetailsMore -> DetailsMoreScreen( + onNavigateUp = ::showMainPane, + onNavigateToAppDetails = onNavigateToAppDetails + ) + + is Screen.DetailsScreenshot -> DetailsScreenshotScreen( + index = screen.index, + onNavigateUp = ::showMainPane + ) + // TODO: Pass the real developerId + is Screen.DevProfile -> DevProfileScreen( + developerId = app.developerName, + onNavigateUp = ::showMainPane, + onNavigateToAppDetails = { onNavigateToAppDetails(it) } + ) + + else -> {} + } + } + } + } + ) +} + +/** + * Composable to display app details + */ +@Composable +private fun ScreenContentAppMainPane( + app: App, + download: Download?, + installProgress: Float?, + isAnonymous: Boolean, + plexusScores: Scores?, + dataSafetyReport: DataSafetyReport?, + exodusReport: Report?, + hasValidUpdate: Boolean, + onNavigateUp: () -> Unit, + onNavigateToDetailsDevProfile: (developerName: String) -> Unit, + onNavigateToDetailsMore: () -> Unit, + onNavigateToDetailsScreenshot: (index: Int) -> Unit, + onNavigateToDetailsReview: () -> Unit, + onNavigateToDetailsExodus: () -> Unit, + onDownload: () -> Unit, + onManualDownload: () -> Unit, + onCancelDownload: () -> Unit, + onUninstall: () -> Unit, + onOpen: () -> Unit, + onTestingSubscriptionChange: (subscribe: Boolean) -> Unit ) { val context = LocalContext.current + @Composable + fun SetupAppActions() { + when { + download?.isRunning == true -> { + AppActions( + primaryActionDisplayName = stringResource(R.string.action_open), + secondaryActionDisplayName = stringResource(R.string.action_cancel), + isPrimaryActionEnabled = false, + onSecondaryAction = onCancelDownload + ) + } + + app.isInstalled && hasValidUpdate -> { + AppActions( + primaryActionDisplayName = stringResource(R.string.action_update), + secondaryActionDisplayName = stringResource(R.string.action_uninstall), + onPrimaryAction = onDownload, + onSecondaryAction = onUninstall + ) + } + + app.isInstalled -> { + AppActions( + primaryActionDisplayName = stringResource(R.string.action_open), + secondaryActionDisplayName = stringResource(R.string.action_uninstall), + onPrimaryAction = onOpen, + onSecondaryAction = onUninstall + ) + } + + else -> { + val primaryActionName = if (PackageUtil.isArchived(context, app.packageName)) { + stringResource(R.string.action_unarchive) + } else { + if (app.isFree) stringResource(R.string.action_install) else app.price + } + + AppActions( + primaryActionDisplayName = primaryActionName, + secondaryActionDisplayName = stringResource(R.string.title_manual_download), + onPrimaryAction = onDownload, + onSecondaryAction = onManualDownload + ) + } + } + } + Scaffold( topBar = { TopAppBarComposable( @@ -230,53 +416,24 @@ private fun ScreenContent( .padding(dimensionResource(R.dimen.padding_medium)), verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) ) { - // TODO: Deal with download status - AppDetails(app = app, hasValidUpdate = hasValidUpdate) - - when { - download?.isRunning == true -> { - AppActions( - primaryActionDisplayName = stringResource(R.string.action_open), - secondaryActionDisplayName = stringResource(R.string.action_cancel), - isPrimaryActionEnabled = false, - onSecondaryAction = onCancelDownload - ) - } - - app.isInstalled && hasValidUpdate -> { - AppActions( - primaryActionDisplayName = stringResource(R.string.action_update), - secondaryActionDisplayName = stringResource(R.string.action_uninstall), - onPrimaryAction = onDownload, - onSecondaryAction = onUninstall - ) - } - - app.isInstalled -> { - AppActions( - primaryActionDisplayName = stringResource(R.string.action_open), - secondaryActionDisplayName = stringResource(R.string.action_uninstall), - onPrimaryAction = onOpen, - onSecondaryAction = onUninstall - ) - } - - else -> { - val primaryActionName = if (PackageUtil.isArchived(context, app.packageName)) { - stringResource(R.string.action_unarchive) - } else { - if (app.isFree) stringResource(R.string.action_install) else app.price - } - - AppActions( - primaryActionDisplayName = primaryActionName, - secondaryActionDisplayName = stringResource(R.string.title_manual_download), - onPrimaryAction = onDownload, - onSecondaryAction = onManualDownload - ) - } + val isDownloading = download != null && download.isRunning + val isInstalling = installProgress != null + val progress = when { + isDownloading -> download?.progress?.toFloat() ?: 0F + isInstalling -> installProgress ?: 0F + else -> 0F } + AppDetails( + app = app, + inProgress = isDownloading || isInstalling, + progress = progress, + onNavigateToDetailsDevApps = onNavigateToDetailsDevProfile, + hasValidUpdate = hasValidUpdate + ) + + SetupAppActions() + AppTags(app = app) AppChangelog(changelog = app.changes) HeaderComposable( @@ -292,6 +449,13 @@ private fun ScreenContent( AppReviews(rating = app.rating, onNavigateToDetailsReview = onNavigateToDetailsReview) + if (!isAnonymous && app.testingProgram?.isAvailable == true) { + AppTesting( + isSubscribed = app.testingProgram!!.isSubscribed, + onTestingSubscriptionChange = onTestingSubscriptionChange + ) + } + AppCompatibility( needsGms = app.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS), plexusScores = plexusScores @@ -322,24 +486,67 @@ private fun ScreenContent( } } +/** + * Composable to display similar and related app suggestions + */ +@Composable +private fun ScreenContentAppSupportingPane( + suggestions: List = emptyList(), + onNavigateToAppDetails: (packageName: String) -> Unit = {} +) { + Scaffold( + topBar = { TopAppBarComposable() } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + ) { + Row( + modifier = Modifier.padding(dimensionResource(R.dimen.margin_medium)), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(R.drawable.ic_suggestions), + contentDescription = null + ) + HeaderComposable(title = stringResource(R.string.pref_ui_similar_apps)) + } + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(vertical = dimensionResource(R.dimen.padding_medium)) + ) { + items(items = suggestions, key = { item -> item.id }) { app -> + AppListComposable( + app = app, + onClick = { onNavigateToAppDetails(app.packageName) } + ) + } + } + } + } +} + /** * Composable to display basic app details */ @Composable -private fun AppDetails(app: App, hasValidUpdate: Boolean = false) { +private fun AppDetails( + app: App, + progress: Float = 0F, + inProgress: Boolean = false, + onNavigateToDetailsDevApps: (developerName: String) -> Unit, + hasValidUpdate: Boolean = false, +) { val context = LocalContext.current Row(modifier = Modifier.fillMaxWidth()) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(app.iconArtwork.url) - .crossfade(true) - .build(), - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier - .requiredSize(dimensionResource(R.dimen.icon_size_large)) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) + AnimatedAppIconComposable( + modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)), + iconUrl = app.iconArtwork.url, + inProgress = inProgress, + progress = progress ) Column(modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small))) { Text( @@ -349,6 +556,8 @@ private fun AppDetails(app: App, hasValidUpdate: Boolean = false) { overflow = TextOverflow.Ellipsis ) Text( + modifier = Modifier + .clickable(onClick = { onNavigateToDetailsDevApps(app.developerName) }), text = app.developerName, style = MaterialTheme.typography.bodyMedium, maxLines = 1, @@ -383,14 +592,20 @@ private fun AppActions( isPrimaryActionEnabled: Boolean = true, isSecondaryActionEnabled: Boolean = true, onPrimaryAction: () -> Unit = {}, - onSecondaryAction: () -> Unit = {} + onSecondaryAction: () -> Unit = {}, + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)) ) { + val buttonWidthModifier = when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) { + WindowWidthSizeClass.COMPACT -> Modifier.weight(1F) + else -> Modifier.widthIn(min = dimensionResource(R.dimen.width_button)) + } + FilledTonalButton( - modifier = Modifier.weight(1F), + modifier = buttonWidthModifier, onClick = onSecondaryAction, enabled = isSecondaryActionEnabled ) { @@ -402,7 +617,7 @@ private fun AppActions( } Button( - modifier = Modifier.weight(1F), + modifier = buttonWidthModifier, onClick = onPrimaryAction, enabled = isPrimaryActionEnabled ) { @@ -551,7 +766,6 @@ private fun AppReviews(rating: Rating, onNavigateToDetailsReview: () -> Unit) { HeaderComposable( title = stringResource(R.string.details_ratings), - subtitle = stringResource(R.string.details_ratings_subtitle), onClick = onNavigateToDetailsReview ) @@ -719,12 +933,57 @@ private fun AppPrivacy(report: Report?, onNavigateToDetailsExodus: (() -> Unit)? ) } -@Preview +/** + * Composable to display app's beta testing status + */ +@Composable +private fun AppTesting( + isSubscribed: Boolean = false, + onTestingSubscriptionChange: (subscribe: Boolean) -> Unit +) { + HeaderComposable(title = stringResource(R.string.details_beta)) + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) + ) { + InfoComposable( + modifier = Modifier.weight(1F), + icon = R.drawable.ic_experiment, + title = AnnotatedString( + text = if (isSubscribed) { + stringResource(R.string.details_beta_subscribed) + } else { + stringResource(R.string.details_beta_available) + } + ), + description = AnnotatedString(text = stringResource(R.string.details_beta_description)) + ) + FilledTonalButton(onClick = { onTestingSubscriptionChange(!isSubscribed) }) { + Text( + text = if (isSubscribed) { + stringResource(R.string.action_leave) + } else { + stringResource(R.string.action_join) + }, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } +} + +@PreviewScreenSizes @Composable @OptIn(ExperimentalCoilApi::class) private fun AppDetailsScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { CompositionLocalProvider(LocalAsyncImagePreviewHandler provides coilPreviewProvider) { - ScreenContent(app = app, hasValidUpdate = false) + ScreenContentApp( + app = app, + isAnonymous = false, + suggestions = List(5) { app.copy(id = Random.nextInt()) }, + hasValidUpdate = false + ) } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/DetailsExodusScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/DetailsExodusScreen.kt index 7e7e6152b..6d1bd493c 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/DetailsExodusScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/DetailsExodusScreen.kt @@ -10,6 +10,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Scaffold +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -20,7 +22,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.window.core.layout.WindowWidthSizeClass import com.aurora.Constants.EXODUS_REPORT_URL +import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.extensions.browse import com.aurora.gplayapi.data.models.App import com.aurora.store.R @@ -30,20 +34,31 @@ import com.aurora.store.compose.composables.details.ExodusComposable import com.aurora.store.compose.composables.preview.AppPreviewProvider import com.aurora.store.data.model.ExodusTracker import com.aurora.store.viewmodel.details.AppDetailsViewModel +import com.aurora.store.viewmodel.details.DetailsExodusViewModel @Composable fun DetailsExodusScreen( onNavigateUp: () -> Unit, - viewModel: AppDetailsViewModel = hiltViewModel() + appDetailsViewModel: AppDetailsViewModel = hiltViewModel(), + detailsExodusViewModel: DetailsExodusViewModel = hiltViewModel { factory: DetailsExodusViewModel.Factory -> + factory.create(appDetailsViewModel.exodusReport.value!!) + }, + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() ) { - val app by viewModel.app.collectAsStateWithLifecycle() - val exodusReport by viewModel.exodusReport.collectAsStateWithLifecycle() + val app by appDetailsViewModel.app.collectAsStateWithLifecycle() + val exodusReport by appDetailsViewModel.exodusReport.collectAsStateWithLifecycle() + val trackers by detailsExodusViewModel.trackers.collectAsStateWithLifecycle() + + val topAppBarTitle = when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) { + WindowWidthSizeClass.COMPACT -> app!!.displayName + else -> stringResource(R.string.details_privacy) + } ScreenContent( - topAppBarTitle = app!!.displayName, + topAppBarTitle = topAppBarTitle, id = exodusReport!!.id, version = exodusReport!!.version, - trackers = viewModel.getExodusTrackersFromReport(), + trackers = trackers, onNavigateUp = onNavigateUp, ) } @@ -55,12 +70,17 @@ private fun ScreenContent( version: String = String(), trackers: List = emptyList(), onNavigateUp: () -> Unit = {}, + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() ) { val context = LocalContext.current Scaffold( topBar = { - TopAppBarComposable(title = topAppBarTitle, onNavigateUp = onNavigateUp) + TopAppBarComposable( + title = topAppBarTitle, + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, + onNavigateUp = onNavigateUp + ) } ) { paddingValues -> LazyColumn( diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/DetailsMoreScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/DetailsMoreScreen.kt index 1216bf317..7945c6576 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/DetailsMoreScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/DetailsMoreScreen.kt @@ -8,12 +8,17 @@ package com.aurora.store.compose.ui.details import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue @@ -26,32 +31,63 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.window.core.layout.WindowWidthSizeClass import coil3.annotation.ExperimentalCoilApi import coil3.compose.LocalAsyncImagePreviewHandler +import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.gplayapi.data.models.App import com.aurora.store.R import com.aurora.store.compose.composables.HeaderComposable import com.aurora.store.compose.composables.InfoComposable import com.aurora.store.compose.composables.TopAppBarComposable +import com.aurora.store.compose.composables.app.AppComposable import com.aurora.store.compose.composables.preview.AppPreviewProvider import com.aurora.store.compose.composables.preview.coilPreviewProvider import com.aurora.store.viewmodel.details.AppDetailsViewModel +import com.aurora.store.viewmodel.details.DetailsMoreViewModel import java.util.Locale @Composable fun DetailsMoreScreen( onNavigateUp: () -> Unit, - viewModel: AppDetailsViewModel = hiltViewModel() + onNavigateToAppDetails: (packageName: String) -> Unit, + appDetailsViewModel: AppDetailsViewModel = hiltViewModel(), + detailsMoreViewModel: DetailsMoreViewModel = hiltViewModel { factory: DetailsMoreViewModel.Factory -> + factory.create(appDetailsViewModel.app.value!!.dependencies.dependentPackages) + } ) { - val app by viewModel.app.collectAsStateWithLifecycle() + val app by appDetailsViewModel.app.collectAsStateWithLifecycle() + val dependencies by detailsMoreViewModel.dependentApps.collectAsStateWithLifecycle() - ScreenContent(app = app!!, onNavigateUp = onNavigateUp) + ScreenContent( + app = app!!, + dependencies = dependencies, + onNavigateUp = onNavigateUp, + onNavigateToAppDetails = onNavigateToAppDetails + ) } @Composable -private fun ScreenContent(app: App, onNavigateUp: () -> Unit = {}) { +private fun ScreenContent( + app: App, + dependencies: List? = null, + onNavigateUp: () -> Unit = {}, + onNavigateToAppDetails: (packageName: String) -> Unit = {}, + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() +) { + val topAppBarTitle = when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) { + WindowWidthSizeClass.COMPACT -> app.displayName + else -> stringResource(R.string.details_more_about_app) + } + Scaffold( - topBar = { TopAppBarComposable(title = app.displayName, onNavigateUp = onNavigateUp) } + topBar = { + TopAppBarComposable( + title = topAppBarTitle, + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, + onNavigateUp = onNavigateUp + ) + } ) { paddingValues -> Column( modifier = Modifier @@ -69,51 +105,88 @@ private fun ScreenContent(app: App, onNavigateUp: () -> Unit = {}) { style = MaterialTheme.typography.bodyMedium ) - if (app.appInfo.appInfoMap.isNotEmpty()) { - HeaderComposable(title = stringResource(R.string.details_more_info)) - - InfoComposable( - title = AnnotatedString( - text = stringResource(R.string.details_more_package_name) - ), - description = AnnotatedString(text = app.packageName) + if (dependencies != null) { + AppDependencies( + dependencies = dependencies, + onNavigateToAppDetails = onNavigateToAppDetails ) + } - InfoComposable( - title = AnnotatedString( - text = stringResource(R.string.details_more_content_rating) - ), - description = AnnotatedString(text = app.contentRating.title) - ) + AppInfoMore(app = app) + } + } +} - app.appInfo.appInfoMap.forEach { (title, subtitle) -> - InfoComposable( - title = AnnotatedString( - text = title.replace("_", " ") - .lowercase(Locale.getDefault()) - .replaceFirstChar { - if (it.isLowerCase()) { - it.titlecase(Locale.getDefault()) - } else { - it.toString() - } - } - ), - description = AnnotatedString(text = subtitle) - ) - } - - InfoComposable( - title = AnnotatedString( - text = stringResource(R.string.details_more_target_api) - ), - description = AnnotatedString(text = "API ${app.targetSdk}") +/** + * Composable to show dependencies of an app + */ +@Composable +private fun AppDependencies( + dependencies: List, + onNavigateToAppDetails: (packageName: String) -> Unit +) { + HeaderComposable(title = stringResource(R.string.details_dependencies)) + if (dependencies.isEmpty()) { + InfoComposable( + title = AnnotatedString(text = stringResource(R.string.details_no_dependencies)) + ) + } else { + LazyRow(modifier = Modifier.fillMaxWidth()) { + items(items = dependencies, key = { item -> item.id }) { app -> + AppComposable( + app = app, + onClick = { onNavigateToAppDetails(app.packageName) } ) } } } } +/** + * Composable to show more information about the app that maybe advanced + */ +@Composable +private fun AppInfoMore(app: App) { + HeaderComposable(title = stringResource(R.string.details_more_info)) + InfoComposable( + title = AnnotatedString( + text = stringResource(R.string.details_more_package_name) + ), + description = AnnotatedString(text = app.packageName) + ) + + InfoComposable( + title = AnnotatedString( + text = stringResource(R.string.details_more_target_api) + ), + description = AnnotatedString(text = "API ${app.targetSdk}") + ) + + InfoComposable( + title = AnnotatedString( + text = stringResource(R.string.details_more_content_rating) + ), + description = AnnotatedString(text = app.contentRating.title) + ) + + app.appInfo.appInfoMap.forEach { (title, subtitle) -> + InfoComposable( + title = AnnotatedString( + text = title.replace("_", " ") + .lowercase(Locale.getDefault()) + .replaceFirstChar { + if (it.isLowerCase()) { + it.titlecase(Locale.getDefault()) + } else { + it.toString() + } + } + ), + description = AnnotatedString(text = subtitle) + ) + } +} + @Preview @Composable @OptIn(ExperimentalCoilApi::class) diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/DetailsReviewScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/DetailsReviewScreen.kt index e18c396dd..715b773c2 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/DetailsReviewScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/DetailsReviewScreen.kt @@ -18,6 +18,8 @@ import androidx.compose.material3.FilterChip import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -34,26 +36,38 @@ import androidx.paging.PagingData import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.itemKey +import androidx.window.core.layout.WindowWidthSizeClass +import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.gplayapi.data.models.Review import com.aurora.store.R import com.aurora.store.compose.composables.TopAppBarComposable import com.aurora.store.compose.composables.details.ReviewComposable import com.aurora.store.viewmodel.details.AppDetailsViewModel +import com.aurora.store.viewmodel.review.DetailsReviewViewModel import kotlinx.coroutines.flow.flowOf @Composable fun DetailsReviewScreen( onNavigateUp: () -> Unit, - viewModel: AppDetailsViewModel = hiltViewModel() + appDetailsViewModel: AppDetailsViewModel = hiltViewModel(), + detailsReviewViewModel: DetailsReviewViewModel = hiltViewModel { factory: DetailsReviewViewModel.Factory -> + factory.create(appDetailsViewModel.app.value!!.packageName) + }, + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() ) { - val app by viewModel.app.collectAsStateWithLifecycle() - val reviews = viewModel.reviews.collectAsLazyPagingItems() + val app by appDetailsViewModel.app.collectAsStateWithLifecycle() + val reviews = detailsReviewViewModel.reviews.collectAsLazyPagingItems() + + val topAppBarTitle = when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) { + WindowWidthSizeClass.COMPACT -> app!!.displayName + else -> stringResource(R.string.details_ratings) + } ScreenContent( - topAppBarTitle = app!!.displayName, + topAppBarTitle = topAppBarTitle, reviews = reviews, onNavigateUp = onNavigateUp, - onFilter = { filter -> viewModel.fetchReviews(filter) } + onFilter = { filter -> detailsReviewViewModel.fetchReviews(filter) } ) } @@ -62,12 +76,17 @@ private fun ScreenContent( topAppBarTitle: String? = null, onNavigateUp: () -> Unit = {}, reviews: LazyPagingItems, - onFilter: (filter: Review.Filter) -> Unit = {} + onFilter: (filter: Review.Filter) -> Unit = {}, + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() ) { Scaffold( topBar = { - TopAppBarComposable(title = topAppBarTitle, onNavigateUp = onNavigateUp) + TopAppBarComposable( + title = topAppBarTitle, + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, + onNavigateUp = onNavigateUp + ) } ) { paddingValues -> Column( @@ -90,7 +109,7 @@ private fun ScreenContent( count = reviews.itemCount, key = reviews.itemKey { it.commentId } ) { index -> - ReviewComposable(review = reviews[index]!!) + reviews[index]?.let { review -> ReviewComposable(review = review) } } } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/DetailsScreenshotScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/DetailsScreenshotScreen.kt index a6f1d757d..85289067f 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/DetailsScreenshotScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/DetailsScreenshotScreen.kt @@ -10,7 +10,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.Scaffold +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -18,6 +21,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.window.core.layout.WindowWidthSizeClass +import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.gplayapi.data.models.Artwork import com.aurora.store.R import com.aurora.store.compose.composables.TopAppBarComposable @@ -28,12 +33,18 @@ import com.aurora.store.viewmodel.details.AppDetailsViewModel fun DetailsScreenshotScreen( index: Int, onNavigateUp: () -> Unit, - viewModel: AppDetailsViewModel = hiltViewModel() + viewModel: AppDetailsViewModel = hiltViewModel(), + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() ) { val app by viewModel.app.collectAsStateWithLifecycle() + val topAppBarTitle = when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) { + WindowWidthSizeClass.COMPACT -> app!!.displayName + else -> stringResource(R.string.details_more_about_app) + } + ScreenContent( - topAppBarTitle = app!!.displayName, + topAppBarTitle = topAppBarTitle, onNavigateUp = onNavigateUp, screenshots = app!!.screenshots, index = index @@ -45,14 +56,23 @@ private fun ScreenContent( topAppBarTitle: String? = null, onNavigateUp: () -> Unit = {}, screenshots: List = emptyList(), - index: Int = 0 + index: Int = 0, + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() ) { val displayMetrics = LocalContext.current.resources.displayMetrics val pagerState = rememberPagerState(initialPage = index) { screenshots.size } + LaunchedEffect(key1 = index) { + if (pagerState.currentPage != index) pagerState.scrollToPage(index) + } + Scaffold( topBar = { - TopAppBarComposable(title = topAppBarTitle, onNavigateUp = onNavigateUp) + TopAppBarComposable( + title = topAppBarTitle, + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, + onNavigateUp = onNavigateUp + ) } ) { paddingValues -> HorizontalPager( 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 new file mode 100644 index 000000000..c54be52d7 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2025 The Calyx Institute + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.store.compose.ui.dev + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Scaffold +import androidx.compose.material3.adaptive.WindowAdaptiveInfo +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import com.aurora.extensions.adaptiveNavigationIcon +import com.aurora.store.R +import com.aurora.store.compose.composables.TopAppBarComposable +import com.aurora.store.viewmodel.details.DevProfileViewModel + +@Composable +fun DevProfileScreen( + developerId: String, + onNavigateUp: () -> Unit, + onNavigateToAppDetails: (packageName: String) -> Unit, + devProfileViewModel: DevProfileViewModel = hiltViewModel() +) { + + ScreenContent( + topAppBarTitle = "", + onNavigateUp = onNavigateUp, + onNavigateToAppDetails = onNavigateToAppDetails + ) +} + +@Composable +private fun ScreenContent( + topAppBarTitle: String? = null, + onNavigateUp: () -> Unit = {}, + onNavigateToAppDetails: (packageName: String) -> Unit = {}, + windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() +) { + Scaffold( + topBar = { + TopAppBarComposable( + title = topAppBarTitle, + navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, + onNavigateUp = onNavigateUp + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(dimensionResource(R.dimen.padding_medium)), + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + ) { + + } + } +} + +@Preview +@Composable +private fun DevProfileScreenPreview() { + ScreenContent() +} diff --git a/app/src/main/java/com/aurora/store/data/event/BusEvent.kt b/app/src/main/java/com/aurora/store/data/event/BusEvent.kt index b6b92af90..a37d03344 100644 --- a/app/src/main/java/com/aurora/store/data/event/BusEvent.kt +++ b/app/src/main/java/com/aurora/store/data/event/BusEvent.kt @@ -26,7 +26,6 @@ sealed class BusEvent : Event() { lateinit var error: String data class Blacklisted(val packageName: String) : BusEvent() - data class ManualDownload(val packageName: String, val versionCode: Long) : BusEvent() } sealed class AuthEvent : Event() { diff --git a/app/src/main/java/com/aurora/store/util/Preferences.kt b/app/src/main/java/com/aurora/store/util/Preferences.kt index 326dd27a2..6cf12dda3 100644 --- a/app/src/main/java/com/aurora/store/util/Preferences.kt +++ b/app/src/main/java/com/aurora/store/util/Preferences.kt @@ -35,7 +35,6 @@ object Preferences { const val PREFERENCE_THEME_STYLE = "PREFERENCE_THEME_STYLE" const val PREFERENCE_FOR_YOU = "PREFERENCE_FOR_YOU" const val PREFERENCE_DEFAULT_SELECTED_TAB = "PREFERENCE_DEFAULT_SELECTED_TAB" - const val PREFERENCE_SIMILAR = "PREFERENCE_SIMILAR" const val PREFERENCE_INTRO = "PREFERENCE_INTRO" const val PREFERENCE_FILTER_FDROID = "PREFERENCE_FILTER_FDROID" diff --git a/app/src/main/java/com/aurora/store/view/epoxy/controller/DeveloperCarouselController.kt b/app/src/main/java/com/aurora/store/view/epoxy/controller/DeveloperCarouselController.kt deleted file mode 100644 index cf5b1c697..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/controller/DeveloperCarouselController.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.epoxy.controller - -import com.aurora.gplayapi.data.models.StreamBundle -import com.aurora.gplayapi.data.models.StreamCluster -import com.aurora.store.view.epoxy.groups.CarouselShimmerGroup -import com.aurora.store.view.epoxy.groups.DeveloperModelGroup - -open class DeveloperCarouselController(private val callbacks: Callbacks) : - GenericCarouselController(callbacks) { - - override fun applyFilter(streamBundle: StreamCluster): Boolean { - return streamBundle.clusterTitle.isNotBlank() //Filter noisy cluster - && streamBundle.clusterAppList.isNotEmpty() //Filter empty clusters - } - - override fun buildModels(streamBundle: StreamBundle?) { - setFilterDuplicates(true) - if (streamBundle == null) { - for (i in 1..2) { - add( - CarouselShimmerGroup() - .id(i) - ) - } - } else { - streamBundle - .streamClusters - .values - .filter { applyFilter(it) } - .forEach { streamCluster -> - add(DeveloperModelGroup(streamCluster, callbacks)) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/aurora/store/view/epoxy/groups/DeveloperModelGroup.kt b/app/src/main/java/com/aurora/store/view/epoxy/groups/DeveloperModelGroup.kt deleted file mode 100644 index 6e4950aa3..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/groups/DeveloperModelGroup.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.epoxy.groups - -import android.util.Log -import com.airbnb.epoxy.EpoxyModel -import com.airbnb.epoxy.EpoxyModelGroup -import com.aurora.gplayapi.data.models.StreamCluster -import com.aurora.store.R -import com.aurora.store.view.epoxy.controller.GenericCarouselController -import com.aurora.store.view.epoxy.views.HeaderViewModel_ -import com.aurora.store.view.epoxy.views.app.AppListViewModel_ -import com.aurora.store.view.epoxy.views.app.AppViewModel_ -import com.aurora.store.view.epoxy.views.details.ScreenshotViewModel_ - -class DeveloperModelGroup( - streamCluster: StreamCluster, - callbacks: GenericCarouselController.Callbacks -) : - EpoxyModelGroup( - R.layout.model_developer_carousel_group, buildModels( - streamCluster, - callbacks - ) - ) { - companion object { - private const val TAG = "DeveloperModelGroup" - - private fun buildModels( - streamCluster: StreamCluster, - callbacks: GenericCarouselController.Callbacks - ): List> { - val models = ArrayList>() - val clusterViewModels = mutableListOf>() - val screenshotsViewModels = mutableListOf>() - - val idPrefix = streamCluster.id - - models.add( - HeaderViewModel_() - .id("${idPrefix}_header") - .title(streamCluster.clusterTitle) - .browseUrl(streamCluster.clusterBrowseUrl) - .click { _ -> - callbacks.onHeaderClicked(streamCluster) - } - ) - - if (streamCluster.clusterAppList.size == 1) { - val app = streamCluster.clusterAppList[0] - - for (artwork in app.screenshots) { - screenshotsViewModels.add( - ScreenshotViewModel_() - .id(artwork.url) - .artwork(artwork) - ) - } - - clusterViewModels.add( - AppListViewModel_() - .id(app.id) - .app(app) - .click { _ -> - callbacks.onAppClick(app) - } - ) - } else { - for (app in streamCluster.clusterAppList) { - clusterViewModels.add( - AppViewModel_() - .id(app.id) - .app(app) - .click { _ -> - callbacks.onAppClick(app) - } - .longClick { _ -> - callbacks.onAppLongClick(app) - false - } - .onBind { _, _, position -> - val itemCount = clusterViewModels.count() - if (itemCount >= 2) { - if (position == clusterViewModels.count() - 2) { - callbacks.onClusterScrolled(streamCluster) - Log.i(TAG, "Cluster ${streamCluster.clusterTitle} Scrolled") - } - } - } - ) - } - } - - if (screenshotsViewModels.isNotEmpty()) { - models.add( - CarouselHorizontalModel_() - .id("${idPrefix}_screenshots") - .models(screenshotsViewModels) - ) - } - - models.add( - CarouselHorizontalModel_() - .id("${idPrefix}_cluster") - .models(clusterViewModels) - ) - - return models - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ExodusView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ExodusView.kt deleted file mode 100644 index 8bdc12182..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ExodusView.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.epoxy.views.details - -import android.content.Context -import android.util.AttributeSet -import com.airbnb.epoxy.CallbackProp -import com.airbnb.epoxy.ModelProp -import com.airbnb.epoxy.ModelView -import com.airbnb.epoxy.OnViewRecycled -import com.aurora.store.data.model.ExodusTracker -import com.aurora.store.databinding.ViewExodusBinding -import com.aurora.store.view.epoxy.views.BaseModel -import com.aurora.store.view.epoxy.views.BaseView - -@ModelView( - autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT, - baseModelClass = BaseModel::class -) -class ExodusView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : BaseView(context, attrs, defStyleAttr) { - - @ModelProp - fun tracker(report: ExodusTracker) { - binding.txtTitle.text = report.name - binding.txtSubtitle.text = report.signature - binding.txtDescription.text = report.date - } - - @CallbackProp - fun click(onClickListener: OnClickListener?) { - binding.root.setOnClickListener(onClickListener) - } - - @OnViewRecycled - fun clear() { - } -} diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/FileView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/FileView.kt deleted file mode 100644 index 4ad237176..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/FileView.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.epoxy.views.details - -import android.content.Context -import android.util.AttributeSet -import com.airbnb.epoxy.ModelProp -import com.airbnb.epoxy.ModelView -import com.aurora.gplayapi.data.models.PlayFile -import com.aurora.store.databinding.ViewFileBinding -import com.aurora.store.util.CommonUtil -import com.aurora.store.view.epoxy.views.BaseModel -import com.aurora.store.view.epoxy.views.BaseView - -@ModelView( - autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT, - baseModelClass = BaseModel::class -) -class FileView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : BaseView(context, attrs, defStyleAttr) { - - @ModelProp - fun file(file: PlayFile) { - binding.line1.text = file.name - binding.line2.text = CommonUtil.addSiPrefix(file.size) - } -} diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/LargeScreenshotView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/LargeScreenshotView.kt deleted file mode 100644 index a3f6e6815..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/LargeScreenshotView.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.epoxy.views.details - -import android.content.Context -import android.content.res.Resources -import android.util.AttributeSet -import coil3.load -import coil3.request.placeholder -import com.airbnb.epoxy.ModelProp -import com.airbnb.epoxy.ModelView -import com.aurora.gplayapi.data.models.Artwork -import com.aurora.store.R -import com.aurora.store.databinding.ViewScreenshotLargeBinding -import com.aurora.store.view.epoxy.views.BaseModel -import com.aurora.store.view.epoxy.views.BaseView - -@ModelView( - autoLayout = ModelView.Size.MATCH_WIDTH_MATCH_HEIGHT, - baseModelClass = BaseModel::class -) -class LargeScreenshotView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : BaseView(context, attrs, defStyleAttr) { - - @ModelProp - fun artwork(artwork: Artwork) { - val displayMetrics = Resources.getSystem().displayMetrics - binding.img.load("${artwork.url}=rw-w${displayMetrics.widthPixels}-v1-e15") { - placeholder(R.drawable.bg_placeholder) - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/MiniScreenshotView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/MiniScreenshotView.kt deleted file mode 100644 index 9d5a4fba7..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/MiniScreenshotView.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.epoxy.views.details - -import android.content.Context -import android.util.AttributeSet -import coil3.load -import coil3.request.placeholder -import coil3.request.transformations -import coil3.transform.RoundedCornersTransformation -import com.airbnb.epoxy.CallbackProp -import com.airbnb.epoxy.ModelProp -import com.airbnb.epoxy.ModelView -import com.aurora.extensions.px -import com.aurora.gplayapi.data.models.Artwork -import com.aurora.store.R -import com.aurora.store.databinding.ViewScreenshotMiniBinding -import com.aurora.store.view.epoxy.views.BaseModel -import com.aurora.store.view.epoxy.views.BaseView - -@ModelView( - autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT, - baseModelClass = BaseModel::class -) -class MiniScreenshotView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : BaseView(context, attrs, defStyleAttr) { - - private var position: Int = 0 - - interface ScreenshotCallback { - fun onClick(position: Int = 0) - } - - @ModelProp - fun position(pos: Int) { - position = pos - } - - @ModelProp - fun artwork(artwork: Artwork) { - normalizeSize(artwork) - binding.img.load("${artwork.url}=rw-w480-v1-e15") { - placeholder(R.drawable.bg_rounded) - transformations(RoundedCornersTransformation(8.px.toFloat())) - } - } - - private fun normalizeSize(artwork: Artwork) { - if (artwork.height != 0 && artwork.width != 0) { - - val artworkHeight = artwork.height - val artworkWidth = artwork.width - - val normalizedHeight: Float - val normalizedWidth: Float - - when { - artworkHeight == artworkWidth -> { - normalizedHeight = 120f - normalizedWidth = 120f - } - - else -> { - val factor = artworkHeight / 120f - normalizedHeight = 120f - normalizedWidth = (artworkWidth / factor) - } - } - - binding.img.layoutParams.height = normalizedHeight.px.toInt() - binding.img.layoutParams.width = normalizedWidth.px.toInt() - binding.img.requestLayout() - } - } - - @CallbackProp - fun callback(screenshotCallback: ScreenshotCallback?) { - binding.img.setOnClickListener { - screenshotCallback?.onClick(position) - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ReviewView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ReviewView.kt deleted file mode 100644 index 59f1fc73d..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ReviewView.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.epoxy.views.details - -import android.content.Context -import android.util.AttributeSet -import coil3.load -import coil3.request.placeholder -import coil3.request.transformations -import coil3.transform.RoundedCornersTransformation -import com.airbnb.epoxy.CallbackProp -import com.airbnb.epoxy.ModelProp -import com.airbnb.epoxy.ModelView -import com.aurora.extensions.toDate -import com.aurora.gplayapi.data.models.Review -import com.aurora.store.R -import com.aurora.store.databinding.ViewReviewBinding -import com.aurora.store.view.epoxy.views.BaseModel -import com.aurora.store.view.epoxy.views.BaseView - -@ModelView( - autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT, - baseModelClass = BaseModel::class -) -class ReviewView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : BaseView(context, attrs, defStyleAttr) { - - @ModelProp - fun review(review: Review) { - binding.txtAuthor.text = review.userName - binding.txtTime.text = ("${review.timeStamp.toDate()} • v${review.appVersion}") - binding.txtComment.text = review.comment - - binding.img.load(review.userPhotoUrl) { - placeholder(R.drawable.bg_placeholder) - transformations(RoundedCornersTransformation(32F)) - } - - binding.rating.rating = review.rating.toFloat() - } - - @CallbackProp - fun click(onClickListener: OnClickListener?) { - binding.root.setOnClickListener(onClickListener) - } - - @CallbackProp - fun longClick(onClickListener: OnLongClickListener?) { - binding.root.setOnLongClickListener(onClickListener) - } -} diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ScreenshotView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/details/ScreenshotView.kt deleted file mode 100644 index cad000ac4..000000000 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/details/ScreenshotView.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.epoxy.views.details - -import android.content.Context -import android.util.AttributeSet -import coil3.load -import coil3.request.placeholder -import coil3.request.transformations -import coil3.transform.RoundedCornersTransformation -import com.airbnb.epoxy.CallbackProp -import com.airbnb.epoxy.ModelProp -import com.airbnb.epoxy.ModelView -import com.aurora.extensions.px -import com.aurora.gplayapi.data.models.Artwork -import com.aurora.store.R -import com.aurora.store.databinding.ViewScreenshotBinding -import com.aurora.store.view.epoxy.views.BaseModel -import com.aurora.store.view.epoxy.views.BaseView - -@ModelView( - autoLayout = ModelView.Size.WRAP_WIDTH_WRAP_HEIGHT, - baseModelClass = BaseModel::class -) -class ScreenshotView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : BaseView(context, attrs, defStyleAttr) { - - private var position: Int = 0 - - interface ScreenshotCallback { - fun onClick(position: Int = 0) - } - - @ModelProp - fun position(pos: Int) { - position = pos - } - - @ModelProp - fun artwork(artwork: Artwork) { - normalizeSize(artwork) - binding.img.load("${artwork.url}=rw-w480-v1-e15") { - placeholder(R.drawable.bg_rounded) - transformations(RoundedCornersTransformation(8.px.toFloat())) - } - } - - private fun normalizeSize(artwork: Artwork) { - if (artwork.height != 0 && artwork.width != 0) { - - val artworkHeight = artwork.height - val artworkWidth = artwork.width - - val normalizedHeight: Float - val normalizedWidth: Float - - when { - artworkHeight == artworkWidth -> { - normalizedHeight = 192f - normalizedWidth = 192f - } - - else -> { - val factor = artworkHeight / 192f - normalizedHeight = 192f - normalizedWidth = (artworkWidth / factor) - } - } - - binding.img.layoutParams.height = normalizedHeight.px.toInt() - binding.img.layoutParams.width = normalizedWidth.px.toInt() - binding.img.requestLayout() - } - } - - @CallbackProp - fun callback(screenshotCallback: ScreenshotCallback?) { - binding.img.setOnClickListener { - screenshotCallback?.onClick(position) - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt index 52c8e2a83..f0c541f14 100644 --- a/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt @@ -121,15 +121,6 @@ abstract class BaseFragment : Fragment() { ) } - fun openScreenshotFragment(app: App, position: Int) { - findNavController().navigate( - MobileNavigationDirections.actionGlobalScreenshotFragment( - position, - app.screenshots.toTypedArray() - ) - ) - } - fun openAppMenuSheet(app: MinimalApp) { findNavController().navigate(MobileNavigationDirections.actionGlobalAppMenuSheet(app)) } diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/ExpandedStreamBrowseFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/ExpandedStreamBrowseFragment.kt index c1be4599d..64855b553 100644 --- a/app/src/main/java/com/aurora/store/view/ui/commons/ExpandedStreamBrowseFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/commons/ExpandedStreamBrowseFragment.kt @@ -24,15 +24,11 @@ import android.view.View import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import com.airbnb.epoxy.EpoxyModel import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.store.databinding.FragmentGenericWithToolbarBinding import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener -import com.aurora.store.view.epoxy.groups.CarouselHorizontalModel_ import com.aurora.store.view.epoxy.views.AppProgressViewModel_ import com.aurora.store.view.epoxy.views.app.AppListViewModel_ -import com.aurora.store.view.epoxy.views.details.MiniScreenshotView -import com.aurora.store.view.epoxy.views.details.MiniScreenshotViewModel_ import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_ import com.aurora.store.viewmodel.browse.ExpandedStreamBrowseViewModel import dagger.hilt.android.AndroidEntryPoint @@ -92,30 +88,6 @@ class ExpandedStreamBrowseFragment : BaseFragment>() - - for ((position, artwork) in it.screenshots.withIndex()) { - screenshotsViewModels.add( - MiniScreenshotViewModel_() - .id(artwork.url) - .position(position) - .artwork(artwork) - .callback(object : MiniScreenshotView.ScreenshotCallback { - override fun onClick(position: Int) { - openScreenshotFragment(it, position) - } - }) - ) - } - - if (screenshotsViewModels.isNotEmpty()) { - add( - CarouselHorizontalModel_() - .id("${it.id}_screenshots") - .models(screenshotsViewModels) - ) - } - add( AppListViewModel_() .id(it.packageName.hashCode()) diff --git a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt index bf30e94ad..febf2ff39 100644 --- a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt @@ -63,7 +63,6 @@ import com.aurora.store.AppStreamStash import com.aurora.store.AuroraApp import com.aurora.store.MainActivity import com.aurora.store.R -import com.aurora.store.data.event.BusEvent import com.aurora.store.data.event.Event import com.aurora.store.data.event.InstallerEvent import com.aurora.store.data.installer.AppInstaller @@ -77,14 +76,11 @@ import com.aurora.store.util.CommonUtil import com.aurora.store.util.FlavouredUtil import com.aurora.store.util.PackageUtil import com.aurora.store.util.Preferences -import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED import com.aurora.store.util.ShortcutManagerUtil import com.aurora.store.view.custom.RatingView import com.aurora.store.view.epoxy.controller.DetailsCarouselController import com.aurora.store.view.epoxy.controller.GenericCarouselController -import com.aurora.store.view.epoxy.views.details.ScreenshotView -import com.aurora.store.view.epoxy.views.details.ScreenshotViewModel_ import com.aurora.store.view.ui.commons.BaseFragment import com.aurora.store.viewmodel.details.AppDetailsViewModel import com.aurora.store.viewmodel.details.DetailsClusterViewModel @@ -114,8 +110,6 @@ class AppDetailsFragment : BaseFragment() { private val isExtendedUpdateEnabled: Boolean get() = Preferences.getBoolean(requireContext(), PREFERENCE_UPDATES_EXTENDED) - private val showSimilarApps: Boolean - get() = Preferences.getBoolean(requireContext(), PREFERENCE_SIMILAR) private fun onEvent(event: Event) { when (event) { @@ -131,20 +125,6 @@ class AppDetailsFragment : BaseFragment() { } } - is BusEvent.ManualDownload -> { - if (app.packageName == event.packageName) { - val requestedApp = app.copy( - versionCode = event.versionCode, - dependencies = app.dependencies.copy( - dependentLibraries = app.dependencies.dependentLibraries.map { lib -> - lib.copy(versionCode = event.versionCode) - } - ) - ) - purchase(requestedApp) - } - } - is InstallerEvent.Failed -> { if (app.packageName == event.packageName) { findNavController().navigate( @@ -241,16 +221,6 @@ class AppDetailsFragment : BaseFragment() { text = "${report.trackers.size} ${getString(R.string.exodus_substring)} ${report.version}" } - - binding.layoutDetailsPrivacy.headerPrivacy.addClickListener { - findNavController().navigate( - AppDetailsFragmentDirections - .actionAppDetailsFragmentToDetailsExodusFragment( - app.displayName, - report - ) - ) - } } else { binding.layoutDetailsPrivacy.txtStatus.apply { setTextColor(ContextCompat.getColor(requireContext(), R.color.colorGreen)) @@ -618,16 +588,6 @@ class AppDetailsFragment : BaseFragment() { purchase(app) } } - - binding.layoutDetailsApp.btnSecondaryAction.apply { - text = getString(R.string.title_manual_download) - setOnClickListener { - findNavController().navigate( - AppDetailsFragmentDirections - .actionAppDetailsFragmentToManualDownloadSheet(app) - ) - } - } } // Lay out the toolbar again @@ -651,7 +611,6 @@ class AppDetailsFragment : BaseFragment() { } private fun updateExtraDetails(app: App) { - binding.viewFlipper.displayedChild = 1 updateAppDescription(app) updateAppRatingAndReviews(app) @@ -671,10 +630,6 @@ class AppDetailsFragment : BaseFragment() { updateBetaSubscription(app) } - if (showSimilarApps) { - updateAppStream(app) - } - checkAndSetupInstall() } @@ -706,45 +661,11 @@ class AppDetailsFragment : BaseFragment() { ) } } - - headerDescription.addClickListener { - findNavController().navigate( - AppDetailsFragmentDirections.actionAppDetailsFragmentToDetailsMoreFragment(app) - ) - } - - epoxyRecycler.withModels { - setFilterDuplicates(true) - var position = 0 - app.screenshots - //.sortedWith { o1, o2 -> o2.height.compareTo(o1.height) } - .forEach { artwork -> - add( - ScreenshotViewModel_() - .id(artwork.url) - .artwork(artwork) - .position(position++) - .callback(object : ScreenshotView.ScreenshotCallback { - override fun onClick(position: Int) { - openScreenshotFragment(app, position) - } - }) - ) - } - } } } private fun updateAppRatingAndReviews(app: App) { binding.layoutDetailsReview.apply { - headerRatingReviews.addClickListener { - findNavController().navigate( - AppDetailsFragmentDirections.actionAppDetailsFragmentToDetailsReviewFragment( - app.displayName, - app.packageName - ) - ) - } var totalStars = 0L totalStars += app.rating.oneStar @@ -833,15 +754,6 @@ class AppDetailsFragment : BaseFragment() { imgBeta.load(betaProgram.artwork.url) { } - - btnBetaAction.setOnClickListener { - btnBetaAction.text = getString(R.string.action_pending) - btnBetaAction.isEnabled = false - viewModel.fetchTestingProgramStatus( - app.packageName, - !betaProgram.isSubscribed - ) - } } else { root.hide() } @@ -956,7 +868,6 @@ class AppDetailsFragment : BaseFragment() { private fun updateCompatibilityInfo() { if (app.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS)) { - viewModel.fetchPlexusReport(app.packageName) binding.layoutDetailsCompatibility.txtGmsDependency.apply { title = getString(R.string.details_compatibility_gms_required_title) diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DetailsExodusFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/DetailsExodusFragment.kt deleted file mode 100644 index db0b6c0dd..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/details/DetailsExodusFragment.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.ui.details - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.viewModels -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import com.aurora.Constants -import com.aurora.extensions.browse -import com.aurora.store.R -import com.aurora.store.data.model.ExodusTracker -import com.aurora.store.data.model.Report -import com.aurora.store.databinding.FragmentGenericWithToolbarBinding -import com.aurora.store.view.epoxy.views.HeaderViewModel_ -import com.aurora.store.view.epoxy.views.details.ExodusViewModel_ -import com.aurora.store.view.ui.commons.BaseFragment -import com.aurora.store.viewmodel.details.DetailsExodusViewModel -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class DetailsExodusFragment : BaseFragment() { - - private val viewModel: DetailsExodusViewModel by viewModels() - private val args: DetailsExodusFragmentArgs by navArgs() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - // Toolbar - binding.toolbar.apply { - title = args.displayName - setNavigationOnClickListener { findNavController().navigateUp() } - } - - updateController(getExodusTrackersFromReport(args.report)) - } - - private fun updateController(reviews: List) { - binding.recycler.withModels { - add( - HeaderViewModel_() - .id("header") - .title(getString(R.string.exodus_view_report)) - .browseUrl("browse") - .click { _ -> context?.browse(Constants.EXODUS_REPORT_URL + args.report.id) } - ) - reviews.forEach { - add( - ExodusViewModel_() - .id(it.id) - .tracker(it) - .click { _ -> - context?.browse(it.url) - } - ) - } - } - } - - private fun getExodusTrackersFromReport(report: Report): List { - val trackerObjects = report.trackers.map { - viewModel.exodusTrackers.getJSONObject(it.toString()) - }.toList() - - return trackerObjects.map { - ExodusTracker( - id = it.getInt("id"), - name = it.getString("name"), - url = it.getString("website"), - signature = it.getString("code_signature"), - date = it.getString("creation_date"), - description = it.getString("description"), - networkSignature = it.getString("network_signature"), - documentation = listOf(it.getString("documentation")), - categories = listOf(it.getString("categories")) - ) - }.toList() - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DetailsMoreFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/DetailsMoreFragment.kt deleted file mode 100644 index 0651214c7..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/details/DetailsMoreFragment.kt +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.ui.details - -import android.os.Bundle -import android.view.View -import androidx.core.text.HtmlCompat -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import com.aurora.gplayapi.data.models.App -import com.aurora.store.R -import com.aurora.store.databinding.FragmentDetailsMoreBinding -import com.aurora.store.view.epoxy.views.HeaderViewModel_ -import com.aurora.store.view.epoxy.views.app.NoAppAltViewModel_ -import com.aurora.store.view.epoxy.views.details.AppDependentViewModel_ -import com.aurora.store.view.epoxy.views.details.FileViewModel_ -import com.aurora.store.view.epoxy.views.details.InfoViewModel_ -import com.aurora.store.view.epoxy.views.details.MoreBadgeViewModel_ -import com.aurora.store.view.ui.commons.BaseFragment -import com.aurora.store.viewmodel.details.DetailsMoreViewModel -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import java.util.UUID - -@AndroidEntryPoint -class DetailsMoreFragment : BaseFragment() { - - private val viewModel: DetailsMoreViewModel by viewModels() - private val args: DetailsMoreFragmentArgs by navArgs() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - // Adjust layout for edgeToEdge display - ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerMore) { layout, windowInsets -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()) - layout.setPadding(0, 0, 0, insets.bottom) - WindowInsetsCompat.CONSUMED - } - - // Toolbar - binding.toolbar.setOnClickListener { findNavController().navigateUp() } - - inflateDescription(args.app) - inflateFiles(args.app) - viewModel.fetchDependentApps(args.app) - - viewLifecycleOwner.lifecycleScope.launch { - viewModel.dependentApps.collect { list -> - if (list.isNotEmpty()) { - binding.recyclerDependency.withModels { - list.filter { it.displayName.isNotEmpty() }.forEach { - add( - AppDependentViewModel_() - .id(it.id) - .app(it) - .click { _ -> openDetailsFragment(it.packageName, it) } - ) - } - } - } else { - binding.recyclerDependency.withModels { - add( - NoAppAltViewModel_() - .id("no_app") - .message(getString(R.string.details_no_dependencies)) - ) - } - } - } - } - } - - private fun inflateDescription(app: App) { - binding.toolbar.title = app.displayName - binding.txtDescription.text = HtmlCompat.fromHtml( - app.description, - HtmlCompat.FROM_HTML_MODE_COMPACT - ) - } - - private fun inflateFiles(app: App) { - binding.recyclerMore.withModels { - //Add dependent files - if (app.fileList.isNotEmpty()) { - add( - HeaderViewModel_() - .id("badge_header") - .title("Files") - ) - - app.fileList.forEach { - add( - FileViewModel_() - .id(it.id) - .file(it) - ) - } - } - - //Add display & extra badges - if (app.infoBadges.isNotEmpty()) { - add( - HeaderViewModel_() - .id("badge_header") - .title("More") - ) - - app.infoBadges.forEach { - add( - MoreBadgeViewModel_() - .id(it.id) - .badge(it) - ) - } - - if (app.displayBadges.isNotEmpty()) { - app.displayBadges - .filter { it.textMajor.isNotEmpty() } - .forEach { - add( - MoreBadgeViewModel_() - .id(it.id) - .badge(it) - ) - } - } - } - - if (app.appInfo.appInfoMap.isNotEmpty()) { - add( - HeaderViewModel_() - .id("info_header") - .title("Info") - ) - app.appInfo.appInfoMap.forEach { - add( - InfoViewModel_() - .id(it.key) - .badge(it) - ) - } - add( - InfoViewModel_() - .id(UUID.randomUUID().toString()) - .badge(mapOf("TARGET" to "${app.targetSdk}").entries.first()) - ) - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DetailsReviewFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/DetailsReviewFragment.kt deleted file mode 100644 index 613ea9281..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/details/DetailsReviewFragment.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.ui.details - -import android.os.Bundle -import android.view.View -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.fragment.app.viewModels -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import com.aurora.gplayapi.data.models.Review -import com.aurora.gplayapi.data.models.ReviewCluster -import com.aurora.store.R -import com.aurora.store.databinding.FragmentDetailsReviewBinding -import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener -import com.aurora.store.view.epoxy.views.AppProgressViewModel_ -import com.aurora.store.view.epoxy.views.details.ReviewViewModel_ -import com.aurora.store.view.ui.commons.BaseFragment -import com.aurora.store.viewmodel.review.ReviewViewModel -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class DetailsReviewFragment : BaseFragment() { - - private val args: DetailsReviewFragmentArgs by navArgs() - private val viewModel: ReviewViewModel by viewModels() - - private lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener - private lateinit var filter: Review.Filter - private lateinit var reviewCluster: ReviewCluster - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - // Adjust layout for edgeToEdge display - ViewCompat.setOnApplyWindowInsetsListener(binding.recycler) { layout, windowInsets -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars()) - layout.setPadding(0, 0, 0, insets.bottom) - WindowInsetsCompat.CONSUMED - } - - // Toolbar - binding.toolbar.apply { - title = args.displayName - setNavigationOnClickListener { findNavController().navigateUp() } - } - - viewModel.liveData.observe(viewLifecycleOwner) { - if (!::reviewCluster.isInitialized) { - endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() { - override fun onLoadMore(currentPage: Int) { - if (::reviewCluster.isInitialized) { - viewModel.next(reviewCluster.nextPageUrl) - } - } - } - binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener) - } - - it?.let { - reviewCluster = it - updateController(reviewCluster) - } - } - - // Fetch Reviews - filter = Review.Filter.ALL - viewModel.fetchReview(args.packageName, filter) - - // Chips - binding.chipGroup.setOnCheckedStateChangeListener { _, checkedIds -> - when (checkedIds[0]) { - R.id.filter_review_all -> filter = Review.Filter.ALL - R.id.filter_newest_first -> filter = Review.Filter.NEWEST - R.id.filter_review_critical -> filter = Review.Filter.CRITICAL - R.id.filter_review_positive -> filter = Review.Filter.POSITIVE - R.id.filter_review_five -> filter = Review.Filter.FIVE - R.id.filter_review_four -> filter = Review.Filter.FOUR - R.id.filter_review_three -> filter = Review.Filter.THREE - R.id.filter_review_two -> filter = Review.Filter.TWO - R.id.filter_review_one -> filter = Review.Filter.ONE - } - - endlessRecyclerOnScrollListener.resetPageCount() - viewModel.fetchReview(args.packageName, filter) - } - } - - private fun updateController(reviewCluster: ReviewCluster) { - binding.recycler.withModels { - setFilterDuplicates(true) - reviewCluster.reviewList.forEach { - add( - ReviewViewModel_() - .id(it.commentId) - .review(it) - ) - } - - if (reviewCluster.hasNext()) { - add( - AppProgressViewModel_() - .id("progress") - ) - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/details/DevProfileFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/DevProfileFragment.kt index 96bcc60e8..1296ab6ee 100644 --- a/app/src/main/java/com/aurora/store/view/ui/details/DevProfileFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/details/DevProfileFragment.kt @@ -31,7 +31,6 @@ import com.aurora.gplayapi.data.models.details.DevStream import com.aurora.store.R import com.aurora.store.data.model.ViewState import com.aurora.store.databinding.FragmentDevProfileBinding -import com.aurora.store.view.epoxy.controller.DeveloperCarouselController import com.aurora.store.view.epoxy.controller.GenericCarouselController import com.aurora.store.view.ui.commons.BaseFragment import com.aurora.store.viewmodel.details.DevProfileViewModel @@ -47,7 +46,6 @@ class DevProfileFragment : BaseFragment(), override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val developerCarouselController = DeveloperCarouselController(this) // Toolbar binding.toolbar.apply { @@ -56,7 +54,6 @@ class DevProfileFragment : BaseFragment(), } // RecyclerView - binding.recycler.setController(developerCarouselController) viewModel.liveData.observe(viewLifecycleOwner) { when (it) { @@ -82,7 +79,6 @@ class DevProfileFragment : BaseFragment(), binding.txtDevDescription.text = description binding.imgIcon.load(imgUrl) binding.viewFlipper.displayedChild = 0 - developerCarouselController.setData(streamBundle) } } } diff --git a/app/src/main/java/com/aurora/store/view/ui/details/ScreenshotFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/ScreenshotFragment.kt deleted file mode 100644 index 628280d11..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/details/ScreenshotFragment.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.view.ui.details - -import android.os.Bundle -import android.view.View -import androidx.core.content.ContextCompat -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.PagerSnapHelper -import androidx.recyclerview.widget.RecyclerView -import com.aurora.gplayapi.data.models.Artwork -import com.aurora.store.R -import com.aurora.store.databinding.FragmentScreenshotBinding -import com.aurora.store.view.epoxy.views.details.LargeScreenshotViewModel_ -import com.aurora.store.view.ui.commons.BaseFragment -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class ScreenshotFragment : BaseFragment() { - - private val args: ScreenshotFragmentArgs by navArgs() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - // Toolbar - binding.toolbar.apply { - elevation = 0f - navigationIcon = ContextCompat.getDrawable(view.context, R.drawable.ic_arrow_back) - setNavigationOnClickListener { findNavController().navigateUp() } - } - - // Recycler View - binding.recyclerView.apply { - layoutManager = LinearLayoutManager(view.context, RecyclerView.HORIZONTAL, false) - PagerSnapHelper().attachToRecyclerView(this) - } - - updateController(args.arrayOfArtwork, args.position) - } - - private fun updateController(artworks: Array, position: Int) { - binding.recyclerView.withModels { - artworks.forEach { - add( - LargeScreenshotViewModel_() - .id(it.url) - .artwork(it) - ) - } - binding.recyclerView.scrollToPosition(position) - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/ManualDownloadSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/ManualDownloadSheet.kt deleted file mode 100644 index 2ba21f3be..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/sheets/ManualDownloadSheet.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2019, 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 . - * - * - */ -package com.aurora.store.view.ui.sheets - -import android.app.Dialog -import android.os.Bundle -import android.view.View -import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.navArgs -import coil3.load -import coil3.request.placeholder -import coil3.request.transformations -import coil3.transform.RoundedCornersTransformation -import com.aurora.extensions.toast -import com.aurora.store.R -import com.aurora.store.databinding.SheetManualDownloadBinding -import com.aurora.store.viewmodel.sheets.ManualDownloadViewModel -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch - -@AndroidEntryPoint -class ManualDownloadSheet : BaseDialogSheet() { - private val viewModel: ManualDownloadViewModel by viewModels() - private val args: ManualDownloadSheetArgs by navArgs() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - isCancelable = false - - inflateData() - attachActions() - - viewLifecycleOwner.lifecycleScope.launch { - viewModel.purchaseStatus.collectLatest { - if (it) { - toast(R.string.toast_manual_available) - dismissAllowingStateLoss() - } else { - toast(R.string.toast_manual_unavailable) - } - } - } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - this.lifecycleScope.launch { - viewModel.purchaseStatus.collectLatest { - if (it) { - toast(R.string.toast_manual_available) - dismissAllowingStateLoss() - } else { - toast(R.string.toast_manual_unavailable) - } - } - } - return super.onCreateDialog(savedInstanceState) - } - - private fun inflateData() { - binding.imgIcon.load(args.app.iconArtwork.url) { - placeholder(R.drawable.bg_placeholder) - transformations(RoundedCornersTransformation(32F)) - } - - binding.txtLine1.text = args.app.displayName - binding.txtLine2.text = args.app.packageName - binding.txtLine3.text = ("${args.app.versionName} (${args.app.versionCode})") - - binding.versionCodeLayout.hint = "${args.app.versionCode}" - binding.versionCodeLayout.editText?.setText("${args.app.versionCode}") - } - - private fun attachActions() { - binding.btnPrimary.setOnClickListener { - val customVersionString = (binding.versionCodeInp.text).toString() - if (customVersionString.isEmpty()) - binding.versionCodeInp.error = "Enter version code" - else { - viewModel.purchase(args.app, customVersionString.toLong()) - } - } - - binding.btnSecondary.setOnClickListener { - dismissAllowingStateLoss() - } - } -} diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt index 9a5029b7b..7b155813a 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt @@ -7,11 +7,10 @@ package com.aurora.store.viewmodel.details import android.content.Context +import android.content.pm.PackageInstaller import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import androidx.paging.PagingData -import androidx.paging.cachedIn import com.aurora.Constants import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.Review @@ -24,11 +23,9 @@ import com.aurora.gplayapi.network.IHttpClient import com.aurora.store.BuildConfig import com.aurora.store.data.helper.DownloadHelper import com.aurora.store.data.model.ExodusReport -import com.aurora.store.data.model.ExodusTracker import com.aurora.store.data.model.PlexusReport import com.aurora.store.data.model.Report import com.aurora.store.data.model.Scores -import com.aurora.store.data.paging.GenericPagingSource.Companion.createPager import com.aurora.store.data.providers.AuthProvider import com.aurora.store.data.room.favourite.Favourite import com.aurora.store.data.room.favourite.FavouriteDao @@ -36,20 +33,18 @@ import com.aurora.store.util.CertUtil import com.aurora.store.util.PackageUtil import com.aurora.store.util.PackageUtil.PACKAGE_NAME_GMS import com.aurora.store.util.Preferences -import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.serialization.json.Json @@ -68,8 +63,7 @@ class AppDetailsViewModel @Inject constructor( private val downloadHelper: DownloadHelper, private val favouriteDao: FavouriteDao, private val httpClient: IHttpClient, - private val json: Json, - private val exodusTrackers: JSONObject + private val json: Json ) : ViewModel() { private val TAG = AppDetailsViewModel::class.java.simpleName @@ -77,9 +71,8 @@ class AppDetailsViewModel @Inject constructor( private val _app = MutableStateFlow(App("")) val app = _app.asStateFlow() - private var reviewsNextPageUrl: String? = null - private val _reviews = MutableStateFlow>(PagingData.empty()) - val reviews = _reviews.asStateFlow() + private val _suggestions = MutableStateFlow>(emptyList()) + val suggestions = _suggestions.asStateFlow() private val _userReview = MutableStateFlow(null) val userReview = _userReview.asStateFlow() @@ -107,6 +100,27 @@ class AppDetailsViewModel @Inject constructor( list.find { d -> d.packageName == a?.packageName } }.stateIn(viewModelScope, SharingStarted.Eagerly, null) + // TODO: Save sessionId to downloads to monitor packageInstall progress + val installProgress = callbackFlow { + val packageInstaller = context.packageManager.packageInstaller + val callback = object : PackageInstaller.SessionCallback() { + override fun onActiveChanged(p0: Int, p1: Boolean) {} + override fun onBadgingChanged(p0: Int) {} + override fun onCreated(p0: Int) { + // trySend(0F) + } + override fun onFinished(p0: Int, p1: Boolean) { +// trySend(null) + } + override fun onProgressChanged(p0: Int, p1: Float) { +// trySend(p1) + } + } + + packageInstaller.registerSessionCallback(callback) + awaitClose { packageInstaller.unregisterSessionCallback(callback) } + }.stateIn(viewModelScope, SharingStarted.Eagerly, null) + private val isExtendedUpdateEnabled: Boolean get() = Preferences.getBoolean(context, PREFERENCE_UPDATES_EXTENDED) @@ -114,7 +128,7 @@ class AppDetailsViewModel @Inject constructor( get() = PackageUtil.isUpdatable( context, app.value!!.packageName, - app.value!!.versionCode.toLong() + app.value!!.versionCode ) private val hasValidCerts: Boolean @@ -128,15 +142,9 @@ class AppDetailsViewModel @Inject constructor( val hasValidUpdate: Boolean get() = (isUpdatable && hasValidCerts) || (isUpdatable && isExtendedUpdateEnabled) - val showSimilarApps: Boolean - get() = Preferences.getBoolean(context, PREFERENCE_SIMILAR) - fun fetchAppDetails(packageName: String) { viewModelScope.launch(Dispatchers.IO) { try { - _favourite.value = favouriteDao.isFavourite(packageName) - - _dataSafetyReport.value = webDataSafetyHelper.fetch(packageName) _app.value = appDetailsHelper.getAppByPackageName(packageName).copy( isInstalled = PackageUtil.isInstalled(context, packageName) ) @@ -148,7 +156,9 @@ class AppDetailsViewModel @Inject constructor( // Only proceed if there was no error while fetching the app details if (throwable != null) return@invokeOnCompletion - fetchReviews() + fetchFavourite(packageName) + fetchDataSafetyReport(packageName) + fetchSuggestions() fetchExodusPrivacyReport(packageName) if (app.value!!.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS)) { fetchPlexusReport(packageName) @@ -156,48 +166,11 @@ class AppDetailsViewModel @Inject constructor( } } - fun fetchReviews(filter: Review.Filter = Review.Filter.ALL) { - reviewsNextPageUrl = null - - createPager { - val reviewsCluster = reviewsNextPageUrl?.let { nextPageUrl -> - reviewsHelper.next(nextPageUrl) - } ?: reviewsHelper.getReviews(app.value!!.packageName, filter) - - reviewsNextPageUrl = reviewsCluster.nextPageUrl - reviewsCluster.reviewList - }.flow.distinctUntilChanged() - .cachedIn(viewModelScope) - .onEach { _reviews.value = it } - .launchIn(viewModelScope) - } - - private fun fetchExodusPrivacyReport(packageName: String) { + fun updateTestingProgramStatus(packageName: String, subscribe: Boolean) { viewModelScope.launch(Dispatchers.IO) { try { - _exodusReport.value = getLatestExodusReport(packageName) - } catch (exception: Exception) { - Log.e(TAG, "Failed to fetch privacy report", exception) - _exodusReport.value = null - } - } - } - - fun fetchPlexusReport(packageName: String) { - viewModelScope.launch(Dispatchers.IO) { - try { - _plexusScores.value = getPlexusReport(packageName)?.report?.scores - } catch (exception: Exception) { - Log.e(TAG, "Failed to fetch compatibility report", exception) - _plexusScores.value = null - } - } - } - - fun fetchTestingProgramStatus(packageName: String, subscribe: Boolean) { - viewModelScope.launch(Dispatchers.IO) { - try { - _testingProgramStatus.value = appDetailsHelper.testingProgram(packageName, subscribe) + _testingProgramStatus.value = + appDetailsHelper.testingProgram(packageName, subscribe) } catch (exception: Exception) { Log.e(TAG, "Failed to fetch testing program status", exception) } @@ -272,24 +245,50 @@ class AppDetailsViewModel @Inject constructor( } } - fun getExodusTrackersFromReport(): List { - val trackerObjects = exodusReport.value!!.trackers.map { - exodusTrackers.getJSONObject(it.toString()) - }.toList() + private fun fetchFavourite(packageName: String) { + viewModelScope.launch(Dispatchers.IO) { + _favourite.value = favouriteDao.isFavourite(packageName) + } + } - return trackerObjects.map { - ExodusTracker( - id = it.getInt("id"), - name = it.getString("name"), - url = it.getString("website"), - signature = it.getString("code_signature"), - date = it.getString("creation_date"), - description = it.getString("description"), - networkSignature = it.getString("network_signature"), - documentation = listOf(it.getString("documentation")), - categories = listOf(it.getString("categories")) - ) - }.toList() + private fun fetchDataSafetyReport(packageName: String) { + viewModelScope.launch(Dispatchers.IO) { + _dataSafetyReport.value = webDataSafetyHelper.fetch(packageName) + } + } + + private fun fetchSuggestions() { + // Bail out if we go no suggestions to offer + if (app.value!!.detailsStreamUrl.isNullOrBlank()) return + + viewModelScope.launch(Dispatchers.IO) { + val streamBundle = appDetailsHelper.getDetailsStream(app.value!!.detailsStreamUrl!!) + _suggestions.value = streamBundle.streamClusters.values + .flatMap { it.clusterAppList } + .distinctBy { it.packageName } + } + } + + private fun fetchExodusPrivacyReport(packageName: String) { + viewModelScope.launch(Dispatchers.IO) { + try { + _exodusReport.value = getLatestExodusReport(packageName) + } catch (exception: Exception) { + Log.e(TAG, "Failed to fetch privacy report", exception) + _exodusReport.value = null + } + } + } + + private fun fetchPlexusReport(packageName: String) { + viewModelScope.launch(Dispatchers.IO) { + try { + _plexusScores.value = getPlexusReport(packageName)?.report?.scores + } catch (exception: Exception) { + Log.e(TAG, "Failed to fetch compatibility report", exception) + _plexusScores.value = null + } + } } private fun getLatestExodusReport(packageName: String): Report? { @@ -305,12 +304,6 @@ class AppDetailsViewModel @Inject constructor( .firstOrNull() } - private fun getPlexusReport(packageName: String): PlexusReport? { - val url = "${Constants.PLEXUS_API_URL}/${packageName}/?scores=true" - val playResponse = httpClient.get(url, emptyMap()) - return json.decodeFromString(String(playResponse.responseBytes)) - } - private fun parseExodusResponse(response: String, packageName: String): List { try { val jsonObject = JSONObject(response) @@ -322,4 +315,10 @@ class AppDetailsViewModel @Inject constructor( return emptyList() } } + + private fun getPlexusReport(packageName: String): PlexusReport? { + val url = "${Constants.PLEXUS_API_URL}/${packageName}/?scores=true" + val playResponse = httpClient.get(url, emptyMap()) + return json.decodeFromString(String(playResponse.responseBytes)) + } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/DetailsExodusViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/DetailsExodusViewModel.kt index 51d03b993..9493e9280 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/DetailsExodusViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/DetailsExodusViewModel.kt @@ -6,9 +6,56 @@ package com.aurora.store.viewmodel.details import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.aurora.store.data.model.ExodusTracker +import com.aurora.store.data.model.Report +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch import org.json.JSONObject -import javax.inject.Inject -@HiltViewModel -class DetailsExodusViewModel @Inject constructor(val exodusTrackers: JSONObject) : ViewModel() +@HiltViewModel(assistedFactory = DetailsExodusViewModel.Factory::class) +class DetailsExodusViewModel @AssistedInject constructor( + @Assisted private val exodusReport: Report, + private val exodusTrackers: JSONObject +) : ViewModel() { + + @AssistedFactory + interface Factory { + fun create(exodusReport: Report): DetailsExodusViewModel + } + + private val _trackers = MutableStateFlow>(emptyList()) + val trackers = _trackers.asStateFlow() + + init { + getExodusTrackersFromReport() + } + + fun getExodusTrackersFromReport() { + viewModelScope.launch(Dispatchers.IO) { + val trackerObjects = exodusReport.trackers.map { + exodusTrackers.getJSONObject(it.toString()) + }.toList() + + _trackers.value = trackerObjects.map { + ExodusTracker( + id = it.getInt("id"), + name = it.getString("name"), + url = it.getString("website"), + signature = it.getString("code_signature"), + date = it.getString("creation_date"), + description = it.getString("description"), + networkSignature = it.getString("network_signature"), + documentation = listOf(it.getString("documentation")), + categories = listOf(it.getString("categories")) + ) + }.toList() + } + } +} diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/DetailsMoreViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/DetailsMoreViewModel.kt index 603f822ca..c8d277338 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/DetailsMoreViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/DetailsMoreViewModel.kt @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2023-2025 The Calyx Institute + * SPDX-FileCopyrightText: 2024 Aurora OSS + * SPDX-License-Identifier: GPL-3.0-or-later + */ + package com.aurora.store.viewmodel.details import android.util.Log @@ -5,36 +11,42 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.helpers.AppDetailsHelper +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch -import javax.inject.Inject -@HiltViewModel -class DetailsMoreViewModel @Inject constructor( +@HiltViewModel(assistedFactory = DetailsMoreViewModel.Factory::class) +class DetailsMoreViewModel @AssistedInject constructor( + @Assisted private val dependencies: List, private val appDetailsHelper: AppDetailsHelper ) : ViewModel() { + @AssistedFactory + interface Factory { + fun create(dependencies: List): DetailsMoreViewModel + } + private val TAG = DetailsMoreViewModel::class.java.simpleName - private val dependantAppsStash = mutableMapOf>() - private val _dependentApps = MutableSharedFlow>() - val dependentApps = _dependentApps.asSharedFlow() + private val _dependentApps = MutableStateFlow?>(emptyList()) + val dependentApps = _dependentApps.asStateFlow() - fun fetchDependentApps(app: App) { + init { + fetchDependencies() + } + + private fun fetchDependencies() { viewModelScope.launch(Dispatchers.IO) { try { - val dependantApps = dependantAppsStash.getOrPut(app.packageName) { - appDetailsHelper.getAppByPackageName(app.dependencies.dependentPackages) - } - - _dependentApps.emit(dependantApps) + _dependentApps.value = appDetailsHelper.getAppByPackageName(dependencies) } catch (exception: Exception) { Log.e(TAG, "Failed to fetch dependencies", exception) - dependantAppsStash[app.packageName] = emptyList() - _dependentApps.emit(emptyList()) + _dependentApps.value = null } } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/review/DetailsReviewViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/review/DetailsReviewViewModel.kt new file mode 100644 index 000000000..cb550a67f --- /dev/null +++ b/app/src/main/java/com/aurora/store/viewmodel/review/DetailsReviewViewModel.kt @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 AuroraOSS + * SPDX-FileCopyrightText: 2024-2025 The Calyx Institute + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.store.viewmodel.review + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn +import com.aurora.gplayapi.data.models.Review +import com.aurora.gplayapi.helpers.ReviewsHelper +import com.aurora.store.data.paging.GenericPagingSource.Companion.createPager +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import dagger.hilt.android.lifecycle.HiltViewModel +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(assistedFactory = DetailsReviewViewModel.Factory::class) +class DetailsReviewViewModel @AssistedInject constructor( + @Assisted private val packageName: String, + private val reviewsHelper: ReviewsHelper +) : ViewModel() { + + @AssistedFactory + interface Factory { + fun create(packageName: String): DetailsReviewViewModel + } + + private val TAG = DetailsReviewViewModel::class.java.simpleName + + private var reviewsNextPageUrl: String? = null + private val _reviews = MutableStateFlow>(PagingData.empty()) + val reviews = _reviews.asStateFlow() + + init { + fetchReviews() + } + + fun fetchReviews(filter: Review.Filter = Review.Filter.ALL) { + reviewsNextPageUrl = null + + createPager { page -> + try { + when (page) { + 1 -> reviewsHelper.getReviews(packageName, filter).also { + reviewsNextPageUrl = it.nextPageUrl + }.reviewList + + else -> { + if (!reviewsNextPageUrl.isNullOrBlank()) { + reviewsHelper.next(reviewsNextPageUrl!!).also { + reviewsNextPageUrl = it.nextPageUrl + }.reviewList + } else { + emptyList() + } + } + } + } catch (exception: Exception) { + Log.e(TAG, "Failed to fetch reviews for $page: $reviewsNextPageUrl", exception) + emptyList() + } + }.flow.distinctUntilChanged() + .cachedIn(viewModelScope) + .onEach { _reviews.value = it } + .launchIn(viewModelScope) + } +} diff --git a/app/src/main/java/com/aurora/store/viewmodel/review/ReviewViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/review/ReviewViewModel.kt deleted file mode 100644 index 140f4a402..000000000 --- a/app/src/main/java/com/aurora/store/viewmodel/review/ReviewViewModel.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 . - * - */ - -package com.aurora.store.viewmodel.review - -import android.util.Log -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.aurora.gplayapi.data.models.Review -import com.aurora.gplayapi.data.models.ReviewCluster -import com.aurora.gplayapi.helpers.ReviewsHelper -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class ReviewViewModel @Inject constructor( - private val reviewsHelper: ReviewsHelper -) : ViewModel() { - val TAG = javaClass.simpleName - - val liveData: MutableLiveData = MutableLiveData() - - private lateinit var reviewsCluster: ReviewCluster - - fun fetchReview(packageName: String, filter: Review.Filter) { - viewModelScope.launch(Dispatchers.IO) { - try { - reviewsCluster = reviewsHelper.getReviews(packageName, filter) - liveData.postValue(reviewsCluster) - } catch (e: Exception) { - Log.e(TAG, "Failed to fetch reviews", e) - } - } - } - - fun next(nextReviewPageUrl: String) { - viewModelScope.launch(Dispatchers.IO) { - try { - val currentCluster = reviewsCluster - val nextReviewCluster = reviewsHelper.next(nextReviewPageUrl) - - reviewsCluster = currentCluster.copy( - nextPageUrl = nextReviewCluster.nextPageUrl, - reviewList = currentCluster.reviewList + nextReviewCluster.reviewList - ) - - liveData.postValue(reviewsCluster) - } catch (e: Exception) { - Log.e(TAG, "Failed to fetch next reviews $nextReviewPageUrl", e) - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/viewmodel/sheets/ManualDownloadViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/sheets/ManualDownloadViewModel.kt deleted file mode 100644 index d7fb25fec..000000000 --- a/app/src/main/java/com/aurora/store/viewmodel/sheets/ManualDownloadViewModel.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.aurora.store.viewmodel.sheets - -import android.util.Log -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.aurora.gplayapi.data.models.App -import com.aurora.gplayapi.helpers.PurchaseHelper -import com.aurora.store.AuroraApp -import com.aurora.store.data.event.BusEvent -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class ManualDownloadViewModel @Inject constructor( - private val purchaseHelper: PurchaseHelper -) : ViewModel() { - - private val TAG = ManualDownloadViewModel::class.java.simpleName - - private val _purchaseStatus = MutableSharedFlow() - val purchaseStatus = _purchaseStatus.asSharedFlow() - - fun purchase(app: App, customVersion: Long) { - viewModelScope.launch(Dispatchers.IO) { - try { - val files = purchaseHelper.purchase(app.packageName, customVersion, app.offerType) - if (files.isNotEmpty()) { - AuroraApp.events.send( - BusEvent.ManualDownload(app.packageName, customVersion) - ) - } - _purchaseStatus.emit(files.isNotEmpty()) - } catch (exception: Exception) { - _purchaseStatus.emit(false) - Log.e(TAG, "Failed to find version: $customVersion", exception) - } - } - } -} diff --git a/app/src/main/res/drawable/ic_experiment.xml b/app/src/main/res/drawable/ic_experiment.xml new file mode 100644 index 000000000..12fe3025d --- /dev/null +++ b/app/src/main/res/drawable/ic_experiment.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_suggestions.xml b/app/src/main/res/drawable/ic_suggestions.xml new file mode 100644 index 000000000..39cb6d7da --- /dev/null +++ b/app/src/main/res/drawable/ic_suggestions.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 87493c1cb..1219466bc 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -56,74 +56,57 @@ android:id="@+id/layout_details_app" layout="@layout/layout_details_app" /> - + android:layout_height="wrap_content" + android:divider="@drawable/divider" + android:orientation="vertical" + android:showDividers="middle"> - + + + + + + - - - - - + android:overScrollMode="never" + android:scrollbars="none" + app:itemSpacing="@dimen/margin_small" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/view_app" /> - + - + - + - + - - - - - - - - - - - + + diff --git a/app/src/main/res/layout/fragment_details_more.xml b/app/src/main/res/layout/fragment_details_more.xml deleted file mode 100644 index 174611bea..000000000 --- a/app/src/main/res/layout/fragment_details_more.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_screenshot.xml b/app/src/main/res/layout/fragment_screenshot.xml deleted file mode 100644 index 23483cc48..000000000 --- a/app/src/main/res/layout/fragment_screenshot.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/model_developer_carousel_group.xml b/app/src/main/res/layout/model_developer_carousel_group.xml deleted file mode 100644 index 8804d25f5..000000000 --- a/app/src/main/res/layout/model_developer_carousel_group.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/sheet_manual_download.xml b/app/src/main/res/layout/sheet_manual_download.xml deleted file mode 100644 index 3ec96446b..000000000 --- a/app/src/main/res/layout/sheet_manual_download.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/view_exodus.xml b/app/src/main/res/layout/view_exodus.xml deleted file mode 100644 index adc565460..000000000 --- a/app/src/main/res/layout/view_exodus.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/layout/view_file.xml b/app/src/main/res/layout/view_file.xml deleted file mode 100644 index c248993fe..000000000 --- a/app/src/main/res/layout/view_file.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/view_review.xml b/app/src/main/res/layout/view_review.xml deleted file mode 100644 index c786db62a..000000000 --- a/app/src/main/res/layout/view_review.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/view_screenshot.xml b/app/src/main/res/layout/view_screenshot.xml deleted file mode 100644 index 5a6f384f8..000000000 --- a/app/src/main/res/layout/view_screenshot.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/view_screenshot_large.xml b/app/src/main/res/layout/view_screenshot_large.xml deleted file mode 100644 index 51d05c47e..000000000 --- a/app/src/main/res/layout/view_screenshot_large.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/view_screenshot_mini.xml b/app/src/main/res/layout/view_screenshot_mini.xml deleted file mode 100644 index 96eca2c8f..000000000 --- a/app/src/main/res/layout/view_screenshot_mini.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 56816d9be..c53ea63d5 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -32,9 +32,6 @@ - @@ -165,18 +162,11 @@ - - - - + @@ -209,17 +199,6 @@ android:name="expandedStreamUrl" app:argType="string" /> - - - - - - - - - - - - - - - - - - المكتبة تأكد من إعادة تسجيل الدخول وإعادة تشغيل التطبيق لتطبيق التغييرات. الشبكات - عرض مجموعات متشابهة وذات صلة في صفحة تفاصيل التطبيق تطبيقات مشابهة و ذات صلة عرض صفحات لك على الشاشة الرئيسية صفحات لك diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 8838dc34f..8df6c0caa 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -180,7 +180,6 @@ Instalador AM Seleiciona\'l métodu d\'instalación de los archivos APK Aplicaciones asemeyaes y rellacionaes - Amuesa les aplicaciones asemeyaes y rellacionaes nes páxines de detalles de les aplicaciones Pa ti Instalóse App Installer diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 558e17250..9a310da8c 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -179,7 +179,6 @@ Tərtibat İlkin səhifəni seç Oxşar və əlaqəli tətbiqlər - Tətbiq təfərrüatları səhifəsində oxşar və əlaqəli qrupları göstər Endirmə Alınmadı Tətbiq satın alınmayıb Tətbiq dəstəklənmir diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 8fb9d2721..d1fc2da12 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -118,7 +118,6 @@ URL проксі Увядзіце сапраўдны URL для праходжання ўсяго трафіку праз проксі. Падобнае - Паказваць падобныя ці звязаныя кластары на старонцы праграмы Не правяраць наяўнасць абнаўленняў для праграм, усталяваных з крыніц па-за межамі крамы Aurora Памылка спампоўкі Не ўдалося атрымаць файл diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 1d7294d7f..9f1410fd4 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -139,7 +139,6 @@ Услуги на Aurora Изберете режим на инсталиране на APK Показва страници за вас на началния екран - Показва подобни и свързани клъстери на страницата с подробности за приложението Изтеглянето се провали Приложението не се поддържа Приложението не е намерено diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 36aa9c127..14ef16942 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -176,7 +176,6 @@ Aplicacions similars i relacionades Aplicació no compatible No s\'ha trobat l\'aplicació - Mostrar grups similars i relacionats a la pàgina detalls de l\'aplicació Error en descarregar Gestor de llistes negres Visualitzar les Pàgines per a tu a la pantalla d\'inici diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 4c3d063d5..88b2daee3 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -51,7 +51,6 @@ Aplikace není kompatibilní Aplikace nebyla zakoupena Stažení se nezdařilo - Zobrazit podobné a související skupiny na stránce s detaily aplikace Podobné a související aplikace Zobrazit stránky pro vás na domovské obrazovce Stránky pro vás diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 3185f974d..eac331aa6 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -105,7 +105,6 @@ AM-installationsprogram Layout Appen blev ikke fundet - Vis lignende og relaterede samlinger på appens detaljeside Sørg for at logge ind igen for at anvende spoofen Manuel download Hvidlistet diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0f8441eaa..0fdd4d597 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -208,7 +208,6 @@ App-Käufe sind für anonyme Konten nicht verfügbar. Bitte neu anmelden und die App neu starten, um die Änderungen zu übernehmen. Netzwerk - Ähnliche und verwandte Apps in der App-Detailansicht anzeigen Ähnliche Apps „Für dich“-Tab auf der Startseite anzeigen „Für dich“-Tab diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 90c717fd1..eff250f24 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -168,7 +168,6 @@ Προσαρμογή Εμφάνιση σελίδων για εσάς στην αρχική οθόνη Η εφαρμογή δεν υποστηρίζεται - Εμφάνιση παρόμοιων και σχετικών ομάδων στη σελίδα λεπτομερειών εφαρμογής Η συνεδρία έληξε, συνδεθείτε ξανά για να λάβετε νέα. Κορυφαίες επί πληρωμή Συσκευή diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index fc262532d..ae364befe 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -176,7 +176,6 @@ ankoraŭ %1$d m %2$d s Agordo \"Por vi\" paĝoj - Vidigi similajn kaj rilatajn apojn sur la paĝo de la apon detaloj Shizuku instalilo AM instalilo Peti novan analizon diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index b3b980584..46d3d2c71 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -211,7 +211,6 @@ Felicidades, el código de la versión solicitada está disponible, descargándolo ahora. El código de versión que estás solicitando no está disponible. Descarga manual - Mostrar grupos similares y relacionados en la página de detalles de la aplicación Aplicaciones similares y relacionadas Mostrar las páginas Para ti en la pantalla de inicio Páginas para ti diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 701d706ff..3af861bb9 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -201,7 +201,6 @@ Uuenduste märguanded Vajalikud load on keelatud. Toimingu jätkamiseks luba need Automaatsed uuendused - Kuva rakenduse üksikasjade lehel sarnaseid ja seotud rakendusi Käsitsi allalaadimine Kopeeriti lõikelauale Õnnitlused, taotletud versioonikood on saadaval, laadime alla. diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 311e39202..ff2799b89 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -216,7 +216,6 @@ Eskatzen ari zaren bertsio kodea ez dago erabilgarri. Eskuzko deskargatzea Ziurtatu berriro hasten duzula saioa eta berrabiarazi aplikazioa aldaketak aplikatzeko. - Erakutsi antzeko taldeak eta erlazionatuak aplikazioaren xehetasunen orrian Antzeko aplikazioak eta erlazionatuak Erakutsi zuretzako orriak hasierako pantailan Zuretzako orrialdeak diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 553fe7993..4c622f07c 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -223,7 +223,6 @@ اجازه نصب برنامه‌ها از فروشگاه آرورا حذف APK پس از نصب نصب کننده AM. - نمایش خوشه های مشابه و مرتبط در صفحه جزئیات برنامه اطمینان حاصل کنید که برای اعمال پارامتر جعلی دوباره وارد سیستم شوید انتخاب سردبیران بدون شبکه diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index ba262b83e..72cded1ab 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -187,7 +187,6 @@ Varmista, että kirjaudut uudelleen ja käynnistät sovelluksen uudelleen ottaaksesi muutokset käyttöön. Varmista, että kirjaudut uudelleen ottaaksesi huijauksen käyttöösi Sessio päättyi, kirjaudu uudelleen uuden session aloittamiseksi. - Näytä sovelluksen lisätietosivulla sen kanssa samankaltaisia ja siihen liittyviä sovelluksia Näytä Sinulle räätälöity-sivut kotinäytöllä Valitse oletusvälilehti Ulkoasu diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 855d125cb..3a8397ef1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -209,7 +209,6 @@ Reconnectez-vous et redémarrez l’appli pour appliquer les changements. Mise en réseau Pages « Pour vous » - Afficher les grappes semblables et connexes sur la page des détails de l’appli Applis semblables et connexes Afficher les pages « Pour vous » sur l’écran d’accueil Choisir l’onglet par défaut diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index bb8108eea..16819e649 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -17,7 +17,6 @@ Aplicación non soportada Aplicación non mercada Descarga fallida - Amosar grupos similares e relacionados na páxina de detalles da aplicación Aplicacións semellantes e relacionadas Mostrar Suxestións Personalizadas na pantalla de inicio Suxestións personalizadas diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 2cf220f29..66caa7962 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -207,7 +207,6 @@ מתקין מערכת בחר עמוד ברירת מחדל הצגת עמודים בשבילך במסך הבית - יישומים דומים וקשורים במסך פרטי היישום מנהל הזיוף אתה תראה תכונות חדשות ובאגים לפני כולם. תן משוב למפתחים כדי לעזור להם להשתפר. אפשרי לבחור מתקין נתיב, אבל לא תוכל להתקין התקנות מאוחדות (מפוצלות - split), אז הבחירה בידך. diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index f363a10b5..a5e7f6248 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -218,7 +218,6 @@ रूट इंस्टॉलर ऑरोरा सेवाएं (अस्वीकृत) सत्र समाप्त हो गया, नया सत्र प्राप्त करने के लिए पुनः लॉगिन करें। - ऐप विवरण पृष्ठ पर समान और संबंधित समूह प्रदर्शित करें सुनिश्चित करें कि आप छलावरण लागू करने के लिए पुनः लॉगिन करें ऐप समर्थित नहीं है श्वेतसूचीबद्ध diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 5d6816342..094e579a9 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -211,7 +211,6 @@ Čestitamo, traženi kod verzije je dostupan, preuzima se. Zatraženi kod verzije nije dostupan. Ručno preuzimanje - Prikaži slične i srodne skupove podataka na stranici detalja aplikacije Slične i srodne aplikacije Prikaži „Za tvoje stranice” na početnom ekranu Za tvoje stranice diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 4534262e7..6ad5608b6 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -208,7 +208,6 @@ Telepítések Tiltás Törlés - Hasonló és releváns csoportok megjelenítése a részletek lapon Hasonló és releváns alkalmazások „Önnek ajánlott” oldal megjelenítése a kezdőlapon Önnek ajánlott oldalak diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 9ecf88d73..3d3f11de2 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -198,7 +198,6 @@ Aplikasi tidak ditemukan Aplikasi tidak didukung Aplikasi tidak dibeli - Tampilkan kelompok yang serupa dan terkait pada halaman detail aplikasi Aplikasi serupa dan terkait Tampilkan halaman Untuk Anda di layar beranda Halaman Untuk Anda diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b95d67748..be6f0c707 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -208,7 +208,6 @@ Gli acquisti di app non sono disponibili sui profili anonimi. Assicurati di effettuare nuovamente l\'accesso e di riavviare l\'app per applicare le modifiche. Rete - Visualizza i gruppi di app simili e correlate nella pagina dei dettagli dell\'app App simili e correlate Visualizza le pagine «Per te» sulla schermata iniziale Pagine «Per te» diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 86cf63b75..6e3c9e4df 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -212,7 +212,6 @@ 要求したバージョン番号は利用できません。 おめでとう!要求したバージョンコードが利用できます。ダウンロード中。 手動ダウンロード - 類似のアプリをアプリ詳細ページに表示する 類似のアプリ ホーム画面におすすめページを表示する おすすめページ diff --git a/app/src/main/res/values-kmr/strings.xml b/app/src/main/res/values-kmr/strings.xml index 8ff3dee85..d6aa2325d 100644 --- a/app/src/main/res/values-kmr/strings.xml +++ b/app/src/main/res/values-kmr/strings.xml @@ -216,7 +216,6 @@ Koda guhertoya ku tu dixwazî tune ye. Bi destan daxe Ji bo sepandina guhertinan zanibe be ku tu ji nû ve têketiye û sepanê ji nû ve daye destpêkirin. - Komên wekhev û têkildar li ser rûpela hûrgiliyên sepanê nîşan bide Sepanên wekhev û têkildar Rûpelên Bo Te li ser dîmendera malê rê bide Rûpelên Bo Te diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 37904aec0..643756733 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -207,7 +207,6 @@ 앱스 앱 설정 다운로드 실패됨 - 앱 세부 페이지에서 유사한 및 관련 클러스터 표시 새 분석 요청 홈 화면에 추가 완료건 지우기 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 3c7fb2cbf..cbec5c473 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -151,7 +151,6 @@ Priverstinai išvalyti visus Pristabdyti visus „Jums“ puslapiai - Rodyti programėlės puslapyje panašių ir susijusių programėlių rinkinį Redaktoriaus pasirinkimas Jums Programėlės bibliotekoje diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 4baaf72a4..b662dbe2e 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -111,7 +111,6 @@ Lapas \"Tev\" Rādīt lapas \"Tev\" sākuma ekrānā Līdzīgas un saistītas lietotnes - Rādīt līdzīgas un saistītas lietotnes Lejupielāde neizdevās Top maksas Populāri diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 2fbc5226b..ccaea2ab4 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -187,7 +187,6 @@ Skru på utviklingsinnstillingene fra enhetsinnstillingene for å åpne dem. Utforskningssiden er utilgjengelig Enhetslureri i bruk. - Vis lignende og relaterte klynger på appdetaljsiden Vis For You-sider på startskjermen Sett opp Aurora-tjenester og innvilg alle tilganger først. Lureri-håndterer diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 8871f55b5..348c6e6ec 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -70,7 +70,6 @@ App niet ondersteund App niet aangeschaft Installatie mislukt - Toon vergelijkbare en gerelateerde clusters op de app-detailspagina Vergelijkbare en gerelateerde apps Pagina met aanbevelingen op thuisscherm tonen Voor jou pagina\'s diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 5fcda58a7..60b78b5b6 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -55,7 +55,6 @@ ਐਪ ਸਪੋਰਟਡ ਨਹੀਂ ਇਹ ਐਪ ਅਜੇ ਤੁਹਾਡੇ ਵਲੋਂ ਖਰੀਦਿਆ ਨਹੀਂ ਗਿਆ ਡਾਊਨਲੋਡ ਫੇਲ੍ਹ - ਐਪ ਜਾਣਕਾਰੀ ਵਾਲੇ ਪੇਜ ਤੇ ਇਸੇ ਸ਼੍ਰੇਣੀ ਦੇ ਕੁੱਝ ਹੋਰ ਐਪਸ ਵਾਲਾ ਖ਼ਾਨਾ ਵਿਖਾਓ ਇਸੇ ਸ਼੍ਰੇਣੀ ਦੇ ਕੁੱਝ ਹੋਰ ਐਪਸ ਖਾਸ ਤੁਹਾਡੇ ਲਈ ਚੁਣੇ ਹੋਏ ਐਪਸ ਵਾਲਾ ਪੰਨਾ ਵਿਖਾਓ ਤੁਹਾਡੇ ਲਈ ਚੁਣੇ ਹੋਏ ਐਪਸ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 558d5f6b9..9a903e9ea 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -211,7 +211,6 @@ Dodatkowe Strony dla Ciebie Wyświetl strony Dla Ciebie na ekranie głównym - Wyświetl grupy podobnych i powiązanych na stronie szczegółów aplikacji Aplikacje podobne i powiązane Wybierz domyślną kartę Układ diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 630a4ca4d..9af1a1284 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -211,7 +211,6 @@ O código da versão solicitada está disponível, baixando agora. O código de versão que você solicitou não está disponível. Baixar manualmente - Exibir apps semelhantes e relacionados na página de detalhes do app Exibir páginas de recomendação na tela inicial Rede Não há apps correspondentes diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index de71cd0d0..9266a1b67 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -83,7 +83,6 @@ Aplicação não suportada Aplicação não comprada Descarga falhou - Mostrar grupos de aplicações semelhantes e relacionadas na página de detalhes das aplicações Aplicações semelhantes e relacionadas Exibir páginas de recomendação no ecrã inicial Páginas de recomendação diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 5100de626..6aadd4b0b 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -207,7 +207,6 @@ Copiat în clipboard Instalator sesiune Metoda de instalare - Afișare grupuri similare și asociate pe pagina de detalii a aplicației Rețea Vai! Toate bune. Istoricul achizițiilor diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 4be3edbb1..4a5cab910 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -208,7 +208,6 @@ Покупка приложений недоступна когда анонимно. Убедитесь, что выполнили новый вход и перезапустили приложение для применения изменений. Сеть - Показать секции похожих и связанных на странице приложения Показать страницу рекомендаций на главном экране Вкладка по умолчанию Вкладки diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 69a6ac1db..3c5a6715a 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -215,7 +215,6 @@ BHIM - UPI Dona pro mèdiu de UPI Retzende meta-datos - Ammustra sos grupos de aplicatziones simigiantes e ligados in sa pàgina de sos detàllios de s\'aplicatzione Otene Aurora Store pro mèdiu de F-Droid. Servìtzios de Aurora Installatzione diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 49e6919ec..c93f8d082 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -234,7 +234,6 @@ වි. %1$d ත. %2$d ක් ඉතිරිය ත. %1$d ක් ඉතිරිය දැනුම්දීම් - යෙදුමේ විස්තර පිටුවේ සමාන හා ආශ්‍රිත පර්ෂද පෙන්වන්න ඔක්කොම හරි! ඇතුළු වී අත්විඳින්න. සූදානම් කෙරෙමින්… diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index dbac84abf..1aa5b4b3a 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -140,7 +140,6 @@ Shizuku inštalátor Inštalátor relácie Predvolené (z konfigurácie zariadenia) - Zobraziť podobné a súvisiace skupiny na stránke podrobností aplikácie Nekompatibilné aktualizácie Zobraziť aktualizácie pre nekompatibilné alebo zakázané aplikácie, ktoré môžu zlyhať pri nštalovaní Iba aplikácie Aurora obchod diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 8b35da7f4..b714fc636 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -272,7 +272,6 @@ Filtrirajte F-Droid aplikacije Prilagajanje Strani za vas - Prikaži podobne in sorodne gruče na strani s podrobnostmi o aplikaciji Meni Wiki Poiščite odgovore na pogosto zastavljena vprašanja (F.A.Q.), korake za odpravljanje težav in več diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index f4e5db1bc..3297e94e3 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -82,7 +82,6 @@ Appka lama taageerin Appka lama iibsanin Dajintii maguulaysan - Soodhig appska lamidka ah iyo kuwa laxidhiidha bogga faahfaahinta appka Lamid ah & laxidhiidha Boggaga Adiga shaashada hore soodhig Boggaga Adiga diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 0645c6483..0f5f9084d 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -173,7 +173,6 @@ Faqe “Për ju” Shfaq te skena e kreut faqe “Për ju” Aplikacione të ngjashme dhe të afërta - Shfaq te faqja e hollësive të aplikacionit grumbuj të ngjashëm dhe të afërt Shkarkimi dështoi Aplikacion jo i blerë Aplikacioni nuk mbulohet diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 00c040c29..56997784e 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -215,7 +215,6 @@ Кôд верзије који тражите није доступан. Ручно преузимање Обавезно се поново пријавите и поново покрените апликацију да бисте применили промене. - Прикажите сличне и сродне скупове на страници с детаљима о апликацији Сличне и сродне апликације Прикажите странице за вас на почетном екрану Странице за вас diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index ed17f5c06..c70b704fa 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -202,7 +202,6 @@ Kunde inte exportera enhetens konfiguration Enhetens konfiguration exporterades Svartlistad - Visa liknande och relaterade klustrar på appens detaljsida Hanterare av externt lagringsutrymme Inga behörigheter Avvaktande diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 8aad8fa3c..a5eec028c 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -97,7 +97,6 @@ தளவமைப்பு இயல்புநிலை தாவலைத் தேர்ந்தெடுக்கவும் ஒத்த மற்றும் தொடர்புடைய பயன்பாடுகள் - செயலி விவரங்கள் பக்கத்தில் ஒத்த மற்றும் தொடர்புடைய கொத்துகளைக் காண்பி பயன்பாடு கிடைக்கவில்லை சாதனம் நிறவப்பட்டவை diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index acddb4614..a0279a5be 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -78,7 +78,6 @@ Uygulama desteklenmiyor Uygulama satın alınmadı İndirme başarısız - Uygulama ayrıntıları sayfasında benzer ve ilgili kümeleri görüntüle Benzer ve ilgili uygulamalar Ana ekranda Sana özel sayfaları göster Sana özel sayfalar diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 0916e83df..0d061c3d3 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -162,7 +162,6 @@ Застосунок не підтримується Застосунок не придбано Не вдалося завантажити - Показувати групи подібних та пов\'язаних застосунків на сторінці відомостей про застосунок Подібні та пов’язані застосунки Показувати сторінки для вас на головному екрані Сторінки Для вас diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 117477571..01ac85f63 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -218,7 +218,6 @@ Chúc mừng, mã phiên bản được yêu cầu đang có sẵn, đang tải xuống ngay bây giờ. Mã phiên bản bạn đang yêu cầu không có sẵn. Tải xuống thủ công - Hiển thị các cụm tương tự và liên quan trên trang chi tiết ứng dụng Các ứng dụng tương tự và liên quan Hiển thị trang Cho bạn trên màn hình chính Trang Cho Bạn diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 094365cf5..325f5ad48 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -208,7 +208,6 @@ 安装 Aurora 服务 1.09 或以上版本,或更改安装程序。 确保你重新登录并重新启动应用程序以应用更改。 网络 - 在应用程序详细信息页面上显示相似和相关的集群 相似及相关应用 在主页展示 For You 页面 For You 页面 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index dd88d272c..b3d1ed5eb 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -116,7 +116,6 @@ 尚未支援該應用程式 尚未購買該應用程式 下載失敗 - 在詳細說明頁面顯示「相似及相關」 相似及相關的程式 在首頁顯示「為你精選」頁面 為你精選頁面 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c59abd6b9..474d9778e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -136,7 +136,6 @@ Data privacy and security practices declared by developer "Privacy" "Rating and reviews" - Feedback given by other users "Review title" "What do you think about this app?" "Cancel all" @@ -223,7 +222,6 @@ "For you pages" "Display For you pages on home screen" "Similar and related apps" - "Display similar and related clusters on app details page" Show updates that may fail Display updates for incompatible or disabled apps that may fail to install Filter apps from other sources diff --git a/app/src/main/res/xml/preferences_ui.xml b/app/src/main/res/xml/preferences_ui.xml index 005f62f23..b3367dcaa 100644 --- a/app/src/main/res/xml/preferences_ui.xml +++ b/app/src/main/res/xml/preferences_ui.xml @@ -49,11 +49,4 @@ app:key="PREFERENCE_FOR_YOU" app:summary="@string/pref_ui_no_for_you_desc" app:title="@string/pref_ui_no_for_you" /> - - diff --git a/app/src/preload/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt b/app/src/preload/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt index cadc94c05..b0aea999a 100644 --- a/app/src/preload/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt +++ b/app/src/preload/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt @@ -28,7 +28,6 @@ import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID -import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR import com.aurora.store.util.Preferences.PREFERENCE_THEME_STYLE import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED @@ -52,7 +51,6 @@ class OnboardingFragment : BaseFlavouredOnboardingFragment() { save(PREFERENCE_THEME_STYLE, 0) save(PREFERENCE_DEFAULT_SELECTED_TAB, 0) save(PREFERENCE_FOR_YOU, true) - save(PREFERENCE_SIMILAR, false) /*Installer*/ save(PREFERENCE_AUTO_DELETE, true) diff --git a/app/src/vanilla/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt b/app/src/vanilla/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt index cadc94c05..b0aea999a 100644 --- a/app/src/vanilla/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt +++ b/app/src/vanilla/java/com/aurora/store/view/ui/onboarding/OnboardingFragment.kt @@ -28,7 +28,6 @@ import com.aurora.store.util.Preferences.PREFERENCE_FILTER_AURORA_ONLY import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID -import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR import com.aurora.store.util.Preferences.PREFERENCE_THEME_STYLE import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED @@ -52,7 +51,6 @@ class OnboardingFragment : BaseFlavouredOnboardingFragment() { save(PREFERENCE_THEME_STYLE, 0) save(PREFERENCE_DEFAULT_SELECTED_TAB, 0) save(PREFERENCE_FOR_YOU, true) - save(PREFERENCE_SIMILAR, false) /*Installer*/ save(PREFERENCE_AUTO_DELETE, true) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6032e4bda..7f2815752 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ activity = "1.10.1" agCoreservice = "13.3.1.300" androidGradlePlugin = "8.12.0" +adaptive = "1.1.0" androidx-hilt = "1.2.0" androidx-junit = "1.3.0" browser = "1.9.0" @@ -47,6 +48,9 @@ androidx-activity-compose = { group = "androidx.activity", name = "activity-comp androidx-browser = { module = "androidx.browser:browser", version.ref = "browser" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "core" } +androidx-adaptive-core = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "adaptive" } +androidx-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout", version.ref = "adaptive" } +androidx-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation", version.ref = "adaptive" } androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" } androidx-hilt-navigation = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidx-hilt" } androidx-junit = { module = "androidx.test.ext:junit-ktx", version.ref = "androidx-junit" }