diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt
index aa1d148cf..fae82eb6b 100644
--- a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt
+++ b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt
@@ -7,6 +7,7 @@
package com.aurora.store.compose.ui.details
import android.content.ActivityNotFoundException
+import android.content.Context
import android.content.Intent
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
@@ -135,6 +136,8 @@ fun AppDetailsScreen(
app = loadedApp,
error = err.error,
extra = err.extra,
+ isAnonymous = viewModel.authProvider.isAnonymous,
+ onBuy = { openPlayStore(context, loadedApp.packageName) },
onDismiss = viewModel::dismissInstallError
)
}
@@ -198,6 +201,21 @@ fun AppDetailsScreen(
}
}
+/**
+ * Opens the given app's Play Store listing, preferring the Play Store app when available and
+ * falling back to whichever activity can handle the web listing (e.g. a browser).
+ */
+private fun openPlayStore(context: Context, packageName: String) {
+ val uri = "${Constants.SHARE_URL}$packageName".toUri()
+ val intent = Intent(Intent.ACTION_VIEW).apply { data = uri }
+
+ if (intent.resolveActivity(context.packageManager) != null) {
+ context.startActivity(intent.apply { setPackage(Constants.PACKAGE_NAME_PLAY_STORE) })
+ } else {
+ context.startActivity(intent)
+ }
+}
+
private fun stateKey(state: AppState, app: App?): String = when {
state is AppState.Error -> "error"
state is AppState.Loading || app == null -> "loading"
@@ -261,6 +279,11 @@ private fun ScreenContentApp(
onForceRestart: () -> Unit = {}
) {
val context = LocalContext.current
+
+ // Anonymous accounts can't purchase, so a paid app can neither be installed nor manually
+ // downloaded (any version) by them. Free apps are always acquirable.
+ val canAcquire = app.isFree || !isAnonymous
+
var scaffoldDirective = calculatePaneScaffoldDirective(windowAdaptiveInfo)
if (forceSinglePane) {
@@ -317,7 +340,11 @@ private fun ScreenContentApp(
@Composable
fun SetupMenu() {
- AppDetailsMenu(isFavorite = isFavorite, state = state) { menuItem ->
+ AppDetailsMenu(
+ isFavorite = isFavorite,
+ state = state,
+ canManualDownload = canAcquire
+ ) { menuItem ->
when (menuItem) {
MenuItem.FAVORITE -> onFavorite()
@@ -333,20 +360,7 @@ private fun ScreenContentApp(
ShortcutManagerUtil.requestPinShortcut(context, app.packageName)
}
- MenuItem.PLAY_STORE -> {
- val uri = "${Constants.SHARE_URL}${app.packageName}".toUri()
- val intent = Intent(Intent.ACTION_VIEW).apply { data = uri }
-
- if (intent.resolveActivity(context.packageManager) != null) {
- context.startActivity(
- intent.apply {
- setPackage(Constants.PACKAGE_NAME_PLAY_STORE)
- }
- )
- } else {
- context.startActivity(intent)
- }
- }
+ MenuItem.PLAY_STORE -> openPlayStore(context, app.packageName)
}
}
}
@@ -413,6 +427,8 @@ private fun ScreenContentApp(
Actions(
primaryActionDisplayName = primaryActionName,
secondaryActionDisplayName = stringResource(R.string.title_manual_download),
+ isPrimaryActionEnabled = canAcquire,
+ isSecondaryActionEnabled = canAcquire,
onPrimaryAction = ::onInstall,
onSecondaryAction = { showExtraPane(ExtraScreen.ManualDownload) }
)
diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/menu/AppDetailsMenu.kt b/app/src/main/java/com/aurora/store/compose/ui/details/menu/AppDetailsMenu.kt
index 07f909a3d..afd70198a 100644
--- a/app/src/main/java/com/aurora/store/compose/ui/details/menu/AppDetailsMenu.kt
+++ b/app/src/main/java/com/aurora/store/compose/ui/details/menu/AppDetailsMenu.kt
@@ -42,6 +42,7 @@ fun AppDetailsMenu(
state: AppState = AppState.Unavailable,
isFavorite: Boolean = false,
isExpanded: Boolean = false,
+ canManualDownload: Boolean = true,
onMenuItemClicked: (menuItem: MenuItem) -> Unit = {}
) {
val context = LocalContext.current
@@ -80,7 +81,7 @@ fun AppDetailsMenu(
DropdownMenuItem(
text = { Text(text = stringResource(R.string.title_manual_download)) },
onClick = { onClick(MenuItem.MANUAL_DOWNLOAD) },
- enabled = !state.inProgress()
+ enabled = canManualDownload && !state.inProgress()
)
DropdownMenuItem(
text = { Text(text = stringResource(R.string.action_info)) },
diff --git a/app/src/main/java/com/aurora/store/compose/ui/sheets/InstallErrorSheet.kt b/app/src/main/java/com/aurora/store/compose/ui/sheets/InstallErrorSheet.kt
index 34a4dd87f..c93ef203c 100644
--- a/app/src/main/java/com/aurora/store/compose/ui/sheets/InstallErrorSheet.kt
+++ b/app/src/main/java/com/aurora/store/compose/ui/sheets/InstallErrorSheet.kt
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
@@ -41,9 +42,20 @@ import com.aurora.store.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun InstallErrorSheet(app: App, error: String?, extra: String?, onDismiss: () -> Unit) {
+fun InstallErrorSheet(
+ app: App,
+ error: String?,
+ extra: String?,
+ isAnonymous: Boolean = true,
+ onBuy: () -> Unit = {},
+ onDismiss: () -> Unit
+) {
val context = LocalContext.current
+ // A paid app that failed to install for a signed-in (non-anonymous) account is almost
+ // always one that hasn't been purchased yet; offer a shortcut to buy it on the Play Store.
+ val canBuy = !app.isFree && !isAnonymous
+
ModalBottomSheet(
onDismissRequest = onDismiss,
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
@@ -53,7 +65,7 @@ fun InstallErrorSheet(app: App, error: String?, extra: String?, onDismiss: () ->
.fillMaxWidth()
.verticalScroll(rememberScrollState())
) {
- Header(app = app)
+ Header(app = app, showBuy = canBuy, onBuy = onBuy)
HorizontalDivider(
modifier = Modifier.padding(
@@ -101,7 +113,7 @@ fun InstallErrorSheet(app: App, error: String?, extra: String?, onDismiss: () ->
}
@Composable
-private fun Header(app: App) {
+private fun Header(app: App, showBuy: Boolean = false, onBuy: () -> Unit = {}) {
Row(
modifier = Modifier
.fillMaxWidth()
@@ -137,5 +149,14 @@ private fun Header(app: App) {
overflow = TextOverflow.Ellipsis
)
}
+ if (showBuy) {
+ Button(onClick = onBuy) {
+ Text(
+ text = stringResource(R.string.action_buy, app.price),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+ }
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 87f9d60cb..a3ffdaf7b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -52,6 +52,7 @@
"Back"
"Blacklist"
"Add to blacklist"
+ "Buy @ %1$s"
"Cancel"
"Clear"
"Close"