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 <aayushgupta219@gmail.com>
This commit is contained in:
@@ -194,6 +194,9 @@ dependencies {
|
|||||||
implementation(libs.androidx.work.runtime.ktx)
|
implementation(libs.androidx.work.runtime.ktx)
|
||||||
implementation(libs.androidx.paging.runtime)
|
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.paging.compose)
|
||||||
implementation(libs.androidx.navigation.compose)
|
implementation(libs.androidx.navigation.compose)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
|||||||
@@ -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_FILTER_FDROID
|
||||||
import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU
|
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_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_THEME_STYLE
|
||||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
|
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
|
||||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
|
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
|
||||||
@@ -57,7 +56,6 @@ class OnboardingFragment : BaseFlavouredOnboardingFragment() {
|
|||||||
save(PREFERENCE_THEME_STYLE, 0)
|
save(PREFERENCE_THEME_STYLE, 0)
|
||||||
save(PREFERENCE_DEFAULT_SELECTED_TAB, 0)
|
save(PREFERENCE_DEFAULT_SELECTED_TAB, 0)
|
||||||
save(PREFERENCE_FOR_YOU, true)
|
save(PREFERENCE_FOR_YOU, true)
|
||||||
save(PREFERENCE_SIMILAR, true)
|
|
||||||
|
|
||||||
/*Installer*/
|
/*Installer*/
|
||||||
save(PREFERENCE_AUTO_DELETE, true)
|
save(PREFERENCE_AUTO_DELETE, true)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import com.aurora.store.compose.composables.preview.AppPreviewProvider
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Composable to show some information
|
* Composable to show some information
|
||||||
|
* @param modifier Modifier to change the composable
|
||||||
* @param title Title of the information
|
* @param title Title of the information
|
||||||
* @param description Information to show
|
* @param description Information to show
|
||||||
* @param icon Optional icon representing the information
|
* @param icon Optional icon representing the information
|
||||||
@@ -39,13 +40,14 @@ import com.aurora.store.compose.composables.preview.AppPreviewProvider
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun InfoComposable(
|
fun InfoComposable(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
title: AnnotatedString,
|
title: AnnotatedString,
|
||||||
description: AnnotatedString? = null,
|
description: AnnotatedString? = null,
|
||||||
@DrawableRes icon: Int? = null,
|
@DrawableRes icon: Int? = null,
|
||||||
onClick: (() -> Unit)? = null
|
onClick: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null)
|
.clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null)
|
||||||
.padding(
|
.padding(
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.aurora.store.R
|
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
|
* A top app bar composable to be used with Scaffold in different Screen
|
||||||
* @param title Title of the screen
|
* @param title Title of the screen
|
||||||
|
* @param navigationIcon Icon for the navigation button
|
||||||
* @param onNavigateUp Action when user clicks the navigation icon
|
* @param onNavigateUp Action when user clicks the navigation icon
|
||||||
* @param actions Actions to display on the top app bar (for e.g. menu)
|
* @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)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
fun TopAppBarComposable(
|
fun TopAppBarComposable(
|
||||||
title: String? = null,
|
title: String? = null,
|
||||||
onNavigateUp: () -> Unit,
|
navigationIcon: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
|
onNavigateUp: (() -> Unit)? = null,
|
||||||
actions: @Composable (RowScope.() -> Unit) = {}
|
actions: @Composable (RowScope.() -> Unit) = {}
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { if (title != null) Text(text = title) },
|
title = { if (title != null) Text(text = title) },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onNavigateUp) {
|
if (onNavigateUp != null) {
|
||||||
Icon(
|
IconButton(onClick = onNavigateUp) {
|
||||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
Icon(imageVector = navigationIcon, contentDescription = null)
|
||||||
contentDescription = null
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = actions
|
actions = actions
|
||||||
|
|||||||
@@ -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<Float> {
|
||||||
|
override val values: Sequence<Float>
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,6 @@ import com.aurora.store.R
|
|||||||
* @param message Message for error
|
* @param message Message for error
|
||||||
* @param actionMessage Message to show on action button; defaults to null with button not visible
|
* @param actionMessage Message to show on action button; defaults to null with button not visible
|
||||||
* @param onAction Callback when action button is clicked
|
* @param onAction Callback when action button is clicked
|
||||||
* @see NoAppAltComposable
|
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun NoAppComposable(
|
fun NoAppComposable(
|
||||||
|
|||||||
@@ -11,13 +11,9 @@ import androidx.navigation.NavHostController
|
|||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.toRoute
|
import androidx.navigation.toRoute
|
||||||
import com.aurora.extensions.parentViewModel
|
|
||||||
import com.aurora.store.compose.ui.commons.BlacklistScreen
|
import com.aurora.store.compose.ui.commons.BlacklistScreen
|
||||||
import com.aurora.store.compose.ui.details.AppDetailsScreen
|
import com.aurora.store.compose.ui.details.AppDetailsScreen
|
||||||
import com.aurora.store.compose.ui.details.DetailsExodusScreen
|
import com.aurora.store.compose.ui.dev.DevProfileScreen
|
||||||
import com.aurora.store.compose.ui.details.DetailsMoreScreen
|
|
||||||
import com.aurora.store.compose.ui.details.DetailsReviewScreen
|
|
||||||
import com.aurora.store.compose.ui.details.DetailsScreenshotScreen
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigation graph for compose screens
|
* Navigation graph for compose screens
|
||||||
@@ -45,46 +41,21 @@ fun NavGraph(navHostController: NavHostController, startDestination: Screen) {
|
|||||||
val appDetails = backstackEntry.toRoute<Screen.AppDetails>()
|
val appDetails = backstackEntry.toRoute<Screen.AppDetails>()
|
||||||
AppDetailsScreen(
|
AppDetailsScreen(
|
||||||
packageName = appDetails.packageName,
|
packageName = appDetails.packageName,
|
||||||
viewModel = backstackEntry.parentViewModel(navHostController),
|
|
||||||
onNavigateUp = { onNavigateUp() },
|
onNavigateUp = { onNavigateUp() },
|
||||||
onNavigateToDetailsMore = { navHostController.navigate(Screen.DetailsMore) },
|
onNavigateToAppDetails = { packageName ->
|
||||||
onNavigateToDetailsReview = { navHostController.navigate(Screen.DetailsReview) },
|
navHostController.navigate(Screen.AppDetails(packageName))
|
||||||
onNavigateToDetailsExodus = { navHostController.navigate(Screen.DetailsExodus) },
|
|
||||||
onNavigateToDetailsScreenshot = { index ->
|
|
||||||
navHostController.navigate(
|
|
||||||
Screen.DetailsScreenshot(index)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Screen.DetailsExodus> { backstackEntry ->
|
composable<Screen.DevProfile> { backstackEntry ->
|
||||||
DetailsExodusScreen(
|
val devProfile = backstackEntry.toRoute<Screen.DevProfile>()
|
||||||
|
DevProfileScreen(
|
||||||
|
developerId = devProfile.developerId,
|
||||||
onNavigateUp = { onNavigateUp() },
|
onNavigateUp = { onNavigateUp() },
|
||||||
viewModel = backstackEntry.parentViewModel(navHostController)
|
onNavigateToAppDetails = { packageName ->
|
||||||
)
|
navHostController.navigate(Screen.AppDetails(packageName))
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Screen.DetailsMore> { backstackEntry ->
|
|
||||||
DetailsMoreScreen(
|
|
||||||
onNavigateUp = { onNavigateUp() },
|
|
||||||
viewModel = backstackEntry.parentViewModel(navHostController)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
composable<Screen.DetailsScreenshot> { backstackEntry ->
|
|
||||||
val screenshotDetails = backstackEntry.toRoute<Screen.DetailsScreenshot>()
|
|
||||||
DetailsScreenshotScreen(
|
|
||||||
index = screenshotDetails.index,
|
|
||||||
onNavigateUp = { onNavigateUp() },
|
|
||||||
viewModel = backstackEntry.parentViewModel(navHostController)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
composable<Screen.DetailsReview> { backstackEntry ->
|
|
||||||
DetailsReviewScreen(
|
|
||||||
onNavigateUp = { onNavigateUp() },
|
|
||||||
viewModel = backstackEntry.parentViewModel(navHostController)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,18 +31,33 @@ sealed class Screen(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data object Blacklist : Screen(label = R.string.title_blacklist_manager)
|
data object Blacklist : Screen(label = R.string.title_blacklist_manager)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class DevProfile(val developerId: String): Screen()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AppDetails(val packageName: String) : Screen()
|
data class AppDetails(val packageName: String) : Screen()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child screen of [AppDetails]; Avoid navigating to this screen directly.
|
||||||
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data object DetailsMore : Screen()
|
data object DetailsMore : Screen()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child screen of [AppDetails]; Avoid navigating to this screen directly.
|
||||||
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data class DetailsScreenshot(val index: Int) : Screen()
|
data class DetailsScreenshot(val index: Int) : Screen()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child screen of [AppDetails]; Avoid navigating to this screen directly.
|
||||||
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data object DetailsExodus : Screen()
|
data object DetailsExodus : Screen()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child screen of [AppDetails]; Avoid navigating to this screen directly.
|
||||||
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data object DetailsReview : Screen()
|
data object DetailsReview : Screen()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.requiredSize
|
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.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
@@ -25,36 +28,44 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
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.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.dimensionResource
|
import androidx.compose.ui.res.dimensionResource
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.fromHtml
|
import androidx.compose.ui.text.fromHtml
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
|
||||||
import androidx.compose.ui.util.fastForEach
|
import androidx.compose.ui.util.fastForEach
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import androidx.window.core.layout.WindowWidthSizeClass
|
||||||
import coil3.annotation.ExperimentalCoilApi
|
import coil3.annotation.ExperimentalCoilApi
|
||||||
import coil3.compose.AsyncImage
|
|
||||||
import coil3.compose.LocalAsyncImagePreviewHandler
|
import coil3.compose.LocalAsyncImagePreviewHandler
|
||||||
import coil3.request.ImageRequest
|
|
||||||
import coil3.request.crossfade
|
|
||||||
import com.aurora.extensions.bodyVerySmall
|
import com.aurora.extensions.bodyVerySmall
|
||||||
import com.aurora.extensions.browse
|
import com.aurora.extensions.browse
|
||||||
import com.aurora.extensions.copyToClipBoard
|
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.HeaderComposable
|
||||||
import com.aurora.store.compose.composables.InfoComposable
|
import com.aurora.store.compose.composables.InfoComposable
|
||||||
import com.aurora.store.compose.composables.TopAppBarComposable
|
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.AppProgressComposable
|
||||||
import com.aurora.store.compose.composables.app.AppTagComposable
|
import com.aurora.store.compose.composables.app.AppTagComposable
|
||||||
import com.aurora.store.compose.composables.app.NoAppComposable
|
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.details.ScreenshotComposable
|
||||||
import com.aurora.store.compose.composables.preview.AppPreviewProvider
|
import com.aurora.store.compose.composables.preview.AppPreviewProvider
|
||||||
import com.aurora.store.compose.composables.preview.coilPreviewProvider
|
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.compose.ui.dialogs.ManualDownloadDialog
|
||||||
import com.aurora.store.data.installer.AppInstaller
|
import com.aurora.store.data.installer.AppInstaller
|
||||||
import com.aurora.store.data.model.Report
|
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
|
||||||
import com.aurora.store.util.PackageUtil.PACKAGE_NAME_GMS
|
import com.aurora.store.util.PackageUtil.PACKAGE_NAME_GMS
|
||||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.random.Random
|
||||||
import com.aurora.gplayapi.data.models.datasafety.Report as DataSafetyReport
|
import com.aurora.gplayapi.data.models.datasafety.Report as DataSafetyReport
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppDetailsScreen(
|
fun AppDetailsScreen(
|
||||||
packageName: String,
|
packageName: String,
|
||||||
onNavigateUp: () -> Unit,
|
onNavigateUp: () -> Unit,
|
||||||
onNavigateToDetailsMore: () -> Unit,
|
onNavigateToAppDetails: (packageName: String) -> Unit,
|
||||||
onNavigateToDetailsScreenshot: (index: Int) -> Unit,
|
|
||||||
onNavigateToDetailsReview: () -> Unit,
|
|
||||||
onNavigateToDetailsExodus: () -> Unit,
|
|
||||||
viewModel: AppDetailsViewModel = hiltViewModel()
|
viewModel: AppDetailsViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -104,17 +118,23 @@ fun AppDetailsScreen(
|
|||||||
val dataSafetyReport by viewModel.dataSafetyReport.collectAsStateWithLifecycle()
|
val dataSafetyReport by viewModel.dataSafetyReport.collectAsStateWithLifecycle()
|
||||||
val plexusScores by viewModel.plexusScores.collectAsStateWithLifecycle()
|
val plexusScores by viewModel.plexusScores.collectAsStateWithLifecycle()
|
||||||
val download by viewModel.download.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) }
|
var shouldShowManualDownloadDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(key1 = Unit) { viewModel.fetchAppDetails(packageName) }
|
LaunchedEffect(key1 = Unit) { viewModel.fetchAppDetails(packageName) }
|
||||||
LaunchedEffect(key1 = Unit) {
|
LaunchedEffect(key1 = Unit) {
|
||||||
viewModel.purchaseStatus.collect { success ->
|
viewModel.purchaseStatus.collect { success ->
|
||||||
if (success) {
|
if (shouldShowManualDownloadDialog) {
|
||||||
shouldShowManualDownloadDialog = false
|
if (success) {
|
||||||
context.toast(R.string.toast_manual_available)
|
shouldShowManualDownloadDialog = false
|
||||||
|
context.toast(R.string.toast_manual_available)
|
||||||
|
} else {
|
||||||
|
context.toast(R.string.toast_manual_unavailable)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
context.toast(R.string.toast_manual_unavailable)
|
context.toast(R.string.download_failed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,19 +155,18 @@ fun AppDetailsScreen(
|
|||||||
if (this.packageName.isBlank()) {
|
if (this.packageName.isBlank()) {
|
||||||
ScreenContentLoading(onNavigateUp = onNavigateUp)
|
ScreenContentLoading(onNavigateUp = onNavigateUp)
|
||||||
} else {
|
} else {
|
||||||
ScreenContent(
|
ScreenContentApp(
|
||||||
app = this,
|
app = this,
|
||||||
|
isAnonymous = viewModel.authProvider.isAnonymous,
|
||||||
|
suggestions = suggestions,
|
||||||
download = download,
|
download = download,
|
||||||
|
installProgress = installProgress,
|
||||||
plexusScores = plexusScores,
|
plexusScores = plexusScores,
|
||||||
dataSafetyReport = dataSafetyReport,
|
dataSafetyReport = dataSafetyReport,
|
||||||
exodusReport = exodusReport,
|
exodusReport = exodusReport,
|
||||||
hasValidUpdate = viewModel.hasValidUpdate,
|
hasValidUpdate = viewModel.hasValidUpdate,
|
||||||
showSimilarApps = viewModel.showSimilarApps,
|
|
||||||
onNavigateUp = onNavigateUp,
|
onNavigateUp = onNavigateUp,
|
||||||
onNavigateToDetailsMore = onNavigateToDetailsMore,
|
onNavigateToAppDetails = onNavigateToAppDetails,
|
||||||
onNavigateToDetailsScreenshot = onNavigateToDetailsScreenshot,
|
|
||||||
onNavigateToDetailsReview = onNavigateToDetailsReview,
|
|
||||||
onNavigateToDetailsExodus = onNavigateToDetailsExodus,
|
|
||||||
onDownload = { viewModel.download(this) },
|
onDownload = { viewModel.download(this) },
|
||||||
onManualDownload = { shouldShowManualDownloadDialog = true },
|
onManualDownload = { shouldShowManualDownloadDialog = true },
|
||||||
onCancelDownload = { viewModel.cancelDownload(this) },
|
onCancelDownload = { viewModel.cancelDownload(this) },
|
||||||
@@ -160,6 +179,9 @@ fun AppDetailsScreen(
|
|||||||
} catch (exception: ActivityNotFoundException) {
|
} catch (exception: ActivityNotFoundException) {
|
||||||
context.toast(context.getString(R.string.unable_to_open))
|
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
|
@Composable
|
||||||
private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) {
|
private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -180,6 +205,9 @@ private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable to display errors related to fetching app details
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
private fun ScreenContentError(onNavigateUp: () -> Unit = {}) {
|
private fun ScreenContentError(onNavigateUp: () -> Unit = {}) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -193,28 +221,186 @@ private fun ScreenContentError(onNavigateUp: () -> Unit = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable to display app details and suggestions
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
private fun ScreenContent(
|
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||||
|
private fun ScreenContentApp(
|
||||||
app: App,
|
app: App,
|
||||||
|
isAnonymous: Boolean = true,
|
||||||
|
suggestions: List<App> = emptyList(),
|
||||||
download: Download? = null,
|
download: Download? = null,
|
||||||
|
installProgress: Float? = null,
|
||||||
plexusScores: Scores? = null,
|
plexusScores: Scores? = null,
|
||||||
dataSafetyReport: DataSafetyReport? = null,
|
dataSafetyReport: DataSafetyReport? = null,
|
||||||
exodusReport: Report? = null,
|
exodusReport: Report? = null,
|
||||||
hasValidUpdate: Boolean = false,
|
hasValidUpdate: Boolean = false,
|
||||||
showSimilarApps: Boolean = false,
|
|
||||||
onNavigateUp: () -> Unit = {},
|
onNavigateUp: () -> Unit = {},
|
||||||
onNavigateToDetailsMore: () -> Unit = {},
|
onNavigateToAppDetails: (packageName: String) -> Unit = {},
|
||||||
onNavigateToDetailsScreenshot: (index: Int) -> Unit = {},
|
|
||||||
onNavigateToDetailsReview: () -> Unit = {},
|
|
||||||
onNavigateToDetailsExodus: () -> Unit = {},
|
|
||||||
onDownload: () -> Unit = {},
|
onDownload: () -> Unit = {},
|
||||||
onManualDownload: () -> Unit = {},
|
onManualDownload: () -> Unit = {},
|
||||||
onCancelDownload: () -> Unit = {},
|
onCancelDownload: () -> Unit = {},
|
||||||
onUninstall: () -> Unit = {},
|
onUninstall: () -> Unit = {},
|
||||||
onOpen: () -> Unit = {}
|
onOpen: () -> Unit = {},
|
||||||
|
onTestingSubscriptionChange: (subscribe: Boolean) -> Unit = {}
|
||||||
|
) {
|
||||||
|
val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator<Screen>()
|
||||||
|
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
|
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(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBarComposable(
|
TopAppBarComposable(
|
||||||
@@ -230,53 +416,24 @@ private fun ScreenContent(
|
|||||||
.padding(dimensionResource(R.dimen.padding_medium)),
|
.padding(dimensionResource(R.dimen.padding_medium)),
|
||||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||||
) {
|
) {
|
||||||
// TODO: Deal with download status
|
val isDownloading = download != null && download.isRunning
|
||||||
AppDetails(app = app, hasValidUpdate = hasValidUpdate)
|
val isInstalling = installProgress != null
|
||||||
|
val progress = when {
|
||||||
when {
|
isDownloading -> download?.progress?.toFloat() ?: 0F
|
||||||
download?.isRunning == true -> {
|
isInstalling -> installProgress ?: 0F
|
||||||
AppActions(
|
else -> 0F
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppDetails(
|
||||||
|
app = app,
|
||||||
|
inProgress = isDownloading || isInstalling,
|
||||||
|
progress = progress,
|
||||||
|
onNavigateToDetailsDevApps = onNavigateToDetailsDevProfile,
|
||||||
|
hasValidUpdate = hasValidUpdate
|
||||||
|
)
|
||||||
|
|
||||||
|
SetupAppActions()
|
||||||
|
|
||||||
AppTags(app = app)
|
AppTags(app = app)
|
||||||
AppChangelog(changelog = app.changes)
|
AppChangelog(changelog = app.changes)
|
||||||
HeaderComposable(
|
HeaderComposable(
|
||||||
@@ -292,6 +449,13 @@ private fun ScreenContent(
|
|||||||
|
|
||||||
AppReviews(rating = app.rating, onNavigateToDetailsReview = onNavigateToDetailsReview)
|
AppReviews(rating = app.rating, onNavigateToDetailsReview = onNavigateToDetailsReview)
|
||||||
|
|
||||||
|
if (!isAnonymous && app.testingProgram?.isAvailable == true) {
|
||||||
|
AppTesting(
|
||||||
|
isSubscribed = app.testingProgram!!.isSubscribed,
|
||||||
|
onTestingSubscriptionChange = onTestingSubscriptionChange
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
AppCompatibility(
|
AppCompatibility(
|
||||||
needsGms = app.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS),
|
needsGms = app.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS),
|
||||||
plexusScores = plexusScores
|
plexusScores = plexusScores
|
||||||
@@ -322,24 +486,67 @@ private fun ScreenContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable to display similar and related app suggestions
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
private fun ScreenContentAppSupportingPane(
|
||||||
|
suggestions: List<App> = 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 to display basic app details
|
||||||
*/
|
*/
|
||||||
@Composable
|
@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
|
val context = LocalContext.current
|
||||||
|
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
AsyncImage(
|
AnimatedAppIconComposable(
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)),
|
||||||
.data(app.iconArtwork.url)
|
iconUrl = app.iconArtwork.url,
|
||||||
.crossfade(true)
|
inProgress = inProgress,
|
||||||
.build(),
|
progress = progress
|
||||||
contentDescription = null,
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
modifier = Modifier
|
|
||||||
.requiredSize(dimensionResource(R.dimen.icon_size_large))
|
|
||||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
|
|
||||||
)
|
)
|
||||||
Column(modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small))) {
|
Column(modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small))) {
|
||||||
Text(
|
Text(
|
||||||
@@ -349,6 +556,8 @@ private fun AppDetails(app: App, hasValidUpdate: Boolean = false) {
|
|||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(onClick = { onNavigateToDetailsDevApps(app.developerName) }),
|
||||||
text = app.developerName,
|
text = app.developerName,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
@@ -383,14 +592,20 @@ private fun AppActions(
|
|||||||
isPrimaryActionEnabled: Boolean = true,
|
isPrimaryActionEnabled: Boolean = true,
|
||||||
isSecondaryActionEnabled: Boolean = true,
|
isSecondaryActionEnabled: Boolean = true,
|
||||||
onPrimaryAction: () -> Unit = {},
|
onPrimaryAction: () -> Unit = {},
|
||||||
onSecondaryAction: () -> Unit = {}
|
onSecondaryAction: () -> Unit = {},
|
||||||
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium))
|
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(
|
FilledTonalButton(
|
||||||
modifier = Modifier.weight(1F),
|
modifier = buttonWidthModifier,
|
||||||
onClick = onSecondaryAction,
|
onClick = onSecondaryAction,
|
||||||
enabled = isSecondaryActionEnabled
|
enabled = isSecondaryActionEnabled
|
||||||
) {
|
) {
|
||||||
@@ -402,7 +617,7 @@ private fun AppActions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier.weight(1F),
|
modifier = buttonWidthModifier,
|
||||||
onClick = onPrimaryAction,
|
onClick = onPrimaryAction,
|
||||||
enabled = isPrimaryActionEnabled
|
enabled = isPrimaryActionEnabled
|
||||||
) {
|
) {
|
||||||
@@ -551,7 +766,6 @@ private fun AppReviews(rating: Rating, onNavigateToDetailsReview: () -> Unit) {
|
|||||||
|
|
||||||
HeaderComposable(
|
HeaderComposable(
|
||||||
title = stringResource(R.string.details_ratings),
|
title = stringResource(R.string.details_ratings),
|
||||||
subtitle = stringResource(R.string.details_ratings_subtitle),
|
|
||||||
onClick = onNavigateToDetailsReview
|
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
|
@Composable
|
||||||
@OptIn(ExperimentalCoilApi::class)
|
@OptIn(ExperimentalCoilApi::class)
|
||||||
private fun AppDetailsScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
private fun AppDetailsScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||||
CompositionLocalProvider(LocalAsyncImagePreviewHandler provides coilPreviewProvider) {
|
CompositionLocalProvider(LocalAsyncImagePreviewHandler provides coilPreviewProvider) {
|
||||||
ScreenContent(app = app, hasValidUpdate = false)
|
ScreenContentApp(
|
||||||
|
app = app,
|
||||||
|
isAnonymous = false,
|
||||||
|
suggestions = List(5) { app.copy(id = Random.nextInt()) },
|
||||||
|
hasValidUpdate = false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.Scaffold
|
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.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
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.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import androidx.window.core.layout.WindowWidthSizeClass
|
||||||
import com.aurora.Constants.EXODUS_REPORT_URL
|
import com.aurora.Constants.EXODUS_REPORT_URL
|
||||||
|
import com.aurora.extensions.adaptiveNavigationIcon
|
||||||
import com.aurora.extensions.browse
|
import com.aurora.extensions.browse
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.R
|
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.compose.composables.preview.AppPreviewProvider
|
||||||
import com.aurora.store.data.model.ExodusTracker
|
import com.aurora.store.data.model.ExodusTracker
|
||||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||||
|
import com.aurora.store.viewmodel.details.DetailsExodusViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailsExodusScreen(
|
fun DetailsExodusScreen(
|
||||||
onNavigateUp: () -> Unit,
|
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 app by appDetailsViewModel.app.collectAsStateWithLifecycle()
|
||||||
val exodusReport by viewModel.exodusReport.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(
|
ScreenContent(
|
||||||
topAppBarTitle = app!!.displayName,
|
topAppBarTitle = topAppBarTitle,
|
||||||
id = exodusReport!!.id,
|
id = exodusReport!!.id,
|
||||||
version = exodusReport!!.version,
|
version = exodusReport!!.version,
|
||||||
trackers = viewModel.getExodusTrackersFromReport(),
|
trackers = trackers,
|
||||||
onNavigateUp = onNavigateUp,
|
onNavigateUp = onNavigateUp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -55,12 +70,17 @@ private fun ScreenContent(
|
|||||||
version: String = String(),
|
version: String = String(),
|
||||||
trackers: List<ExodusTracker> = emptyList(),
|
trackers: List<ExodusTracker> = emptyList(),
|
||||||
onNavigateUp: () -> Unit = {},
|
onNavigateUp: () -> Unit = {},
|
||||||
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBarComposable(title = topAppBarTitle, onNavigateUp = onNavigateUp)
|
TopAppBarComposable(
|
||||||
|
title = topAppBarTitle,
|
||||||
|
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
||||||
|
onNavigateUp = onNavigateUp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ package com.aurora.store.compose.ui.details
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
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.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
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.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.getValue
|
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.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import androidx.window.core.layout.WindowWidthSizeClass
|
||||||
import coil3.annotation.ExperimentalCoilApi
|
import coil3.annotation.ExperimentalCoilApi
|
||||||
import coil3.compose.LocalAsyncImagePreviewHandler
|
import coil3.compose.LocalAsyncImagePreviewHandler
|
||||||
|
import com.aurora.extensions.adaptiveNavigationIcon
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.compose.composables.HeaderComposable
|
import com.aurora.store.compose.composables.HeaderComposable
|
||||||
import com.aurora.store.compose.composables.InfoComposable
|
import com.aurora.store.compose.composables.InfoComposable
|
||||||
import com.aurora.store.compose.composables.TopAppBarComposable
|
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.AppPreviewProvider
|
||||||
import com.aurora.store.compose.composables.preview.coilPreviewProvider
|
import com.aurora.store.compose.composables.preview.coilPreviewProvider
|
||||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||||
|
import com.aurora.store.viewmodel.details.DetailsMoreViewModel
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailsMoreScreen(
|
fun DetailsMoreScreen(
|
||||||
onNavigateUp: () -> Unit,
|
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
|
@Composable
|
||||||
private fun ScreenContent(app: App, onNavigateUp: () -> Unit = {}) {
|
private fun ScreenContent(
|
||||||
|
app: App,
|
||||||
|
dependencies: List<App>? = 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(
|
Scaffold(
|
||||||
topBar = { TopAppBarComposable(title = app.displayName, onNavigateUp = onNavigateUp) }
|
topBar = {
|
||||||
|
TopAppBarComposable(
|
||||||
|
title = topAppBarTitle,
|
||||||
|
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
||||||
|
onNavigateUp = onNavigateUp
|
||||||
|
)
|
||||||
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -69,51 +105,88 @@ private fun ScreenContent(app: App, onNavigateUp: () -> Unit = {}) {
|
|||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
|
|
||||||
if (app.appInfo.appInfoMap.isNotEmpty()) {
|
if (dependencies != null) {
|
||||||
HeaderComposable(title = stringResource(R.string.details_more_info))
|
AppDependencies(
|
||||||
|
dependencies = dependencies,
|
||||||
InfoComposable(
|
onNavigateToAppDetails = onNavigateToAppDetails
|
||||||
title = AnnotatedString(
|
|
||||||
text = stringResource(R.string.details_more_package_name)
|
|
||||||
),
|
|
||||||
description = AnnotatedString(text = app.packageName)
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
InfoComposable(
|
AppInfoMore(app = app)
|
||||||
title = AnnotatedString(
|
}
|
||||||
text = stringResource(R.string.details_more_content_rating)
|
}
|
||||||
),
|
}
|
||||||
description = AnnotatedString(text = app.contentRating.title)
|
|
||||||
)
|
|
||||||
|
|
||||||
app.appInfo.appInfoMap.forEach { (title, subtitle) ->
|
/**
|
||||||
InfoComposable(
|
* Composable to show dependencies of an app
|
||||||
title = AnnotatedString(
|
*/
|
||||||
text = title.replace("_", " ")
|
@Composable
|
||||||
.lowercase(Locale.getDefault())
|
private fun AppDependencies(
|
||||||
.replaceFirstChar {
|
dependencies: List<App>,
|
||||||
if (it.isLowerCase()) {
|
onNavigateToAppDetails: (packageName: String) -> Unit
|
||||||
it.titlecase(Locale.getDefault())
|
) {
|
||||||
} else {
|
HeaderComposable(title = stringResource(R.string.details_dependencies))
|
||||||
it.toString()
|
if (dependencies.isEmpty()) {
|
||||||
}
|
InfoComposable(
|
||||||
}
|
title = AnnotatedString(text = stringResource(R.string.details_no_dependencies))
|
||||||
),
|
)
|
||||||
description = AnnotatedString(text = subtitle)
|
} else {
|
||||||
)
|
LazyRow(modifier = Modifier.fillMaxWidth()) {
|
||||||
}
|
items(items = dependencies, key = { item -> item.id }) { app ->
|
||||||
|
AppComposable(
|
||||||
InfoComposable(
|
app = app,
|
||||||
title = AnnotatedString(
|
onClick = { onNavigateToAppDetails(app.packageName) }
|
||||||
text = stringResource(R.string.details_more_target_api)
|
|
||||||
),
|
|
||||||
description = AnnotatedString(text = "API ${app.targetSdk}")
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalCoilApi::class)
|
@OptIn(ExperimentalCoilApi::class)
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import androidx.compose.material3.FilterChip
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
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.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -34,26 +36,38 @@ import androidx.paging.PagingData
|
|||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import androidx.paging.compose.itemKey
|
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.gplayapi.data.models.Review
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.compose.composables.TopAppBarComposable
|
import com.aurora.store.compose.composables.TopAppBarComposable
|
||||||
import com.aurora.store.compose.composables.details.ReviewComposable
|
import com.aurora.store.compose.composables.details.ReviewComposable
|
||||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||||
|
import com.aurora.store.viewmodel.review.DetailsReviewViewModel
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailsReviewScreen(
|
fun DetailsReviewScreen(
|
||||||
onNavigateUp: () -> Unit,
|
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 app by appDetailsViewModel.app.collectAsStateWithLifecycle()
|
||||||
val reviews = viewModel.reviews.collectAsLazyPagingItems()
|
val reviews = detailsReviewViewModel.reviews.collectAsLazyPagingItems()
|
||||||
|
|
||||||
|
val topAppBarTitle = when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) {
|
||||||
|
WindowWidthSizeClass.COMPACT -> app!!.displayName
|
||||||
|
else -> stringResource(R.string.details_ratings)
|
||||||
|
}
|
||||||
|
|
||||||
ScreenContent(
|
ScreenContent(
|
||||||
topAppBarTitle = app!!.displayName,
|
topAppBarTitle = topAppBarTitle,
|
||||||
reviews = reviews,
|
reviews = reviews,
|
||||||
onNavigateUp = onNavigateUp,
|
onNavigateUp = onNavigateUp,
|
||||||
onFilter = { filter -> viewModel.fetchReviews(filter) }
|
onFilter = { filter -> detailsReviewViewModel.fetchReviews(filter) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,12 +76,17 @@ private fun ScreenContent(
|
|||||||
topAppBarTitle: String? = null,
|
topAppBarTitle: String? = null,
|
||||||
onNavigateUp: () -> Unit = {},
|
onNavigateUp: () -> Unit = {},
|
||||||
reviews: LazyPagingItems<Review>,
|
reviews: LazyPagingItems<Review>,
|
||||||
onFilter: (filter: Review.Filter) -> Unit = {}
|
onFilter: (filter: Review.Filter) -> Unit = {},
|
||||||
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBarComposable(title = topAppBarTitle, onNavigateUp = onNavigateUp)
|
TopAppBarComposable(
|
||||||
|
title = topAppBarTitle,
|
||||||
|
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
||||||
|
onNavigateUp = onNavigateUp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(
|
Column(
|
||||||
@@ -90,7 +109,7 @@ private fun ScreenContent(
|
|||||||
count = reviews.itemCount,
|
count = reviews.itemCount,
|
||||||
key = reviews.itemKey { it.commentId }
|
key = reviews.itemKey { it.commentId }
|
||||||
) { index ->
|
) { index ->
|
||||||
ReviewComposable(review = reviews[index]!!)
|
reviews[index]?.let { review -> ReviewComposable(review = review) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material3.Scaffold
|
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.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -18,6 +21,8 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
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.gplayapi.data.models.Artwork
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.compose.composables.TopAppBarComposable
|
import com.aurora.store.compose.composables.TopAppBarComposable
|
||||||
@@ -28,12 +33,18 @@ import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
|||||||
fun DetailsScreenshotScreen(
|
fun DetailsScreenshotScreen(
|
||||||
index: Int,
|
index: Int,
|
||||||
onNavigateUp: () -> Unit,
|
onNavigateUp: () -> Unit,
|
||||||
viewModel: AppDetailsViewModel = hiltViewModel()
|
viewModel: AppDetailsViewModel = hiltViewModel(),
|
||||||
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
) {
|
) {
|
||||||
val app by viewModel.app.collectAsStateWithLifecycle()
|
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(
|
ScreenContent(
|
||||||
topAppBarTitle = app!!.displayName,
|
topAppBarTitle = topAppBarTitle,
|
||||||
onNavigateUp = onNavigateUp,
|
onNavigateUp = onNavigateUp,
|
||||||
screenshots = app!!.screenshots,
|
screenshots = app!!.screenshots,
|
||||||
index = index
|
index = index
|
||||||
@@ -45,14 +56,23 @@ private fun ScreenContent(
|
|||||||
topAppBarTitle: String? = null,
|
topAppBarTitle: String? = null,
|
||||||
onNavigateUp: () -> Unit = {},
|
onNavigateUp: () -> Unit = {},
|
||||||
screenshots: List<Artwork> = emptyList(),
|
screenshots: List<Artwork> = emptyList(),
|
||||||
index: Int = 0
|
index: Int = 0,
|
||||||
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
) {
|
) {
|
||||||
val displayMetrics = LocalContext.current.resources.displayMetrics
|
val displayMetrics = LocalContext.current.resources.displayMetrics
|
||||||
val pagerState = rememberPagerState(initialPage = index) { screenshots.size }
|
val pagerState = rememberPagerState(initialPage = index) { screenshots.size }
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = index) {
|
||||||
|
if (pagerState.currentPage != index) pagerState.scrollToPage(index)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBarComposable(title = topAppBarTitle, onNavigateUp = onNavigateUp)
|
TopAppBarComposable(
|
||||||
|
title = topAppBarTitle,
|
||||||
|
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
|
||||||
|
onNavigateUp = onNavigateUp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ sealed class BusEvent : Event() {
|
|||||||
lateinit var error: String
|
lateinit var error: String
|
||||||
|
|
||||||
data class Blacklisted(val packageName: String) : BusEvent()
|
data class Blacklisted(val packageName: String) : BusEvent()
|
||||||
data class ManualDownload(val packageName: String, val versionCode: Long) : BusEvent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class AuthEvent : Event() {
|
sealed class AuthEvent : Event() {
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ object Preferences {
|
|||||||
const val PREFERENCE_THEME_STYLE = "PREFERENCE_THEME_STYLE"
|
const val PREFERENCE_THEME_STYLE = "PREFERENCE_THEME_STYLE"
|
||||||
const val PREFERENCE_FOR_YOU = "PREFERENCE_FOR_YOU"
|
const val PREFERENCE_FOR_YOU = "PREFERENCE_FOR_YOU"
|
||||||
const val PREFERENCE_DEFAULT_SELECTED_TAB = "PREFERENCE_DEFAULT_SELECTED_TAB"
|
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_INTRO = "PREFERENCE_INTRO"
|
||||||
|
|
||||||
const val PREFERENCE_FILTER_FDROID = "PREFERENCE_FILTER_FDROID"
|
const val PREFERENCE_FILTER_FDROID = "PREFERENCE_FILTER_FDROID"
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<EpoxyModel<*>> {
|
|
||||||
val models = ArrayList<EpoxyModel<*>>()
|
|
||||||
val clusterViewModels = mutableListOf<EpoxyModel<*>>()
|
|
||||||
val screenshotsViewModels = mutableListOf<EpoxyModel<*>>()
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ViewExodusBinding>(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() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ViewFileBinding>(context, attrs, defStyleAttr) {
|
|
||||||
|
|
||||||
@ModelProp
|
|
||||||
fun file(file: PlayFile) {
|
|
||||||
binding.line1.text = file.name
|
|
||||||
binding.line2.text = CommonUtil.addSiPrefix(file.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ViewScreenshotLargeBinding>(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ViewScreenshotMiniBinding>(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ViewReviewBinding>(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ViewScreenshotBinding>(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -121,15 +121,6 @@ abstract class BaseFragment<ViewBindingType : ViewBinding> : Fragment() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openScreenshotFragment(app: App, position: Int) {
|
|
||||||
findNavController().navigate(
|
|
||||||
MobileNavigationDirections.actionGlobalScreenshotFragment(
|
|
||||||
position,
|
|
||||||
app.screenshots.toTypedArray()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun openAppMenuSheet(app: MinimalApp) {
|
fun openAppMenuSheet(app: MinimalApp) {
|
||||||
findNavController().navigate(MobileNavigationDirections.actionGlobalAppMenuSheet(app))
|
findNavController().navigate(MobileNavigationDirections.actionGlobalAppMenuSheet(app))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,11 @@ import android.view.View
|
|||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
||||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
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.AppProgressViewModel_
|
||||||
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
|
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.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
||||||
import com.aurora.store.viewmodel.browse.ExpandedStreamBrowseViewModel
|
import com.aurora.store.viewmodel.browse.ExpandedStreamBrowseViewModel
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@@ -92,30 +88,6 @@ class ExpandedStreamBrowseFragment : BaseFragment<FragmentGenericWithToolbarBind
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
streamCluster.clusterAppList.forEach {
|
streamCluster.clusterAppList.forEach {
|
||||||
val screenshotsViewModels = mutableListOf<EpoxyModel<*>>()
|
|
||||||
|
|
||||||
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(
|
add(
|
||||||
AppListViewModel_()
|
AppListViewModel_()
|
||||||
.id(it.packageName.hashCode())
|
.id(it.packageName.hashCode())
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ import com.aurora.store.AppStreamStash
|
|||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.MainActivity
|
import com.aurora.store.MainActivity
|
||||||
import com.aurora.store.R
|
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.Event
|
||||||
import com.aurora.store.data.event.InstallerEvent
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.data.installer.AppInstaller
|
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.FlavouredUtil
|
||||||
import com.aurora.store.util.PackageUtil
|
import com.aurora.store.util.PackageUtil
|
||||||
import com.aurora.store.util.Preferences
|
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.Preferences.PREFERENCE_UPDATES_EXTENDED
|
||||||
import com.aurora.store.util.ShortcutManagerUtil
|
import com.aurora.store.util.ShortcutManagerUtil
|
||||||
import com.aurora.store.view.custom.RatingView
|
import com.aurora.store.view.custom.RatingView
|
||||||
import com.aurora.store.view.epoxy.controller.DetailsCarouselController
|
import com.aurora.store.view.epoxy.controller.DetailsCarouselController
|
||||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
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.view.ui.commons.BaseFragment
|
||||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||||
import com.aurora.store.viewmodel.details.DetailsClusterViewModel
|
import com.aurora.store.viewmodel.details.DetailsClusterViewModel
|
||||||
@@ -114,8 +110,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
|
|
||||||
private val isExtendedUpdateEnabled: Boolean
|
private val isExtendedUpdateEnabled: Boolean
|
||||||
get() = Preferences.getBoolean(requireContext(), PREFERENCE_UPDATES_EXTENDED)
|
get() = Preferences.getBoolean(requireContext(), PREFERENCE_UPDATES_EXTENDED)
|
||||||
private val showSimilarApps: Boolean
|
|
||||||
get() = Preferences.getBoolean(requireContext(), PREFERENCE_SIMILAR)
|
|
||||||
|
|
||||||
private fun onEvent(event: Event) {
|
private fun onEvent(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
@@ -131,20 +125,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 -> {
|
is InstallerEvent.Failed -> {
|
||||||
if (app.packageName == event.packageName) {
|
if (app.packageName == event.packageName) {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
@@ -241,16 +221,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
text =
|
text =
|
||||||
"${report.trackers.size} ${getString(R.string.exodus_substring)} ${report.version}"
|
"${report.trackers.size} ${getString(R.string.exodus_substring)} ${report.version}"
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.layoutDetailsPrivacy.headerPrivacy.addClickListener {
|
|
||||||
findNavController().navigate(
|
|
||||||
AppDetailsFragmentDirections
|
|
||||||
.actionAppDetailsFragmentToDetailsExodusFragment(
|
|
||||||
app.displayName,
|
|
||||||
report
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
binding.layoutDetailsPrivacy.txtStatus.apply {
|
binding.layoutDetailsPrivacy.txtStatus.apply {
|
||||||
setTextColor(ContextCompat.getColor(requireContext(), R.color.colorGreen))
|
setTextColor(ContextCompat.getColor(requireContext(), R.color.colorGreen))
|
||||||
@@ -618,16 +588,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
purchase(app)
|
purchase(app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.layoutDetailsApp.btnSecondaryAction.apply {
|
|
||||||
text = getString(R.string.title_manual_download)
|
|
||||||
setOnClickListener {
|
|
||||||
findNavController().navigate(
|
|
||||||
AppDetailsFragmentDirections
|
|
||||||
.actionAppDetailsFragmentToManualDownloadSheet(app)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lay out the toolbar again
|
// Lay out the toolbar again
|
||||||
@@ -651,7 +611,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateExtraDetails(app: App) {
|
private fun updateExtraDetails(app: App) {
|
||||||
binding.viewFlipper.displayedChild = 1
|
|
||||||
|
|
||||||
updateAppDescription(app)
|
updateAppDescription(app)
|
||||||
updateAppRatingAndReviews(app)
|
updateAppRatingAndReviews(app)
|
||||||
@@ -671,10 +630,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
updateBetaSubscription(app)
|
updateBetaSubscription(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSimilarApps) {
|
|
||||||
updateAppStream(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
checkAndSetupInstall()
|
checkAndSetupInstall()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,45 +661,11 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
private fun updateAppRatingAndReviews(app: App) {
|
||||||
binding.layoutDetailsReview.apply {
|
binding.layoutDetailsReview.apply {
|
||||||
headerRatingReviews.addClickListener {
|
|
||||||
findNavController().navigate(
|
|
||||||
AppDetailsFragmentDirections.actionAppDetailsFragmentToDetailsReviewFragment(
|
|
||||||
app.displayName,
|
|
||||||
app.packageName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalStars = 0L
|
var totalStars = 0L
|
||||||
totalStars += app.rating.oneStar
|
totalStars += app.rating.oneStar
|
||||||
@@ -833,15 +754,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
imgBeta.load(betaProgram.artwork.url) {
|
imgBeta.load(betaProgram.artwork.url) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
btnBetaAction.setOnClickListener {
|
|
||||||
btnBetaAction.text = getString(R.string.action_pending)
|
|
||||||
btnBetaAction.isEnabled = false
|
|
||||||
viewModel.fetchTestingProgramStatus(
|
|
||||||
app.packageName,
|
|
||||||
!betaProgram.isSubscribed
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
root.hide()
|
root.hide()
|
||||||
}
|
}
|
||||||
@@ -956,7 +868,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
|
|
||||||
private fun updateCompatibilityInfo() {
|
private fun updateCompatibilityInfo() {
|
||||||
if (app.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS)) {
|
if (app.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS)) {
|
||||||
viewModel.fetchPlexusReport(app.packageName)
|
|
||||||
|
|
||||||
binding.layoutDetailsCompatibility.txtGmsDependency.apply {
|
binding.layoutDetailsCompatibility.txtGmsDependency.apply {
|
||||||
title = getString(R.string.details_compatibility_gms_required_title)
|
title = getString(R.string.details_compatibility_gms_required_title)
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<FragmentGenericWithToolbarBinding>() {
|
|
||||||
|
|
||||||
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<ExodusTracker>) {
|
|
||||||
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<ExodusTracker> {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<FragmentDetailsMoreBinding>() {
|
|
||||||
|
|
||||||
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())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<FragmentDetailsReviewBinding>() {
|
|
||||||
|
|
||||||
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")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,6 @@ import com.aurora.gplayapi.data.models.details.DevStream
|
|||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.ViewState
|
||||||
import com.aurora.store.databinding.FragmentDevProfileBinding
|
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.epoxy.controller.GenericCarouselController
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
import com.aurora.store.view.ui.commons.BaseFragment
|
||||||
import com.aurora.store.viewmodel.details.DevProfileViewModel
|
import com.aurora.store.viewmodel.details.DevProfileViewModel
|
||||||
@@ -47,7 +46,6 @@ class DevProfileFragment : BaseFragment<FragmentDevProfileBinding>(),
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val developerCarouselController = DeveloperCarouselController(this)
|
|
||||||
|
|
||||||
// Toolbar
|
// Toolbar
|
||||||
binding.toolbar.apply {
|
binding.toolbar.apply {
|
||||||
@@ -56,7 +54,6 @@ class DevProfileFragment : BaseFragment<FragmentDevProfileBinding>(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RecyclerView
|
// RecyclerView
|
||||||
binding.recycler.setController(developerCarouselController)
|
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
when (it) {
|
when (it) {
|
||||||
@@ -82,7 +79,6 @@ class DevProfileFragment : BaseFragment<FragmentDevProfileBinding>(),
|
|||||||
binding.txtDevDescription.text = description
|
binding.txtDevDescription.text = description
|
||||||
binding.imgIcon.load(imgUrl)
|
binding.imgIcon.load(imgUrl)
|
||||||
binding.viewFlipper.displayedChild = 0
|
binding.viewFlipper.displayedChild = 0
|
||||||
developerCarouselController.setData(streamBundle)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<FragmentScreenshotBinding>() {
|
|
||||||
|
|
||||||
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<Artwork>, position: Int) {
|
|
||||||
binding.recyclerView.withModels {
|
|
||||||
artworks.forEach {
|
|
||||||
add(
|
|
||||||
LargeScreenshotViewModel_()
|
|
||||||
.id(it.url)
|
|
||||||
.artwork(it)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
binding.recyclerView.scrollToPosition(position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2019, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
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<SheetManualDownloadBinding>() {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,11 +7,10 @@
|
|||||||
package com.aurora.store.viewmodel.details
|
package com.aurora.store.viewmodel.details
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageInstaller
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.paging.PagingData
|
|
||||||
import androidx.paging.cachedIn
|
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.Review
|
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.BuildConfig
|
||||||
import com.aurora.store.data.helper.DownloadHelper
|
import com.aurora.store.data.helper.DownloadHelper
|
||||||
import com.aurora.store.data.model.ExodusReport
|
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.PlexusReport
|
||||||
import com.aurora.store.data.model.Report
|
import com.aurora.store.data.model.Report
|
||||||
import com.aurora.store.data.model.Scores
|
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.providers.AuthProvider
|
||||||
import com.aurora.store.data.room.favourite.Favourite
|
import com.aurora.store.data.room.favourite.Favourite
|
||||||
import com.aurora.store.data.room.favourite.FavouriteDao
|
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
|
||||||
import com.aurora.store.util.PackageUtil.PACKAGE_NAME_GMS
|
import com.aurora.store.util.PackageUtil.PACKAGE_NAME_GMS
|
||||||
import com.aurora.store.util.Preferences
|
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.Preferences.PREFERENCE_UPDATES_EXTENDED
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
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.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@@ -68,8 +63,7 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
private val downloadHelper: DownloadHelper,
|
private val downloadHelper: DownloadHelper,
|
||||||
private val favouriteDao: FavouriteDao,
|
private val favouriteDao: FavouriteDao,
|
||||||
private val httpClient: IHttpClient,
|
private val httpClient: IHttpClient,
|
||||||
private val json: Json,
|
private val json: Json
|
||||||
private val exodusTrackers: JSONObject
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val TAG = AppDetailsViewModel::class.java.simpleName
|
private val TAG = AppDetailsViewModel::class.java.simpleName
|
||||||
@@ -77,9 +71,8 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
private val _app = MutableStateFlow<App?>(App(""))
|
private val _app = MutableStateFlow<App?>(App(""))
|
||||||
val app = _app.asStateFlow()
|
val app = _app.asStateFlow()
|
||||||
|
|
||||||
private var reviewsNextPageUrl: String? = null
|
private val _suggestions = MutableStateFlow<List<App>>(emptyList())
|
||||||
private val _reviews = MutableStateFlow<PagingData<Review>>(PagingData.empty())
|
val suggestions = _suggestions.asStateFlow()
|
||||||
val reviews = _reviews.asStateFlow()
|
|
||||||
|
|
||||||
private val _userReview = MutableStateFlow<Review?>(null)
|
private val _userReview = MutableStateFlow<Review?>(null)
|
||||||
val userReview = _userReview.asStateFlow()
|
val userReview = _userReview.asStateFlow()
|
||||||
@@ -107,6 +100,27 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
list.find { d -> d.packageName == a?.packageName }
|
list.find { d -> d.packageName == a?.packageName }
|
||||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
||||||
|
|
||||||
|
// TODO: Save sessionId to downloads to monitor packageInstall progress
|
||||||
|
val installProgress = callbackFlow<Float?> {
|
||||||
|
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
|
private val isExtendedUpdateEnabled: Boolean
|
||||||
get() = Preferences.getBoolean(context, PREFERENCE_UPDATES_EXTENDED)
|
get() = Preferences.getBoolean(context, PREFERENCE_UPDATES_EXTENDED)
|
||||||
|
|
||||||
@@ -114,7 +128,7 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
get() = PackageUtil.isUpdatable(
|
get() = PackageUtil.isUpdatable(
|
||||||
context,
|
context,
|
||||||
app.value!!.packageName,
|
app.value!!.packageName,
|
||||||
app.value!!.versionCode.toLong()
|
app.value!!.versionCode
|
||||||
)
|
)
|
||||||
|
|
||||||
private val hasValidCerts: Boolean
|
private val hasValidCerts: Boolean
|
||||||
@@ -128,15 +142,9 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
val hasValidUpdate: Boolean
|
val hasValidUpdate: Boolean
|
||||||
get() = (isUpdatable && hasValidCerts) || (isUpdatable && isExtendedUpdateEnabled)
|
get() = (isUpdatable && hasValidCerts) || (isUpdatable && isExtendedUpdateEnabled)
|
||||||
|
|
||||||
val showSimilarApps: Boolean
|
|
||||||
get() = Preferences.getBoolean(context, PREFERENCE_SIMILAR)
|
|
||||||
|
|
||||||
fun fetchAppDetails(packageName: String) {
|
fun fetchAppDetails(packageName: String) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
_favourite.value = favouriteDao.isFavourite(packageName)
|
|
||||||
|
|
||||||
_dataSafetyReport.value = webDataSafetyHelper.fetch(packageName)
|
|
||||||
_app.value = appDetailsHelper.getAppByPackageName(packageName).copy(
|
_app.value = appDetailsHelper.getAppByPackageName(packageName).copy(
|
||||||
isInstalled = PackageUtil.isInstalled(context, packageName)
|
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
|
// Only proceed if there was no error while fetching the app details
|
||||||
if (throwable != null) return@invokeOnCompletion
|
if (throwable != null) return@invokeOnCompletion
|
||||||
|
|
||||||
fetchReviews()
|
fetchFavourite(packageName)
|
||||||
|
fetchDataSafetyReport(packageName)
|
||||||
|
fetchSuggestions()
|
||||||
fetchExodusPrivacyReport(packageName)
|
fetchExodusPrivacyReport(packageName)
|
||||||
if (app.value!!.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS)) {
|
if (app.value!!.dependencies.dependentPackages.contains(PACKAGE_NAME_GMS)) {
|
||||||
fetchPlexusReport(packageName)
|
fetchPlexusReport(packageName)
|
||||||
@@ -156,48 +166,11 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchReviews(filter: Review.Filter = Review.Filter.ALL) {
|
fun updateTestingProgramStatus(packageName: String, subscribe: Boolean) {
|
||||||
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) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
_exodusReport.value = getLatestExodusReport(packageName)
|
_testingProgramStatus.value =
|
||||||
} catch (exception: Exception) {
|
appDetailsHelper.testingProgram(packageName, subscribe)
|
||||||
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)
|
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Failed to fetch testing program status", exception)
|
Log.e(TAG, "Failed to fetch testing program status", exception)
|
||||||
}
|
}
|
||||||
@@ -272,24 +245,50 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getExodusTrackersFromReport(): List<ExodusTracker> {
|
private fun fetchFavourite(packageName: String) {
|
||||||
val trackerObjects = exodusReport.value!!.trackers.map {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
exodusTrackers.getJSONObject(it.toString())
|
_favourite.value = favouriteDao.isFavourite(packageName)
|
||||||
}.toList()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return trackerObjects.map {
|
private fun fetchDataSafetyReport(packageName: String) {
|
||||||
ExodusTracker(
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
id = it.getInt("id"),
|
_dataSafetyReport.value = webDataSafetyHelper.fetch(packageName)
|
||||||
name = it.getString("name"),
|
}
|
||||||
url = it.getString("website"),
|
}
|
||||||
signature = it.getString("code_signature"),
|
|
||||||
date = it.getString("creation_date"),
|
private fun fetchSuggestions() {
|
||||||
description = it.getString("description"),
|
// Bail out if we go no suggestions to offer
|
||||||
networkSignature = it.getString("network_signature"),
|
if (app.value!!.detailsStreamUrl.isNullOrBlank()) return
|
||||||
documentation = listOf(it.getString("documentation")),
|
|
||||||
categories = listOf(it.getString("categories"))
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
)
|
val streamBundle = appDetailsHelper.getDetailsStream(app.value!!.detailsStreamUrl!!)
|
||||||
}.toList()
|
_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? {
|
private fun getLatestExodusReport(packageName: String): Report? {
|
||||||
@@ -305,12 +304,6 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
.firstOrNull()
|
.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<PlexusReport>(String(playResponse.responseBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseExodusResponse(response: String, packageName: String): List<Report> {
|
private fun parseExodusResponse(response: String, packageName: String): List<Report> {
|
||||||
try {
|
try {
|
||||||
val jsonObject = JSONObject(response)
|
val jsonObject = JSONObject(response)
|
||||||
@@ -322,4 +315,10 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
return emptyList()
|
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<PlexusReport>(String(playResponse.responseBytes))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,56 @@
|
|||||||
package com.aurora.store.viewmodel.details
|
package com.aurora.store.viewmodel.details
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
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 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 org.json.JSONObject
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel(assistedFactory = DetailsExodusViewModel.Factory::class)
|
||||||
class DetailsExodusViewModel @Inject constructor(val exodusTrackers: JSONObject) : ViewModel()
|
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<List<ExodusTracker>>(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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
package com.aurora.store.viewmodel.details
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -5,36 +11,42 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
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 dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel(assistedFactory = DetailsMoreViewModel.Factory::class)
|
||||||
class DetailsMoreViewModel @Inject constructor(
|
class DetailsMoreViewModel @AssistedInject constructor(
|
||||||
|
@Assisted private val dependencies: List<String>,
|
||||||
private val appDetailsHelper: AppDetailsHelper
|
private val appDetailsHelper: AppDetailsHelper
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(dependencies: List<String>): DetailsMoreViewModel
|
||||||
|
}
|
||||||
|
|
||||||
private val TAG = DetailsMoreViewModel::class.java.simpleName
|
private val TAG = DetailsMoreViewModel::class.java.simpleName
|
||||||
|
|
||||||
private val dependantAppsStash = mutableMapOf<String, List<App>>()
|
private val _dependentApps = MutableStateFlow<List<App>?>(emptyList())
|
||||||
private val _dependentApps = MutableSharedFlow<List<App>>()
|
val dependentApps = _dependentApps.asStateFlow()
|
||||||
val dependentApps = _dependentApps.asSharedFlow()
|
|
||||||
|
|
||||||
fun fetchDependentApps(app: App) {
|
init {
|
||||||
|
fetchDependencies()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchDependencies() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val dependantApps = dependantAppsStash.getOrPut(app.packageName) {
|
_dependentApps.value = appDetailsHelper.getAppByPackageName(dependencies)
|
||||||
appDetailsHelper.getAppByPackageName(app.dependencies.dependentPackages)
|
|
||||||
}
|
|
||||||
|
|
||||||
_dependentApps.emit(dependantApps)
|
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Failed to fetch dependencies", exception)
|
Log.e(TAG, "Failed to fetch dependencies", exception)
|
||||||
dependantAppsStash[app.packageName] = emptyList()
|
_dependentApps.value = null
|
||||||
_dependentApps.emit(emptyList())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Review>>(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
*
|
|
||||||
* Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ReviewCluster> = 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<Boolean>()
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
14
app/src/main/res/drawable/ic_experiment.xml
Normal file
14
app/src/main/res/drawable/ic_experiment.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: Material Design Authors / Google LLC
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M200,840Q149,840 127.5,794.5Q106,749 138,710L360,440L360,200L320,200Q303,200 291.5,188.5Q280,177 280,160Q280,143 291.5,131.5Q303,120 320,120L640,120Q657,120 668.5,131.5Q680,143 680,160Q680,177 668.5,188.5Q657,200 640,200L600,200L600,440L822,710Q854,749 832.5,794.5Q811,840 760,840L200,840ZM280,720L680,720L544,560L416,560L280,720ZM200,760L760,760L520,468L520,200L440,200L440,468L200,760ZM480,480L480,480L480,480L480,480L480,480L480,480Z"/>
|
||||||
|
</vector>
|
||||||
14
app/src/main/res/drawable/ic_suggestions.xml
Normal file
14
app/src/main/res/drawable/ic_suggestions.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: Material Design Authors / Google LLC
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M480,880Q447,880 423.5,856.5Q400,833 400,800L560,800Q560,833 536.5,856.5Q513,880 480,880ZM320,760L320,680L640,680L640,760L320,760ZM330,640Q261,599 220.5,530Q180,461 180,380Q180,255 267.5,167.5Q355,80 480,80Q605,80 692.5,167.5Q780,255 780,380Q780,461 739.5,530Q699,599 630,640L330,640ZM354,560L606,560Q651,528 675.5,481Q700,434 700,380Q700,288 636,224Q572,160 480,160Q388,160 324,224Q260,288 260,380Q260,434 284.5,481Q309,528 354,560ZM480,560Q480,560 480,560Q480,560 480,560Q480,560 480,560Q480,560 480,560Q480,560 480,560Q480,560 480,560Q480,560 480,560Q480,560 480,560Z"/>
|
||||||
|
</vector>
|
||||||
@@ -56,74 +56,57 @@
|
|||||||
android:id="@+id/layout_details_app"
|
android:id="@+id/layout_details_app"
|
||||||
layout="@layout/layout_details_app" />
|
layout="@layout/layout_details_app" />
|
||||||
|
|
||||||
<ViewFlipper
|
<LinearLayout
|
||||||
android:id="@+id/view_flipper"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:divider="@drawable/divider"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:showDividers="middle">
|
||||||
|
|
||||||
<RelativeLayout
|
<include
|
||||||
|
android:id="@+id/layout_detail_description"
|
||||||
|
layout="@layout/layout_details_description" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/layout_details_review"
|
||||||
|
layout="@layout/layout_details_review" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/layout_details_beta"
|
||||||
|
layout="@layout/layout_details_beta" />
|
||||||
|
|
||||||
|
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
|
android:id="@+id/epoxy_recycler_stream"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="320dp">
|
android:clipToPadding="false"
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:divider="@drawable/divider"
|
|
||||||
android:orientation="vertical"
|
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" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/layout_detail_description"
|
android:id="@+id/layout_details_compatibility"
|
||||||
layout="@layout/layout_details_description" />
|
layout="@layout/layout_details_compatibility" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/layout_details_review"
|
android:id="@+id/layout_details_permissions"
|
||||||
layout="@layout/layout_details_review" />
|
layout="@layout/layout_details_permissions" />
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/layout_details_beta"
|
android:id="@+id/layout_details_data_safety"
|
||||||
layout="@layout/layout_details_beta" />
|
layout="@layout/layout_details_data_safety" />
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<include
|
||||||
android:id="@+id/epoxy_recycler_stream"
|
android:id="@+id/layout_details_privacy"
|
||||||
android:layout_width="match_parent"
|
layout="@layout/layout_details_privacy" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:overScrollMode="never"
|
|
||||||
android:scrollbars="none"
|
|
||||||
app:itemSpacing="@dimen/margin_small"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:listitem="@layout/view_app" />
|
|
||||||
|
|
||||||
<include
|
<include
|
||||||
android:id="@+id/layout_details_compatibility"
|
android:id="@+id/layout_details_dev"
|
||||||
layout="@layout/layout_details_compatibility" />
|
layout="@layout/layout_details_dev" />
|
||||||
|
</LinearLayout>
|
||||||
<include
|
|
||||||
android:id="@+id/layout_details_permissions"
|
|
||||||
layout="@layout/layout_details_permissions" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/layout_details_data_safety"
|
|
||||||
layout="@layout/layout_details_data_safety" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/layout_details_privacy"
|
|
||||||
layout="@layout/layout_details_privacy" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/layout_details_dev"
|
|
||||||
layout="@layout/layout_details_dev" />
|
|
||||||
</LinearLayout>
|
|
||||||
</ViewFlipper>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:context=".view.ui.details.DetailsMoreFragment">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="?attr/actionBarSize"
|
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fillViewport="true">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:divider="@drawable/divider"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:showDividers="middle">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:divider="@drawable/divider"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingStart="@dimen/padding_small"
|
|
||||||
android:paddingEnd="@dimen/padding_normal"
|
|
||||||
android:showDividers="middle">
|
|
||||||
|
|
||||||
<com.aurora.store.view.custom.layouts.ActionHeaderLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:headerTitle="@string/details_description" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/txt_description"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:autoLink="all"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
|
||||||
android:textIsSelectable="true" />
|
|
||||||
|
|
||||||
<com.aurora.store.view.custom.layouts.ActionHeaderLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:headerTitle="@string/details_dependencies" />
|
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
|
||||||
android:id="@+id/recycler_dependency"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clipToPadding="true"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:overScrollMode="never"
|
|
||||||
android:scrollbars="none"
|
|
||||||
app:itemSpacing="@dimen/margin_small"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="4"
|
|
||||||
tools:listitem="@layout/view_app_dependent" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
|
||||||
android:id="@+id/recycler_more"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="true"
|
|
||||||
android:nestedScrollingEnabled="false"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:overScrollMode="never"
|
|
||||||
android:scrollbars="none"
|
|
||||||
app:itemSpacing="@dimen/margin_small"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="4"
|
|
||||||
tools:listitem="@layout/view_file" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:context=".view.ui.details.ScreenshotFragment">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="?attr/actionBarSize"
|
|
||||||
android:translationZ="1dp"
|
|
||||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
|
||||||
android:id="@+id/recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:listitem="@layout/view_screenshot" />
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inflatedId="@+id/header_view" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inflatedId="@+id/recycler_view_01" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inflatedId="@+id/recycler_view_02" />
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:divider="@drawable/divider"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="@dimen/padding_large"
|
|
||||||
android:showDividers="middle">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/img_icon"
|
|
||||||
android:layout_width="@dimen/icon_size_category"
|
|
||||||
android:layout_height="@dimen/icon_size_category"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
tools:src="@drawable/bg_placeholder" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txt_header"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:layout_marginStart="@dimen/margin_normal"
|
|
||||||
android:layout_marginBottom="@dimen/margin_normal"
|
|
||||||
android:layout_toEndOf="@id/img_icon"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@string/title_manual_download"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.SubTitle" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txt_line1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
|
||||||
tools:text="App Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txt_line2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/txt_line1"
|
|
||||||
android:layout_alignStart="@id/txt_line1"
|
|
||||||
android:layout_alignEnd="@id/txt_line1"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line2"
|
|
||||||
android:textColor="?colorAccent"
|
|
||||||
tools:text="Package Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txt_line3"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/txt_line2"
|
|
||||||
android:layout_alignStart="@id/txt_line1"
|
|
||||||
android:layout_alignEnd="@id/txt_line1"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line3"
|
|
||||||
tools:text="Base version" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/version_code_layout"
|
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:helperText="@string/manual_download_hint">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/version_code_inp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:imeOptions="actionDone"
|
|
||||||
android:inputType="number" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:weightSum="2">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btn_secondary"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton.Dialog.Flush"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/height_button"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/action_cancel" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btn_primary"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton.Dialog.Flush"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="@dimen/height_button"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="@string/action_install" />
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</FrameLayout>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="@dimen/padding_small"
|
|
||||||
android:textIsSelectable="true">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/txt_title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
tools:text="Line1" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/txt_subtitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/txt_title"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line2"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
tools:text="Line2" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/txt_description"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/txt_subtitle"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line3"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
tools:text="Line3" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="@dimen/padding_xsmall"
|
|
||||||
android:paddingStart="@dimen/padding_small"
|
|
||||||
android:paddingEnd="@dimen/padding_xsmall">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/line1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
|
||||||
tools:text="File Name" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/line2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/line1"
|
|
||||||
android:layout_alignStart="@id/line1"
|
|
||||||
android:layout_alignEnd="@id/line1"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.Line2"
|
|
||||||
tools:text="File Size" />
|
|
||||||
</RelativeLayout>
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="@dimen/padding_xsmall">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/img"
|
|
||||||
android:layout_width="@dimen/icon_size_small"
|
|
||||||
android:layout_height="@dimen/icon_size_small"
|
|
||||||
android:layout_marginEnd="@dimen/margin_small" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/txt_author"
|
|
||||||
style="@style/TextAppearance.Aurora.Line1"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_toEndOf="@id/img"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
android:textSize="14sp"
|
|
||||||
tools:text="Author Name" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/txt_time"
|
|
||||||
style="@style/TextAppearance.Aurora.Line3"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/txt_author"
|
|
||||||
android:layout_alignStart="@id/txt_author"
|
|
||||||
android:layout_alignEnd="@id/txt_author"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textAlignment="viewStart"
|
|
||||||
tools:text="Date" />
|
|
||||||
|
|
||||||
<RatingBar
|
|
||||||
android:id="@+id/rating"
|
|
||||||
style="@style/Widget.AppCompat.RatingBar.Small"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/txt_time"
|
|
||||||
android:layout_alignStart="@id/txt_author"
|
|
||||||
android:numStars="5" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
|
||||||
android:id="@+id/txt_comment"
|
|
||||||
style="@style/TextAppearance.Aurora.Line2"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/img"
|
|
||||||
android:layout_alignStart="@id/txt_author"
|
|
||||||
android:layout_marginTop="@dimen/margin_xsmall"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="10"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
tools:text="Comment" />
|
|
||||||
</RelativeLayout>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/img"
|
|
||||||
android:layout_width="@dimen/screenshot_width"
|
|
||||||
android:layout_height="@dimen/screenshot_height"
|
|
||||||
android:adjustViewBounds="true" />
|
|
||||||
</RelativeLayout>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/img"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Aurora Store
|
|
||||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
~
|
|
||||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 2 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ Aurora Store is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
|
||||||
android:id="@+id/img"
|
|
||||||
android:layout_width="@dimen/screenshot_width_mini"
|
|
||||||
android:layout_height="@dimen/screenshot_height_mini"
|
|
||||||
android:adjustViewBounds="true" />
|
|
||||||
</RelativeLayout>
|
|
||||||
@@ -32,9 +32,6 @@
|
|||||||
<action
|
<action
|
||||||
android:id="@+id/action_global_expandedStreamBrowseFragment"
|
android:id="@+id/action_global_expandedStreamBrowseFragment"
|
||||||
app:destination="@id/expandedStreamBrowseFragment" />
|
app:destination="@id/expandedStreamBrowseFragment" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_global_screenshotFragment"
|
|
||||||
app:destination="@id/screenshotFragment" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_global_devProfileFragment"
|
android:id="@+id/action_global_devProfileFragment"
|
||||||
app:destination="@id/devProfileFragment" />
|
app:destination="@id/devProfileFragment" />
|
||||||
@@ -165,18 +162,11 @@
|
|||||||
<action
|
<action
|
||||||
android:id="@+id/action_appDetailsFragment_to_devAppsFragment"
|
android:id="@+id/action_appDetailsFragment_to_devAppsFragment"
|
||||||
app:destination="@id/devAppsFragment" />
|
app:destination="@id/devAppsFragment" />
|
||||||
<action
|
<argument
|
||||||
android:id="@+id/action_appDetailsFragment_to_detailsMoreFragment"
|
android:name="app"
|
||||||
app:destination="@id/detailsMoreFragment" />
|
android:defaultValue="@null"
|
||||||
<action
|
app:argType="com.aurora.gplayapi.data.models.App"
|
||||||
android:id="@+id/action_appDetailsFragment_to_detailsReviewFragment"
|
app:nullable="true" />
|
||||||
app:destination="@id/detailsReviewFragment" />
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_appDetailsFragment_to_detailsExodusFragment"
|
|
||||||
app:destination="@id/detailsExodusFragment" />
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_appDetailsFragment_to_manualDownloadSheet"
|
|
||||||
app:destination="@id/manualDownloadSheet" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_appDetailsFragment_to_permissionBottomSheet"
|
android:id="@+id/action_appDetailsFragment_to_permissionBottomSheet"
|
||||||
app:destination="@id/permissionBottomSheet" />
|
app:destination="@id/permissionBottomSheet" />
|
||||||
@@ -209,17 +199,6 @@
|
|||||||
android:name="expandedStreamUrl"
|
android:name="expandedStreamUrl"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
|
||||||
android:id="@+id/screenshotFragment"
|
|
||||||
android:name="com.aurora.store.view.ui.details.ScreenshotFragment"
|
|
||||||
tools:layout="@layout/fragment_screenshot">
|
|
||||||
<argument
|
|
||||||
android:name="position"
|
|
||||||
app:argType="integer" />
|
|
||||||
<argument
|
|
||||||
android:name="arrayOfArtwork"
|
|
||||||
app:argType="com.aurora.gplayapi.data.models.Artwork[]" />
|
|
||||||
</fragment>
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/devProfileFragment"
|
android:id="@+id/devProfileFragment"
|
||||||
android:name="com.aurora.store.view.ui.details.DevProfileFragment"
|
android:name="com.aurora.store.view.ui.details.DevProfileFragment"
|
||||||
@@ -253,36 +232,6 @@
|
|||||||
android:name="developerName"
|
android:name="developerName"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
|
||||||
android:id="@+id/detailsMoreFragment"
|
|
||||||
android:name="com.aurora.store.view.ui.details.DetailsMoreFragment"
|
|
||||||
tools:layout="@layout/fragment_details_more">
|
|
||||||
<argument
|
|
||||||
android:name="app"
|
|
||||||
app:argType="com.aurora.gplayapi.data.models.App" />
|
|
||||||
</fragment>
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/detailsReviewFragment"
|
|
||||||
android:name="com.aurora.store.view.ui.details.DetailsReviewFragment"
|
|
||||||
tools:layout="@layout/fragment_details_review">
|
|
||||||
<argument
|
|
||||||
android:name="displayName"
|
|
||||||
app:argType="string" />
|
|
||||||
<argument
|
|
||||||
android:name="packageName"
|
|
||||||
app:argType="string" />
|
|
||||||
</fragment>
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/detailsExodusFragment"
|
|
||||||
android:name="com.aurora.store.view.ui.details.DetailsExodusFragment"
|
|
||||||
tools:layout="@layout/fragment_generic_with_toolbar">
|
|
||||||
<argument
|
|
||||||
android:name="displayName"
|
|
||||||
app:argType="string" />
|
|
||||||
<argument
|
|
||||||
android:name="report"
|
|
||||||
app:argType="com.aurora.store.data.model.Report" />
|
|
||||||
</fragment>
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/splashFragment"
|
android:id="@+id/splashFragment"
|
||||||
android:name="com.aurora.store.view.ui.splash.SplashFragment"
|
android:name="com.aurora.store.view.ui.splash.SplashFragment"
|
||||||
@@ -397,15 +346,6 @@
|
|||||||
android:name="com.aurora.store.view.ui.sheets.FilterSheet"
|
android:name="com.aurora.store.view.ui.sheets.FilterSheet"
|
||||||
android:label="FilterSheet"
|
android:label="FilterSheet"
|
||||||
tools:layout="@layout/sheet_filter" />
|
tools:layout="@layout/sheet_filter" />
|
||||||
<dialog
|
|
||||||
android:id="@+id/manualDownloadSheet"
|
|
||||||
android:name="com.aurora.store.view.ui.sheets.ManualDownloadSheet"
|
|
||||||
android:label="ManualDownloadSheet"
|
|
||||||
tools:layout="@layout/sheet_manual_download">
|
|
||||||
<argument
|
|
||||||
android:name="app"
|
|
||||||
app:argType="com.aurora.gplayapi.data.models.App" />
|
|
||||||
</dialog>
|
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/permissionBottomSheet"
|
android:id="@+id/permissionBottomSheet"
|
||||||
android:name="com.aurora.store.view.ui.sheets.PermissionBottomSheet"
|
android:name="com.aurora.store.view.ui.sheets.PermissionBottomSheet"
|
||||||
|
|||||||
@@ -208,7 +208,6 @@
|
|||||||
<string name="title_library">المكتبة</string>
|
<string name="title_library">المكتبة</string>
|
||||||
<string name="insecure_anonymous_apply">تأكد من إعادة تسجيل الدخول وإعادة تشغيل التطبيق لتطبيق التغييرات.</string>
|
<string name="insecure_anonymous_apply">تأكد من إعادة تسجيل الدخول وإعادة تشغيل التطبيق لتطبيق التغييرات.</string>
|
||||||
<string name="pref_network_title">الشبكات</string>
|
<string name="pref_network_title">الشبكات</string>
|
||||||
<string name="pref_ui_similar_apps_desc">عرض مجموعات متشابهة وذات صلة في صفحة تفاصيل التطبيق</string>
|
|
||||||
<string name="pref_ui_similar_apps">تطبيقات مشابهة و ذات صلة</string>
|
<string name="pref_ui_similar_apps">تطبيقات مشابهة و ذات صلة</string>
|
||||||
<string name="pref_ui_no_for_you_desc">عرض صفحات لك على الشاشة الرئيسية</string>
|
<string name="pref_ui_no_for_you_desc">عرض صفحات لك على الشاشة الرئيسية</string>
|
||||||
<string name="pref_ui_no_for_you">صفحات لك</string>
|
<string name="pref_ui_no_for_you">صفحات لك</string>
|
||||||
|
|||||||
@@ -180,7 +180,6 @@
|
|||||||
<string name="pref_install_mode_am">Instalador AM</string>
|
<string name="pref_install_mode_am">Instalador AM</string>
|
||||||
<string name="pref_install_mode_summary">Seleiciona\'l métodu d\'instalación de los archivos APK</string>
|
<string name="pref_install_mode_summary">Seleiciona\'l métodu d\'instalación de los archivos APK</string>
|
||||||
<string name="pref_ui_similar_apps">Aplicaciones asemeyaes y rellacionaes</string>
|
<string name="pref_ui_similar_apps">Aplicaciones asemeyaes y rellacionaes</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Amuesa les aplicaciones asemeyaes y rellacionaes nes páxines de detalles de les aplicaciones</string>
|
|
||||||
<string name="tab_for_you">Pa ti</string>
|
<string name="tab_for_you">Pa ti</string>
|
||||||
<string name="title_installed">Instalóse</string>
|
<string name="title_installed">Instalóse</string>
|
||||||
<string name="title_installer">App Installer</string>
|
<string name="title_installer">App Installer</string>
|
||||||
|
|||||||
@@ -179,7 +179,6 @@
|
|||||||
<string name="pref_ui_layout">Tərtibat</string>
|
<string name="pref_ui_layout">Tərtibat</string>
|
||||||
<string name="pref_ui_layout_tab">İlkin səhifəni seç</string>
|
<string name="pref_ui_layout_tab">İlkin səhifəni seç</string>
|
||||||
<string name="pref_ui_similar_apps">Oxşar və əlaqəli tətbiqlər</string>
|
<string name="pref_ui_similar_apps">Oxşar və əlaqəli tətbiqlər</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Tətbiq təfərrüatları səhifəsində oxşar və əlaqəli qrupları göstər</string>
|
|
||||||
<string name="purchase_failed">Endirmə Alınmadı</string>
|
<string name="purchase_failed">Endirmə Alınmadı</string>
|
||||||
<string name="purchase_invalid">Tətbiq satın alınmayıb</string>
|
<string name="purchase_invalid">Tətbiq satın alınmayıb</string>
|
||||||
<string name="purchase_unsupported">Tətbiq dəstəklənmir</string>
|
<string name="purchase_unsupported">Tətbiq dəstəklənmir</string>
|
||||||
|
|||||||
@@ -118,7 +118,6 @@
|
|||||||
<string name="pref_network_proxy_url">URL проксі</string>
|
<string name="pref_network_proxy_url">URL проксі</string>
|
||||||
<string name="pref_network_proxy_url_message">Увядзіце сапраўдны URL для праходжання ўсяго трафіку праз проксі.</string>
|
<string name="pref_network_proxy_url_message">Увядзіце сапраўдны URL для праходжання ўсяго трафіку праз проксі.</string>
|
||||||
<string name="pref_ui_similar_apps">Падобнае</string>
|
<string name="pref_ui_similar_apps">Падобнае</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Паказваць падобныя ці звязаныя кластары на старонцы праграмы</string>
|
|
||||||
<string name="pref_aurora_only_desc">Не правяраць наяўнасць абнаўленняў для праграм, усталяваных з крыніц па-за межамі крамы Aurora</string>
|
<string name="pref_aurora_only_desc">Не правяраць наяўнасць абнаўленняў для праграм, усталяваных з крыніц па-за межамі крамы Aurora</string>
|
||||||
<string name="purchase_failed">Памылка спампоўкі</string>
|
<string name="purchase_failed">Памылка спампоўкі</string>
|
||||||
<string name="purchase_no_file">Не ўдалося атрымаць файл</string>
|
<string name="purchase_no_file">Не ўдалося атрымаць файл</string>
|
||||||
|
|||||||
@@ -139,7 +139,6 @@
|
|||||||
<string name="pref_install_mode_services">Услуги на Aurora</string>
|
<string name="pref_install_mode_services">Услуги на Aurora</string>
|
||||||
<string name="pref_install_mode_summary">Изберете режим на инсталиране на APK</string>
|
<string name="pref_install_mode_summary">Изберете режим на инсталиране на APK</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Показва страници за вас на началния екран</string>
|
<string name="pref_ui_no_for_you_desc">Показва страници за вас на началния екран</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Показва подобни и свързани клъстери на страницата с подробности за приложението</string>
|
|
||||||
<string name="purchase_failed">Изтеглянето се провали</string>
|
<string name="purchase_failed">Изтеглянето се провали</string>
|
||||||
<string name="purchase_unsupported">Приложението не се поддържа</string>
|
<string name="purchase_unsupported">Приложението не се поддържа</string>
|
||||||
<string name="purchase_not_found">Приложението не е намерено</string>
|
<string name="purchase_not_found">Приложението не е намерено</string>
|
||||||
|
|||||||
@@ -176,7 +176,6 @@
|
|||||||
<string name="pref_ui_similar_apps">Aplicacions similars i relacionades</string>
|
<string name="pref_ui_similar_apps">Aplicacions similars i relacionades</string>
|
||||||
<string name="purchase_unsupported">Aplicació no compatible</string>
|
<string name="purchase_unsupported">Aplicació no compatible</string>
|
||||||
<string name="purchase_not_found">No s\'ha trobat l\'aplicació</string>
|
<string name="purchase_not_found">No s\'ha trobat l\'aplicació</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Mostrar grups similars i relacionats a la pàgina detalls de l\'aplicació</string>
|
|
||||||
<string name="purchase_failed">Error en descarregar</string>
|
<string name="purchase_failed">Error en descarregar</string>
|
||||||
<string name="title_blacklist_manager">Gestor de llistes negres</string>
|
<string name="title_blacklist_manager">Gestor de llistes negres</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Visualitzar les Pàgines per a tu a la pantalla d\'inici</string>
|
<string name="pref_ui_no_for_you_desc">Visualitzar les Pàgines per a tu a la pantalla d\'inici</string>
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
<string name="purchase_unsupported">Aplikace není kompatibilní</string>
|
<string name="purchase_unsupported">Aplikace není kompatibilní</string>
|
||||||
<string name="purchase_invalid">Aplikace nebyla zakoupena</string>
|
<string name="purchase_invalid">Aplikace nebyla zakoupena</string>
|
||||||
<string name="purchase_failed">Stažení se nezdařilo</string>
|
<string name="purchase_failed">Stažení se nezdařilo</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Zobrazit podobné a související skupiny na stránce s detaily aplikace</string>
|
|
||||||
<string name="pref_ui_similar_apps">Podobné a související aplikace</string>
|
<string name="pref_ui_similar_apps">Podobné a související aplikace</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Zobrazit stránky pro vás na domovské obrazovce</string>
|
<string name="pref_ui_no_for_you_desc">Zobrazit stránky pro vás na domovské obrazovce</string>
|
||||||
<string name="pref_ui_no_for_you">Stránky pro vás</string>
|
<string name="pref_ui_no_for_you">Stránky pro vás</string>
|
||||||
|
|||||||
@@ -105,7 +105,6 @@
|
|||||||
<string name="pref_install_mode_am">AM-installationsprogram</string>
|
<string name="pref_install_mode_am">AM-installationsprogram</string>
|
||||||
<string name="pref_ui_layout">Layout</string>
|
<string name="pref_ui_layout">Layout</string>
|
||||||
<string name="purchase_not_found">Appen blev ikke fundet</string>
|
<string name="purchase_not_found">Appen blev ikke fundet</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Vis lignende og relaterede samlinger på appens detaljeside</string>
|
|
||||||
<string name="spoof_apply">Sørg for at logge ind igen for at anvende spoofen</string>
|
<string name="spoof_apply">Sørg for at logge ind igen for at anvende spoofen</string>
|
||||||
<string name="title_manual_download">Manuel download</string>
|
<string name="title_manual_download">Manuel download</string>
|
||||||
<string name="toast_apk_whitelisted">Hvidlistet</string>
|
<string name="toast_apk_whitelisted">Hvidlistet</string>
|
||||||
|
|||||||
@@ -208,7 +208,6 @@
|
|||||||
<string name="toast_purchase_blocked">App-Käufe sind für anonyme Konten nicht verfügbar.</string>
|
<string name="toast_purchase_blocked">App-Käufe sind für anonyme Konten nicht verfügbar.</string>
|
||||||
<string name="insecure_anonymous_apply">Bitte neu anmelden und die App neu starten, um die Änderungen zu übernehmen.</string>
|
<string name="insecure_anonymous_apply">Bitte neu anmelden und die App neu starten, um die Änderungen zu übernehmen.</string>
|
||||||
<string name="pref_network_title">Netzwerk</string>
|
<string name="pref_network_title">Netzwerk</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Ähnliche und verwandte Apps in der App-Detailansicht anzeigen</string>
|
|
||||||
<string name="pref_ui_similar_apps">Ähnliche Apps</string>
|
<string name="pref_ui_similar_apps">Ähnliche Apps</string>
|
||||||
<string name="pref_ui_no_for_you_desc">„Für dich“-Tab auf der Startseite anzeigen</string>
|
<string name="pref_ui_no_for_you_desc">„Für dich“-Tab auf der Startseite anzeigen</string>
|
||||||
<string name="pref_ui_no_for_you">„Für dich“-Tab</string>
|
<string name="pref_ui_no_for_you">„Für dich“-Tab</string>
|
||||||
|
|||||||
@@ -168,7 +168,6 @@
|
|||||||
<string name="pref_ui_title">Προσαρμογή</string>
|
<string name="pref_ui_title">Προσαρμογή</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Εμφάνιση σελίδων για εσάς στην αρχική οθόνη</string>
|
<string name="pref_ui_no_for_you_desc">Εμφάνιση σελίδων για εσάς στην αρχική οθόνη</string>
|
||||||
<string name="purchase_unsupported">Η εφαρμογή δεν υποστηρίζεται</string>
|
<string name="purchase_unsupported">Η εφαρμογή δεν υποστηρίζεται</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Εμφάνιση παρόμοιων και σχετικών ομάδων στη σελίδα λεπτομερειών εφαρμογής</string>
|
|
||||||
<string name="purchase_session_expired">Η συνεδρία έληξε, συνδεθείτε ξανά για να λάβετε νέα.</string>
|
<string name="purchase_session_expired">Η συνεδρία έληξε, συνδεθείτε ξανά για να λάβετε νέα.</string>
|
||||||
<string name="tab_top_paid">Κορυφαίες επί πληρωμή</string>
|
<string name="tab_top_paid">Κορυφαίες επί πληρωμή</string>
|
||||||
<string name="title_device">Συσκευή</string>
|
<string name="title_device">Συσκευή</string>
|
||||||
|
|||||||
@@ -176,7 +176,6 @@
|
|||||||
<string name="download_eta_min">ankoraŭ %1$d m %2$d s</string>
|
<string name="download_eta_min">ankoraŭ %1$d m %2$d s</string>
|
||||||
<string name="pref_ui_title">Agordo</string>
|
<string name="pref_ui_title">Agordo</string>
|
||||||
<string name="pref_ui_no_for_you">\"Por vi\" paĝoj</string>
|
<string name="pref_ui_no_for_you">\"Por vi\" paĝoj</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Vidigi similajn kaj rilatajn apojn sur la paĝo de la apon detaloj</string>
|
|
||||||
<string name="pref_install_mode_shizuku">Shizuku instalilo</string>
|
<string name="pref_install_mode_shizuku">Shizuku instalilo</string>
|
||||||
<string name="pref_install_mode_am">AM instalilo</string>
|
<string name="pref_install_mode_am">AM instalilo</string>
|
||||||
<string name="action_request_analysis">Peti novan analizon</string>
|
<string name="action_request_analysis">Peti novan analizon</string>
|
||||||
|
|||||||
@@ -211,7 +211,6 @@
|
|||||||
<string name="toast_manual_available">Felicidades, el código de la versión solicitada está disponible, descargándolo ahora.</string>
|
<string name="toast_manual_available">Felicidades, el código de la versión solicitada está disponible, descargándolo ahora.</string>
|
||||||
<string name="toast_manual_unavailable">El código de versión que estás solicitando no está disponible.</string>
|
<string name="toast_manual_unavailable">El código de versión que estás solicitando no está disponible.</string>
|
||||||
<string name="title_manual_download">Descarga manual</string>
|
<string name="title_manual_download">Descarga manual</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Mostrar grupos similares y relacionados en la página de detalles de la aplicación</string>
|
|
||||||
<string name="pref_ui_similar_apps">Aplicaciones similares y relacionadas</string>
|
<string name="pref_ui_similar_apps">Aplicaciones similares y relacionadas</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Mostrar las páginas Para ti en la pantalla de inicio</string>
|
<string name="pref_ui_no_for_you_desc">Mostrar las páginas Para ti en la pantalla de inicio</string>
|
||||||
<string name="pref_ui_no_for_you">Páginas para ti</string>
|
<string name="pref_ui_no_for_you">Páginas para ti</string>
|
||||||
|
|||||||
@@ -201,7 +201,6 @@
|
|||||||
<string name="notification_channel_updates">Uuenduste märguanded</string>
|
<string name="notification_channel_updates">Uuenduste märguanded</string>
|
||||||
<string name="permissions_denied">Vajalikud load on keelatud. Toimingu jätkamiseks luba need</string>
|
<string name="permissions_denied">Vajalikud load on keelatud. Toimingu jätkamiseks luba need</string>
|
||||||
<string name="pref_updates_auto">Automaatsed uuendused</string>
|
<string name="pref_updates_auto">Automaatsed uuendused</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Kuva rakenduse üksikasjade lehel sarnaseid ja seotud rakendusi</string>
|
|
||||||
<string name="title_manual_download">Käsitsi allalaadimine</string>
|
<string name="title_manual_download">Käsitsi allalaadimine</string>
|
||||||
<string name="toast_clipboard_copied">Kopeeriti lõikelauale</string>
|
<string name="toast_clipboard_copied">Kopeeriti lõikelauale</string>
|
||||||
<string name="toast_manual_available">Õnnitlused, taotletud versioonikood on saadaval, laadime alla.</string>
|
<string name="toast_manual_available">Õnnitlused, taotletud versioonikood on saadaval, laadime alla.</string>
|
||||||
|
|||||||
@@ -216,7 +216,6 @@
|
|||||||
<string name="toast_manual_unavailable">Eskatzen ari zaren bertsio kodea ez dago erabilgarri.</string>
|
<string name="toast_manual_unavailable">Eskatzen ari zaren bertsio kodea ez dago erabilgarri.</string>
|
||||||
<string name="title_manual_download">Eskuzko deskargatzea</string>
|
<string name="title_manual_download">Eskuzko deskargatzea</string>
|
||||||
<string name="insecure_anonymous_apply">Ziurtatu berriro hasten duzula saioa eta berrabiarazi aplikazioa aldaketak aplikatzeko.</string>
|
<string name="insecure_anonymous_apply">Ziurtatu berriro hasten duzula saioa eta berrabiarazi aplikazioa aldaketak aplikatzeko.</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Erakutsi antzeko taldeak eta erlazionatuak aplikazioaren xehetasunen orrian</string>
|
|
||||||
<string name="pref_ui_similar_apps">Antzeko aplikazioak eta erlazionatuak</string>
|
<string name="pref_ui_similar_apps">Antzeko aplikazioak eta erlazionatuak</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Erakutsi zuretzako orriak hasierako pantailan</string>
|
<string name="pref_ui_no_for_you_desc">Erakutsi zuretzako orriak hasierako pantailan</string>
|
||||||
<string name="pref_ui_no_for_you">Zuretzako orrialdeak</string>
|
<string name="pref_ui_no_for_you">Zuretzako orrialdeak</string>
|
||||||
|
|||||||
@@ -223,7 +223,6 @@
|
|||||||
<string name="onboarding_permission_installer_desc">اجازه نصب برنامهها از فروشگاه آرورا</string>
|
<string name="onboarding_permission_installer_desc">اجازه نصب برنامهها از فروشگاه آرورا</string>
|
||||||
<string name="pref_install_delete_title">حذف APK پس از نصب</string>
|
<string name="pref_install_delete_title">حذف APK پس از نصب</string>
|
||||||
<string name="pref_install_mode_am">نصب کننده AM.</string>
|
<string name="pref_install_mode_am">نصب کننده AM.</string>
|
||||||
<string name="pref_ui_similar_apps_desc">نمایش خوشه های مشابه و مرتبط در صفحه جزئیات برنامه</string>
|
|
||||||
<string name="spoof_apply">اطمینان حاصل کنید که برای اعمال پارامتر جعلی دوباره وارد سیستم شوید</string>
|
<string name="spoof_apply">اطمینان حاصل کنید که برای اعمال پارامتر جعلی دوباره وارد سیستم شوید</string>
|
||||||
<string name="tab_editor_choice">انتخاب سردبیران</string>
|
<string name="tab_editor_choice">انتخاب سردبیران</string>
|
||||||
<string name="title_no_network">بدون شبکه</string>
|
<string name="title_no_network">بدون شبکه</string>
|
||||||
|
|||||||
@@ -187,7 +187,6 @@
|
|||||||
<string name="insecure_anonymous_apply">Varmista, että kirjaudut uudelleen ja käynnistät sovelluksen uudelleen ottaaksesi muutokset käyttöön.</string>
|
<string name="insecure_anonymous_apply">Varmista, että kirjaudut uudelleen ja käynnistät sovelluksen uudelleen ottaaksesi muutokset käyttöön.</string>
|
||||||
<string name="spoof_apply">Varmista, että kirjaudut uudelleen ottaaksesi huijauksen käyttöösi</string>
|
<string name="spoof_apply">Varmista, että kirjaudut uudelleen ottaaksesi huijauksen käyttöösi</string>
|
||||||
<string name="purchase_session_expired">Sessio päättyi, kirjaudu uudelleen uuden session aloittamiseksi.</string>
|
<string name="purchase_session_expired">Sessio päättyi, kirjaudu uudelleen uuden session aloittamiseksi.</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Näytä sovelluksen lisätietosivulla sen kanssa samankaltaisia ja siihen liittyviä sovelluksia</string>
|
|
||||||
<string name="pref_ui_no_for_you_desc">Näytä Sinulle räätälöity-sivut kotinäytöllä</string>
|
<string name="pref_ui_no_for_you_desc">Näytä Sinulle räätälöity-sivut kotinäytöllä</string>
|
||||||
<string name="pref_ui_layout_tab">Valitse oletusvälilehti</string>
|
<string name="pref_ui_layout_tab">Valitse oletusvälilehti</string>
|
||||||
<string name="pref_ui_layout">Ulkoasu</string>
|
<string name="pref_ui_layout">Ulkoasu</string>
|
||||||
|
|||||||
@@ -209,7 +209,6 @@
|
|||||||
<string name="insecure_anonymous_apply">Reconnectez-vous et redémarrez l’appli pour appliquer les changements.</string>
|
<string name="insecure_anonymous_apply">Reconnectez-vous et redémarrez l’appli pour appliquer les changements.</string>
|
||||||
<string name="pref_network_title">Mise en réseau</string>
|
<string name="pref_network_title">Mise en réseau</string>
|
||||||
<string name="pref_ui_no_for_you">Pages « Pour vous »</string>
|
<string name="pref_ui_no_for_you">Pages « Pour vous »</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Afficher les grappes semblables et connexes sur la page des détails de l’appli</string>
|
|
||||||
<string name="pref_ui_similar_apps">Applis semblables et connexes</string>
|
<string name="pref_ui_similar_apps">Applis semblables et connexes</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Afficher les pages « Pour vous » sur l’écran d’accueil</string>
|
<string name="pref_ui_no_for_you_desc">Afficher les pages « Pour vous » sur l’écran d’accueil</string>
|
||||||
<string name="pref_ui_layout_tab">Choisir l’onglet par défaut</string>
|
<string name="pref_ui_layout_tab">Choisir l’onglet par défaut</string>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
<string name="purchase_unsupported">Aplicación non soportada</string>
|
<string name="purchase_unsupported">Aplicación non soportada</string>
|
||||||
<string name="purchase_invalid">Aplicación non mercada</string>
|
<string name="purchase_invalid">Aplicación non mercada</string>
|
||||||
<string name="purchase_failed">Descarga fallida</string>
|
<string name="purchase_failed">Descarga fallida</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Amosar grupos similares e relacionados na páxina de detalles da aplicación</string>
|
|
||||||
<string name="pref_ui_similar_apps">Aplicacións semellantes e relacionadas</string>
|
<string name="pref_ui_similar_apps">Aplicacións semellantes e relacionadas</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Mostrar Suxestións Personalizadas na pantalla de inicio</string>
|
<string name="pref_ui_no_for_you_desc">Mostrar Suxestións Personalizadas na pantalla de inicio</string>
|
||||||
<string name="pref_ui_no_for_you">Suxestións personalizadas</string>
|
<string name="pref_ui_no_for_you">Suxestións personalizadas</string>
|
||||||
|
|||||||
@@ -207,7 +207,6 @@
|
|||||||
<string name="pref_install_mode_session">מתקין מערכת</string>
|
<string name="pref_install_mode_session">מתקין מערכת</string>
|
||||||
<string name="pref_ui_layout_tab">בחר עמוד ברירת מחדל</string>
|
<string name="pref_ui_layout_tab">בחר עמוד ברירת מחדל</string>
|
||||||
<string name="pref_ui_no_for_you_desc">הצגת עמודים בשבילך במסך הבית</string>
|
<string name="pref_ui_no_for_you_desc">הצגת עמודים בשבילך במסך הבית</string>
|
||||||
<string name="pref_ui_similar_apps_desc">יישומים דומים וקשורים במסך פרטי היישום</string>
|
|
||||||
<string name="title_spoof_manager">מנהל הזיוף</string>
|
<string name="title_spoof_manager">מנהל הזיוף</string>
|
||||||
<string name="details_beta_description">אתה תראה תכונות חדשות ובאגים לפני כולם. תן משוב למפתחים כדי לעזור להם להשתפר.</string>
|
<string name="details_beta_description">אתה תראה תכונות חדשות ובאגים לפני כולם. תן משוב למפתחים כדי לעזור להם להשתפר.</string>
|
||||||
<string name="device_miui_extra">אפשרי לבחור מתקין נתיב, אבל לא תוכל להתקין התקנות מאוחדות (מפוצלות - split), אז הבחירה בידך.</string>
|
<string name="device_miui_extra">אפשרי לבחור מתקין נתיב, אבל לא תוכל להתקין התקנות מאוחדות (מפוצלות - split), אז הבחירה בידך.</string>
|
||||||
|
|||||||
@@ -218,7 +218,6 @@
|
|||||||
<string name="pref_install_mode_root">रूट इंस्टॉलर</string>
|
<string name="pref_install_mode_root">रूट इंस्टॉलर</string>
|
||||||
<string name="pref_install_mode_services">ऑरोरा सेवाएं (अस्वीकृत)</string>
|
<string name="pref_install_mode_services">ऑरोरा सेवाएं (अस्वीकृत)</string>
|
||||||
<string name="purchase_session_expired">सत्र समाप्त हो गया, नया सत्र प्राप्त करने के लिए पुनः लॉगिन करें।</string>
|
<string name="purchase_session_expired">सत्र समाप्त हो गया, नया सत्र प्राप्त करने के लिए पुनः लॉगिन करें।</string>
|
||||||
<string name="pref_ui_similar_apps_desc">ऐप विवरण पृष्ठ पर समान और संबंधित समूह प्रदर्शित करें</string>
|
|
||||||
<string name="spoof_apply">सुनिश्चित करें कि आप छलावरण लागू करने के लिए पुनः लॉगिन करें</string>
|
<string name="spoof_apply">सुनिश्चित करें कि आप छलावरण लागू करने के लिए पुनः लॉगिन करें</string>
|
||||||
<string name="purchase_unsupported">ऐप समर्थित नहीं है</string>
|
<string name="purchase_unsupported">ऐप समर्थित नहीं है</string>
|
||||||
<string name="toast_apk_whitelisted">श्वेतसूचीबद्ध</string>
|
<string name="toast_apk_whitelisted">श्वेतसूचीबद्ध</string>
|
||||||
|
|||||||
@@ -211,7 +211,6 @@
|
|||||||
<string name="toast_manual_available">Čestitamo, traženi kod verzije je dostupan, preuzima se.</string>
|
<string name="toast_manual_available">Čestitamo, traženi kod verzije je dostupan, preuzima se.</string>
|
||||||
<string name="toast_manual_unavailable">Zatraženi kod verzije nije dostupan.</string>
|
<string name="toast_manual_unavailable">Zatraženi kod verzije nije dostupan.</string>
|
||||||
<string name="title_manual_download">Ručno preuzimanje</string>
|
<string name="title_manual_download">Ručno preuzimanje</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Prikaži slične i srodne skupove podataka na stranici detalja aplikacije</string>
|
|
||||||
<string name="pref_ui_similar_apps">Slične i srodne aplikacije</string>
|
<string name="pref_ui_similar_apps">Slične i srodne aplikacije</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Prikaži „Za tvoje stranice” na početnom ekranu</string>
|
<string name="pref_ui_no_for_you_desc">Prikaži „Za tvoje stranice” na početnom ekranu</string>
|
||||||
<string name="pref_ui_no_for_you">Za tvoje stranice</string>
|
<string name="pref_ui_no_for_you">Za tvoje stranice</string>
|
||||||
|
|||||||
@@ -208,7 +208,6 @@
|
|||||||
<string name="action_installations">Telepítések</string>
|
<string name="action_installations">Telepítések</string>
|
||||||
<string name="action_disable">Tiltás</string>
|
<string name="action_disable">Tiltás</string>
|
||||||
<string name="action_clear">Törlés</string>
|
<string name="action_clear">Törlés</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Hasonló és releváns csoportok megjelenítése a részletek lapon</string>
|
|
||||||
<string name="pref_ui_similar_apps">Hasonló és releváns alkalmazások</string>
|
<string name="pref_ui_similar_apps">Hasonló és releváns alkalmazások</string>
|
||||||
<string name="pref_ui_no_for_you_desc">„Önnek ajánlott” oldal megjelenítése a kezdőlapon</string>
|
<string name="pref_ui_no_for_you_desc">„Önnek ajánlott” oldal megjelenítése a kezdőlapon</string>
|
||||||
<string name="pref_ui_no_for_you">Önnek ajánlott oldalak</string>
|
<string name="pref_ui_no_for_you">Önnek ajánlott oldalak</string>
|
||||||
|
|||||||
@@ -198,7 +198,6 @@
|
|||||||
<string name="purchase_not_found">Aplikasi tidak ditemukan</string>
|
<string name="purchase_not_found">Aplikasi tidak ditemukan</string>
|
||||||
<string name="purchase_unsupported">Aplikasi tidak didukung</string>
|
<string name="purchase_unsupported">Aplikasi tidak didukung</string>
|
||||||
<string name="purchase_invalid">Aplikasi tidak dibeli</string>
|
<string name="purchase_invalid">Aplikasi tidak dibeli</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Tampilkan kelompok yang serupa dan terkait pada halaman detail aplikasi</string>
|
|
||||||
<string name="pref_ui_similar_apps">Aplikasi serupa dan terkait</string>
|
<string name="pref_ui_similar_apps">Aplikasi serupa dan terkait</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Tampilkan halaman Untuk Anda di layar beranda</string>
|
<string name="pref_ui_no_for_you_desc">Tampilkan halaman Untuk Anda di layar beranda</string>
|
||||||
<string name="pref_ui_no_for_you">Halaman Untuk Anda</string>
|
<string name="pref_ui_no_for_you">Halaman Untuk Anda</string>
|
||||||
|
|||||||
@@ -208,7 +208,6 @@
|
|||||||
<string name="toast_purchase_blocked">Gli acquisti di app non sono disponibili sui profili anonimi.</string>
|
<string name="toast_purchase_blocked">Gli acquisti di app non sono disponibili sui profili anonimi.</string>
|
||||||
<string name="insecure_anonymous_apply">Assicurati di effettuare nuovamente l\'accesso e di riavviare l\'app per applicare le modifiche.</string>
|
<string name="insecure_anonymous_apply">Assicurati di effettuare nuovamente l\'accesso e di riavviare l\'app per applicare le modifiche.</string>
|
||||||
<string name="pref_network_title">Rete</string>
|
<string name="pref_network_title">Rete</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Visualizza i gruppi di app simili e correlate nella pagina dei dettagli dell\'app</string>
|
|
||||||
<string name="pref_ui_similar_apps">App simili e correlate</string>
|
<string name="pref_ui_similar_apps">App simili e correlate</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Visualizza le pagine «Per te» sulla schermata iniziale</string>
|
<string name="pref_ui_no_for_you_desc">Visualizza le pagine «Per te» sulla schermata iniziale</string>
|
||||||
<string name="pref_ui_no_for_you">Pagine «Per te»</string>
|
<string name="pref_ui_no_for_you">Pagine «Per te»</string>
|
||||||
|
|||||||
@@ -212,7 +212,6 @@
|
|||||||
<string name="toast_manual_unavailable">要求したバージョン番号は利用できません。</string>
|
<string name="toast_manual_unavailable">要求したバージョン番号は利用できません。</string>
|
||||||
<string name="toast_manual_available">おめでとう!要求したバージョンコードが利用できます。ダウンロード中。</string>
|
<string name="toast_manual_available">おめでとう!要求したバージョンコードが利用できます。ダウンロード中。</string>
|
||||||
<string name="title_manual_download">手動ダウンロード</string>
|
<string name="title_manual_download">手動ダウンロード</string>
|
||||||
<string name="pref_ui_similar_apps_desc">類似のアプリをアプリ詳細ページに表示する</string>
|
|
||||||
<string name="pref_ui_similar_apps">類似のアプリ</string>
|
<string name="pref_ui_similar_apps">類似のアプリ</string>
|
||||||
<string name="pref_ui_no_for_you_desc">ホーム画面におすすめページを表示する</string>
|
<string name="pref_ui_no_for_you_desc">ホーム画面におすすめページを表示する</string>
|
||||||
<string name="pref_ui_no_for_you">おすすめページ</string>
|
<string name="pref_ui_no_for_you">おすすめページ</string>
|
||||||
|
|||||||
@@ -216,7 +216,6 @@
|
|||||||
<string name="toast_manual_unavailable">Koda guhertoya ku tu dixwazî tune ye.</string>
|
<string name="toast_manual_unavailable">Koda guhertoya ku tu dixwazî tune ye.</string>
|
||||||
<string name="title_manual_download">Bi destan daxe</string>
|
<string name="title_manual_download">Bi destan daxe</string>
|
||||||
<string name="insecure_anonymous_apply">Ji bo sepandina guhertinan zanibe be ku tu ji nû ve têketiye û sepanê ji nû ve daye destpêkirin.</string>
|
<string name="insecure_anonymous_apply">Ji bo sepandina guhertinan zanibe be ku tu ji nû ve têketiye û sepanê ji nû ve daye destpêkirin.</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Komên wekhev û têkildar li ser rûpela hûrgiliyên sepanê nîşan bide</string>
|
|
||||||
<string name="pref_ui_similar_apps">Sepanên wekhev û têkildar</string>
|
<string name="pref_ui_similar_apps">Sepanên wekhev û têkildar</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Rûpelên Bo Te li ser dîmendera malê rê bide</string>
|
<string name="pref_ui_no_for_you_desc">Rûpelên Bo Te li ser dîmendera malê rê bide</string>
|
||||||
<string name="pref_ui_no_for_you">Rûpelên Bo Te</string>
|
<string name="pref_ui_no_for_you">Rûpelên Bo Te</string>
|
||||||
|
|||||||
@@ -207,7 +207,6 @@
|
|||||||
<string name="title_apps">앱스</string>
|
<string name="title_apps">앱스</string>
|
||||||
<string name="title_app_settings">앱 설정</string>
|
<string name="title_app_settings">앱 설정</string>
|
||||||
<string name="purchase_failed">다운로드 실패됨</string>
|
<string name="purchase_failed">다운로드 실패됨</string>
|
||||||
<string name="pref_ui_similar_apps_desc">앱 세부 페이지에서 유사한 및 관련 클러스터 표시</string>
|
|
||||||
<string name="action_request_analysis">새 분석 요청</string>
|
<string name="action_request_analysis">새 분석 요청</string>
|
||||||
<string name="action_home_screen">홈 화면에 추가</string>
|
<string name="action_home_screen">홈 화면에 추가</string>
|
||||||
<string name="download_clear_finished">완료건 지우기</string>
|
<string name="download_clear_finished">완료건 지우기</string>
|
||||||
|
|||||||
@@ -151,7 +151,6 @@
|
|||||||
<string name="download_force_clear_all">Priverstinai išvalyti visus</string>
|
<string name="download_force_clear_all">Priverstinai išvalyti visus</string>
|
||||||
<string name="download_pause_all">Pristabdyti visus</string>
|
<string name="download_pause_all">Pristabdyti visus</string>
|
||||||
<string name="pref_ui_no_for_you">„Jums“ puslapiai</string>
|
<string name="pref_ui_no_for_you">„Jums“ puslapiai</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Rodyti programėlės puslapyje panašių ir susijusių programėlių rinkinį</string>
|
|
||||||
<string name="tab_editor_choice">Redaktoriaus pasirinkimas</string>
|
<string name="tab_editor_choice">Redaktoriaus pasirinkimas</string>
|
||||||
<string name="tab_for_you">Jums</string>
|
<string name="tab_for_you">Jums</string>
|
||||||
<string name="title_apps_library">Programėlės bibliotekoje</string>
|
<string name="title_apps_library">Programėlės bibliotekoje</string>
|
||||||
|
|||||||
@@ -111,7 +111,6 @@
|
|||||||
<string name="pref_ui_no_for_you">Lapas \"Tev\"</string>
|
<string name="pref_ui_no_for_you">Lapas \"Tev\"</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Rādīt lapas \"Tev\" sākuma ekrānā</string>
|
<string name="pref_ui_no_for_you_desc">Rādīt lapas \"Tev\" sākuma ekrānā</string>
|
||||||
<string name="pref_ui_similar_apps">Līdzīgas un saistītas lietotnes</string>
|
<string name="pref_ui_similar_apps">Līdzīgas un saistītas lietotnes</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Rādīt līdzīgas un saistītas lietotnes</string>
|
|
||||||
<string name="purchase_failed">Lejupielāde neizdevās</string>
|
<string name="purchase_failed">Lejupielāde neizdevās</string>
|
||||||
<string name="tab_top_paid">Top maksas</string>
|
<string name="tab_top_paid">Top maksas</string>
|
||||||
<string name="tab_trending">Populāri</string>
|
<string name="tab_trending">Populāri</string>
|
||||||
|
|||||||
@@ -187,7 +187,6 @@
|
|||||||
<string name="toast_developer_setting_failed">Skru på utviklingsinnstillingene fra enhetsinnstillingene for å åpne dem.</string>
|
<string name="toast_developer_setting_failed">Skru på utviklingsinnstillingene fra enhetsinnstillingene for å åpne dem.</string>
|
||||||
<string name="toast_page_unavailable">Utforskningssiden er utilgjengelig</string>
|
<string name="toast_page_unavailable">Utforskningssiden er utilgjengelig</string>
|
||||||
<string name="toast_spoof_applied">Enhetslureri i bruk.</string>
|
<string name="toast_spoof_applied">Enhetslureri i bruk.</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Vis lignende og relaterte klynger på appdetaljsiden</string>
|
|
||||||
<string name="pref_ui_no_for_you_desc">Vis For You-sider på startskjermen</string>
|
<string name="pref_ui_no_for_you_desc">Vis For You-sider på startskjermen</string>
|
||||||
<string name="installer_service_misconfigured">Sett opp Aurora-tjenester og innvilg alle tilganger først.</string>
|
<string name="installer_service_misconfigured">Sett opp Aurora-tjenester og innvilg alle tilganger først.</string>
|
||||||
<string name="title_spoof_manager">Lureri-håndterer</string>
|
<string name="title_spoof_manager">Lureri-håndterer</string>
|
||||||
|
|||||||
@@ -70,7 +70,6 @@
|
|||||||
<string name="purchase_unsupported">App niet ondersteund</string>
|
<string name="purchase_unsupported">App niet ondersteund</string>
|
||||||
<string name="purchase_invalid">App niet aangeschaft</string>
|
<string name="purchase_invalid">App niet aangeschaft</string>
|
||||||
<string name="purchase_failed">Installatie mislukt</string>
|
<string name="purchase_failed">Installatie mislukt</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Toon vergelijkbare en gerelateerde clusters op de app-detailspagina</string>
|
|
||||||
<string name="pref_ui_similar_apps">Vergelijkbare en gerelateerde apps</string>
|
<string name="pref_ui_similar_apps">Vergelijkbare en gerelateerde apps</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Pagina met aanbevelingen op thuisscherm tonen</string>
|
<string name="pref_ui_no_for_you_desc">Pagina met aanbevelingen op thuisscherm tonen</string>
|
||||||
<string name="pref_ui_no_for_you">Voor jou pagina\'s</string>
|
<string name="pref_ui_no_for_you">Voor jou pagina\'s</string>
|
||||||
|
|||||||
@@ -55,7 +55,6 @@
|
|||||||
<string name="purchase_unsupported">ਐਪ ਸਪੋਰਟਡ ਨਹੀਂ</string>
|
<string name="purchase_unsupported">ਐਪ ਸਪੋਰਟਡ ਨਹੀਂ</string>
|
||||||
<string name="purchase_invalid">ਇਹ ਐਪ ਅਜੇ ਤੁਹਾਡੇ ਵਲੋਂ ਖਰੀਦਿਆ ਨਹੀਂ ਗਿਆ</string>
|
<string name="purchase_invalid">ਇਹ ਐਪ ਅਜੇ ਤੁਹਾਡੇ ਵਲੋਂ ਖਰੀਦਿਆ ਨਹੀਂ ਗਿਆ</string>
|
||||||
<string name="purchase_failed">ਡਾਊਨਲੋਡ ਫੇਲ੍ਹ</string>
|
<string name="purchase_failed">ਡਾਊਨਲੋਡ ਫੇਲ੍ਹ</string>
|
||||||
<string name="pref_ui_similar_apps_desc">ਐਪ ਜਾਣਕਾਰੀ ਵਾਲੇ ਪੇਜ ਤੇ ਇਸੇ ਸ਼੍ਰੇਣੀ ਦੇ ਕੁੱਝ ਹੋਰ ਐਪਸ ਵਾਲਾ ਖ਼ਾਨਾ ਵਿਖਾਓ</string>
|
|
||||||
<string name="pref_ui_similar_apps">ਇਸੇ ਸ਼੍ਰੇਣੀ ਦੇ ਕੁੱਝ ਹੋਰ ਐਪਸ</string>
|
<string name="pref_ui_similar_apps">ਇਸੇ ਸ਼੍ਰੇਣੀ ਦੇ ਕੁੱਝ ਹੋਰ ਐਪਸ</string>
|
||||||
<string name="pref_ui_no_for_you_desc">ਖਾਸ ਤੁਹਾਡੇ ਲਈ ਚੁਣੇ ਹੋਏ ਐਪਸ ਵਾਲਾ ਪੰਨਾ ਵਿਖਾਓ</string>
|
<string name="pref_ui_no_for_you_desc">ਖਾਸ ਤੁਹਾਡੇ ਲਈ ਚੁਣੇ ਹੋਏ ਐਪਸ ਵਾਲਾ ਪੰਨਾ ਵਿਖਾਓ</string>
|
||||||
<string name="pref_ui_no_for_you">ਤੁਹਾਡੇ ਲਈ ਚੁਣੇ ਹੋਏ ਐਪਸ</string>
|
<string name="pref_ui_no_for_you">ਤੁਹਾਡੇ ਲਈ ਚੁਣੇ ਹੋਏ ਐਪਸ</string>
|
||||||
|
|||||||
@@ -211,7 +211,6 @@
|
|||||||
<string name="pref_common_extra">Dodatkowe</string>
|
<string name="pref_common_extra">Dodatkowe</string>
|
||||||
<string name="pref_ui_no_for_you">Strony dla Ciebie</string>
|
<string name="pref_ui_no_for_you">Strony dla Ciebie</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Wyświetl strony Dla Ciebie na ekranie głównym</string>
|
<string name="pref_ui_no_for_you_desc">Wyświetl strony Dla Ciebie na ekranie głównym</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Wyświetl grupy podobnych i powiązanych na stronie szczegółów aplikacji</string>
|
|
||||||
<string name="pref_ui_similar_apps">Aplikacje podobne i powiązane</string>
|
<string name="pref_ui_similar_apps">Aplikacje podobne i powiązane</string>
|
||||||
<string name="pref_ui_layout_tab">Wybierz domyślną kartę</string>
|
<string name="pref_ui_layout_tab">Wybierz domyślną kartę</string>
|
||||||
<string name="pref_ui_layout">Układ</string>
|
<string name="pref_ui_layout">Układ</string>
|
||||||
|
|||||||
@@ -211,7 +211,6 @@
|
|||||||
<string name="toast_manual_available">O código da versão solicitada está disponível, baixando agora.</string>
|
<string name="toast_manual_available">O código da versão solicitada está disponível, baixando agora.</string>
|
||||||
<string name="toast_manual_unavailable">O código de versão que você solicitou não está disponível.</string>
|
<string name="toast_manual_unavailable">O código de versão que você solicitou não está disponível.</string>
|
||||||
<string name="title_manual_download">Baixar manualmente</string>
|
<string name="title_manual_download">Baixar manualmente</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Exibir apps semelhantes e relacionados na página de detalhes do app</string>
|
|
||||||
<string name="pref_ui_no_for_you_desc">Exibir páginas de recomendação na tela inicial</string>
|
<string name="pref_ui_no_for_you_desc">Exibir páginas de recomendação na tela inicial</string>
|
||||||
<string name="pref_network_title">Rede</string>
|
<string name="pref_network_title">Rede</string>
|
||||||
<string name="details_no_app_match">Não há apps correspondentes</string>
|
<string name="details_no_app_match">Não há apps correspondentes</string>
|
||||||
|
|||||||
@@ -83,7 +83,6 @@
|
|||||||
<string name="purchase_unsupported">Aplicação não suportada</string>
|
<string name="purchase_unsupported">Aplicação não suportada</string>
|
||||||
<string name="purchase_invalid">Aplicação não comprada</string>
|
<string name="purchase_invalid">Aplicação não comprada</string>
|
||||||
<string name="purchase_failed">Descarga falhou</string>
|
<string name="purchase_failed">Descarga falhou</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Mostrar grupos de aplicações semelhantes e relacionadas na página de detalhes das aplicações</string>
|
|
||||||
<string name="pref_ui_similar_apps">Aplicações semelhantes e relacionadas</string>
|
<string name="pref_ui_similar_apps">Aplicações semelhantes e relacionadas</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Exibir páginas de recomendação no ecrã inicial</string>
|
<string name="pref_ui_no_for_you_desc">Exibir páginas de recomendação no ecrã inicial</string>
|
||||||
<string name="pref_ui_no_for_you">Páginas de recomendação</string>
|
<string name="pref_ui_no_for_you">Páginas de recomendação</string>
|
||||||
|
|||||||
@@ -207,7 +207,6 @@
|
|||||||
<string name="toast_clipboard_copied">Copiat în clipboard</string>
|
<string name="toast_clipboard_copied">Copiat în clipboard</string>
|
||||||
<string name="pref_install_mode_session">Instalator sesiune</string>
|
<string name="pref_install_mode_session">Instalator sesiune</string>
|
||||||
<string name="pref_install_mode_title">Metoda de instalare</string>
|
<string name="pref_install_mode_title">Metoda de instalare</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Afișare grupuri similare și asociate pe pagina de detalii a aplicației</string>
|
|
||||||
<string name="pref_network_title">Rețea</string>
|
<string name="pref_network_title">Rețea</string>
|
||||||
<string name="session_good">Vai! Toate bune.</string>
|
<string name="session_good">Vai! Toate bune.</string>
|
||||||
<string name="title_purchase_history">Istoricul achizițiilor</string>
|
<string name="title_purchase_history">Istoricul achizițiilor</string>
|
||||||
|
|||||||
@@ -208,7 +208,6 @@
|
|||||||
<string name="toast_purchase_blocked">Покупка приложений недоступна когда анонимно.</string>
|
<string name="toast_purchase_blocked">Покупка приложений недоступна когда анонимно.</string>
|
||||||
<string name="insecure_anonymous_apply">Убедитесь, что выполнили новый вход и перезапустили приложение для применения изменений.</string>
|
<string name="insecure_anonymous_apply">Убедитесь, что выполнили новый вход и перезапустили приложение для применения изменений.</string>
|
||||||
<string name="pref_network_title">Сеть</string>
|
<string name="pref_network_title">Сеть</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Показать секции похожих и связанных на странице приложения</string>
|
|
||||||
<string name="pref_ui_no_for_you_desc">Показать страницу рекомендаций на главном экране</string>
|
<string name="pref_ui_no_for_you_desc">Показать страницу рекомендаций на главном экране</string>
|
||||||
<string name="pref_ui_layout_tab">Вкладка по умолчанию</string>
|
<string name="pref_ui_layout_tab">Вкладка по умолчанию</string>
|
||||||
<string name="pref_ui_layout">Вкладки</string>
|
<string name="pref_ui_layout">Вкладки</string>
|
||||||
|
|||||||
@@ -215,7 +215,6 @@
|
|||||||
<string name="about_bhim">BHIM - UPI</string>
|
<string name="about_bhim">BHIM - UPI</string>
|
||||||
<string name="about_bhim_summary">Dona pro mèdiu de UPI</string>
|
<string name="about_bhim_summary">Dona pro mèdiu de UPI</string>
|
||||||
<string name="download_metadata">Retzende meta-datos</string>
|
<string name="download_metadata">Retzende meta-datos</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Ammustra sos grupos de aplicatziones simigiantes e ligados in sa pàgina de sos detàllios de s\'aplicatzione</string>
|
|
||||||
<string name="about_fdroid_summary">Otene Aurora Store pro mèdiu de F-Droid.</string>
|
<string name="about_fdroid_summary">Otene Aurora Store pro mèdiu de F-Droid.</string>
|
||||||
<string name="pref_install_mode_services">Servìtzios de Aurora</string>
|
<string name="pref_install_mode_services">Servìtzios de Aurora</string>
|
||||||
<string name="title_installation">Installatzione</string>
|
<string name="title_installation">Installatzione</string>
|
||||||
|
|||||||
@@ -234,7 +234,6 @@
|
|||||||
<string name="download_eta_min">වි. %1$d ත. %2$d ක් ඉතිරිය</string>
|
<string name="download_eta_min">වි. %1$d ත. %2$d ක් ඉතිරිය</string>
|
||||||
<string name="download_eta_sec">ත. %1$d ක් ඉතිරිය</string>
|
<string name="download_eta_sec">ත. %1$d ක් ඉතිරිය</string>
|
||||||
<string name="onboarding_permission_notifications">දැනුම්දීම්</string>
|
<string name="onboarding_permission_notifications">දැනුම්දීම්</string>
|
||||||
<string name="pref_ui_similar_apps_desc">යෙදුමේ විස්තර පිටුවේ සමාන හා ආශ්රිත පර්ෂද පෙන්වන්න</string>
|
|
||||||
<string name="session_good">ඔක්කොම හරි!</string>
|
<string name="session_good">ඔක්කොම හරි!</string>
|
||||||
<string name="session_enjoy">ඇතුළු වී අත්විඳින්න.</string>
|
<string name="session_enjoy">ඇතුළු වී අත්විඳින්න.</string>
|
||||||
<string name="session_init">සූදානම් කෙරෙමින්…</string>
|
<string name="session_init">සූදානම් කෙරෙමින්…</string>
|
||||||
|
|||||||
@@ -140,7 +140,6 @@
|
|||||||
<string name="pref_install_mode_shizuku">Shizuku inštalátor</string>
|
<string name="pref_install_mode_shizuku">Shizuku inštalátor</string>
|
||||||
<string name="pref_install_mode_session">Inštalátor relácie</string>
|
<string name="pref_install_mode_session">Inštalátor relácie</string>
|
||||||
<string name="pref_vending_version_device">Predvolené (z konfigurácie zariadenia)</string>
|
<string name="pref_vending_version_device">Predvolené (z konfigurácie zariadenia)</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Zobraziť podobné a súvisiace skupiny na stránke podrobností aplikácie</string>
|
|
||||||
<string name="pref_updates_incompatible">Nekompatibilné aktualizácie</string>
|
<string name="pref_updates_incompatible">Nekompatibilné aktualizácie</string>
|
||||||
<string name="pref_updates_incompatible_desc">Zobraziť aktualizácie pre nekompatibilné alebo zakázané aplikácie, ktoré môžu zlyhať pri nštalovaní</string>
|
<string name="pref_updates_incompatible_desc">Zobraziť aktualizácie pre nekompatibilné alebo zakázané aplikácie, ktoré môžu zlyhať pri nštalovaní</string>
|
||||||
<string name="pref_aurora_only">Iba aplikácie Aurora obchod</string>
|
<string name="pref_aurora_only">Iba aplikácie Aurora obchod</string>
|
||||||
|
|||||||
@@ -272,7 +272,6 @@
|
|||||||
<string name="pref_filter_fdroid_title">Filtrirajte F-Droid aplikacije</string>
|
<string name="pref_filter_fdroid_title">Filtrirajte F-Droid aplikacije</string>
|
||||||
<string name="pref_ui_title">Prilagajanje</string>
|
<string name="pref_ui_title">Prilagajanje</string>
|
||||||
<string name="pref_ui_no_for_you">Strani za vas</string>
|
<string name="pref_ui_no_for_you">Strani za vas</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Prikaži podobne in sorodne gruče na strani s podrobnostmi o aplikaciji</string>
|
|
||||||
<string name="menu">Meni</string>
|
<string name="menu">Meni</string>
|
||||||
<string name="about_wiki_title">Wiki</string>
|
<string name="about_wiki_title">Wiki</string>
|
||||||
<string name="about_wiki_summary">Poiščite odgovore na pogosto zastavljena vprašanja (F.A.Q.), korake za odpravljanje težav in več</string>
|
<string name="about_wiki_summary">Poiščite odgovore na pogosto zastavljena vprašanja (F.A.Q.), korake za odpravljanje težav in več</string>
|
||||||
|
|||||||
@@ -82,7 +82,6 @@
|
|||||||
<string name="purchase_unsupported">Appka lama taageerin</string>
|
<string name="purchase_unsupported">Appka lama taageerin</string>
|
||||||
<string name="purchase_invalid">Appka lama iibsanin</string>
|
<string name="purchase_invalid">Appka lama iibsanin</string>
|
||||||
<string name="purchase_failed">Dajintii maguulaysan</string>
|
<string name="purchase_failed">Dajintii maguulaysan</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Soodhig appska lamidka ah iyo kuwa laxidhiidha bogga faahfaahinta appka</string>
|
|
||||||
<string name="pref_ui_similar_apps">Lamid ah & laxidhiidha</string>
|
<string name="pref_ui_similar_apps">Lamid ah & laxidhiidha</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Boggaga Adiga shaashada hore soodhig</string>
|
<string name="pref_ui_no_for_you_desc">Boggaga Adiga shaashada hore soodhig</string>
|
||||||
<string name="pref_ui_no_for_you">Boggaga Adiga</string>
|
<string name="pref_ui_no_for_you">Boggaga Adiga</string>
|
||||||
|
|||||||
@@ -173,7 +173,6 @@
|
|||||||
<string name="pref_ui_no_for_you">Faqe “Për ju”</string>
|
<string name="pref_ui_no_for_you">Faqe “Për ju”</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Shfaq te skena e kreut faqe “Për ju”</string>
|
<string name="pref_ui_no_for_you_desc">Shfaq te skena e kreut faqe “Për ju”</string>
|
||||||
<string name="pref_ui_similar_apps">Aplikacione të ngjashme dhe të afërta</string>
|
<string name="pref_ui_similar_apps">Aplikacione të ngjashme dhe të afërta</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Shfaq te faqja e hollësive të aplikacionit grumbuj të ngjashëm dhe të afërt</string>
|
|
||||||
<string name="purchase_failed">Shkarkimi dështoi</string>
|
<string name="purchase_failed">Shkarkimi dështoi</string>
|
||||||
<string name="purchase_invalid">Aplikacion jo i blerë</string>
|
<string name="purchase_invalid">Aplikacion jo i blerë</string>
|
||||||
<string name="purchase_unsupported">Aplikacioni nuk mbulohet</string>
|
<string name="purchase_unsupported">Aplikacioni nuk mbulohet</string>
|
||||||
|
|||||||
@@ -215,7 +215,6 @@
|
|||||||
<string name="toast_manual_unavailable">Кôд верзије који тражите није доступан.</string>
|
<string name="toast_manual_unavailable">Кôд верзије који тражите није доступан.</string>
|
||||||
<string name="title_manual_download">Ручно преузимање</string>
|
<string name="title_manual_download">Ручно преузимање</string>
|
||||||
<string name="insecure_anonymous_apply">Обавезно се поново пријавите и поново покрените апликацију да бисте применили промене.</string>
|
<string name="insecure_anonymous_apply">Обавезно се поново пријавите и поново покрените апликацију да бисте применили промене.</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Прикажите сличне и сродне скупове на страници с детаљима о апликацији</string>
|
|
||||||
<string name="pref_ui_similar_apps">Сличне и сродне апликације</string>
|
<string name="pref_ui_similar_apps">Сличне и сродне апликације</string>
|
||||||
<string name="pref_ui_no_for_you_desc">Прикажите странице за вас на почетном екрану</string>
|
<string name="pref_ui_no_for_you_desc">Прикажите странице за вас на почетном екрану</string>
|
||||||
<string name="pref_ui_no_for_you">Странице за вас</string>
|
<string name="pref_ui_no_for_you">Странице за вас</string>
|
||||||
|
|||||||
@@ -202,7 +202,6 @@
|
|||||||
<string name="toast_export_failed">Kunde inte exportera enhetens konfiguration</string>
|
<string name="toast_export_failed">Kunde inte exportera enhetens konfiguration</string>
|
||||||
<string name="toast_export_success">Enhetens konfiguration exporterades</string>
|
<string name="toast_export_success">Enhetens konfiguration exporterades</string>
|
||||||
<string name="toast_apk_blacklisted">Svartlistad</string>
|
<string name="toast_apk_blacklisted">Svartlistad</string>
|
||||||
<string name="pref_ui_similar_apps_desc">Visa liknande och relaterade klustrar på appens detaljsida</string>
|
|
||||||
<string name="onboarding_permission_esm">Hanterare av externt lagringsutrymme</string>
|
<string name="onboarding_permission_esm">Hanterare av externt lagringsutrymme</string>
|
||||||
<string name="details_no_permission">Inga behörigheter</string>
|
<string name="details_no_permission">Inga behörigheter</string>
|
||||||
<string name="action_pending">Avvaktande</string>
|
<string name="action_pending">Avvaktande</string>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user