fix(billing): adapt UI according to plus status

This commit is contained in:
Nishant Mishra
2025-10-27 20:27:09 +05:30
parent 540b941b23
commit a985e8d0fc
6 changed files with 159 additions and 43 deletions

View File

@@ -17,11 +17,68 @@
package org.nsh07.pomodoro.billing package org.nsh07.pomodoro.billing
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
@Composable @Composable
fun TomatoPlusPaywallDialog( fun TomatoPlusPaywallDialog(
isPlus: Boolean, isPlus: Boolean,
onDismiss: () -> Unit onDismiss: () -> Unit
) { ) {
val uriHandler = LocalUriHandler.current
BackHandler(enabled = true, onDismiss)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.background(colorScheme.surface)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(
painterResource(R.drawable.bmc),
null,
tint = colorScheme.onSurface
)
Spacer(Modifier.height(16.dp))
Text(
"Tomato FOSS",
style = typography.headlineSmall,
fontFamily = robotoFlexTopBar,
color = colorScheme.onSurface
)
Spacer(Modifier.height(8.dp))
Text(
"All features are unlocked in this version. If my app made a difference in your life, please consider supporting me by donating on ${"BuyMeACoffee"}.",
textAlign = TextAlign.Center,
color = colorScheme.onSurfaceVariant,
modifier = Modifier.padding(horizontal = 24.dp)
)
Spacer(Modifier.height(16.dp))
Button(onClick = { uriHandler.openUri("https://coff.ee/nsh07") }) {
Text("Buy Me A Coffee")
}
}
}
} }

View File

@@ -164,6 +164,7 @@ fun AppScreen(
entry<Screen.Timer> { entry<Screen.Timer> {
TimerScreen( TimerScreen(
timerState = uiState, timerState = uiState,
isPlus = isPlus,
progress = { progress }, progress = { progress },
onAction = { action -> onAction = { action ->
when (action) { when (action) {

View File

@@ -26,26 +26,19 @@ import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
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.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.SliderState import androidx.compose.material3.SliderState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
@@ -58,7 +51,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.snapshots.SnapshotStateList
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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -76,6 +68,7 @@ import org.nsh07.pomodoro.service.TimerService
import org.nsh07.pomodoro.ui.Screen import org.nsh07.pomodoro.ui.Screen
import org.nsh07.pomodoro.ui.settingsScreen.components.AboutCard import org.nsh07.pomodoro.ui.settingsScreen.components.AboutCard
import org.nsh07.pomodoro.ui.settingsScreen.components.ClickableListItem import org.nsh07.pomodoro.ui.settingsScreen.components.ClickableListItem
import org.nsh07.pomodoro.ui.settingsScreen.components.PlusPromo
import org.nsh07.pomodoro.ui.settingsScreen.screens.AlarmSettings import org.nsh07.pomodoro.ui.settingsScreen.screens.AlarmSettings
import org.nsh07.pomodoro.ui.settingsScreen.screens.AppearanceSettings import org.nsh07.pomodoro.ui.settingsScreen.screens.AppearanceSettings
import org.nsh07.pomodoro.ui.settingsScreen.screens.TimerSettings import org.nsh07.pomodoro.ui.settingsScreen.screens.TimerSettings
@@ -233,44 +226,20 @@ private fun SettingsScreen(
) { ) {
item { Spacer(Modifier.height(12.dp)) } item { Spacer(Modifier.height(12.dp)) }
item { if (!isPlus) item {
Row( PlusPromo(isPlus, setShowPaywall)
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(CircleShape)
.background(colorScheme.primary)
.padding(16.dp)
.clickable { setShowPaywall(true) }
) {
Icon(
painterResource(R.drawable.tomato_logo_notification),
null,
tint = colorScheme.onPrimary,
modifier = Modifier
.size(24.dp)
)
Spacer(Modifier.width(8.dp))
Text(
if (!isPlus) stringResource(R.string.get_plus)
else stringResource(R.string.app_name_plus),
style = typography.titleLarge,
fontFamily = robotoFlexTopBar,
color = colorScheme.onPrimary
)
Spacer(Modifier.weight(1f))
Icon(
painterResource(R.drawable.arrow_forward_big),
null,
tint = colorScheme.onPrimary
)
}
Spacer(Modifier.height(14.dp)) Spacer(Modifier.height(14.dp))
} }
item { AboutCard() } item { AboutCard(isPlus) }
item { Spacer(Modifier.height(12.dp)) } item { Spacer(Modifier.height(12.dp)) }
if (isPlus) item {
PlusPromo(isPlus, setShowPaywall)
Spacer(Modifier.height(14.dp))
}
itemsIndexed(settingsScreens) { index, item -> itemsIndexed(settingsScreens) { index, item ->
ClickableListItem( ClickableListItem(
leadingContent = { leadingContent = {

View File

@@ -51,7 +51,10 @@ import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
// Taken from https://github.com/shub39/Grit/blob/master/app/src/main/java/com/shub39/grit/core/presentation/settings/ui/component/AboutApp.kt // Taken from https://github.com/shub39/Grit/blob/master/app/src/main/java/com/shub39/grit/core/presentation/settings/ui/component/AboutApp.kt
@Composable @Composable
fun AboutCard(modifier: Modifier = Modifier) { fun AboutCard(
isPlus: Boolean,
modifier: Modifier = Modifier
) {
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
val context = LocalContext.current val context = LocalContext.current
@@ -77,7 +80,8 @@ fun AboutCard(modifier: Modifier = Modifier) {
) { ) {
Column { Column {
Text( Text(
text = stringResource(R.string.app_name), if (!isPlus) stringResource(R.string.app_name)
else stringResource(R.string.app_name_plus),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
fontFamily = robotoFlexTopBar fontFamily = robotoFlexTopBar
) )

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2025 Nishant Mishra
*
* This file is part of Tomato - a minimalist pomodoro timer for Android.
*
* Tomato 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 3 of the
* License, or (at your option) any later version.
*
* Tomato 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 Tomato.
* If not, see <https://www.gnu.org/licenses/>.
*/
package org.nsh07.pomodoro.ui.settingsScreen.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
@Composable
fun PlusPromo(
isPlus: Boolean,
setShowPaywall: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
val container = if (isPlus) colorScheme.surfaceBright else colorScheme.primary
val onContainer = if (isPlus) colorScheme.onSurface else colorScheme.onPrimary
val onContainerVariant = if (isPlus) colorScheme.onSurfaceVariant else colorScheme.onPrimary
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.clip(CircleShape)
.background(container)
.padding(16.dp)
.clickable { setShowPaywall(true) }
) {
Icon(
painterResource(R.drawable.tomato_logo_notification),
null,
tint = onContainerVariant,
modifier = Modifier
.size(24.dp)
)
Spacer(Modifier.width(8.dp))
Text(
if (!isPlus) stringResource(R.string.get_plus)
else stringResource(R.string.app_name_plus),
style = typography.titleLarge,
fontFamily = robotoFlexTopBar,
color = onContainer
)
Spacer(Modifier.weight(1f))
Icon(
painterResource(R.drawable.arrow_forward_big),
null,
tint = onContainerVariant
)
}
}

View File

@@ -107,6 +107,7 @@ import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerState
@Composable @Composable
fun SharedTransitionScope.TimerScreen( fun SharedTransitionScope.TimerScreen(
timerState: TimerState, timerState: TimerState,
isPlus: Boolean,
progress: () -> Float, progress: () -> Float,
onAction: (TimerAction) -> Unit, onAction: (TimerAction) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
@@ -159,7 +160,8 @@ fun SharedTransitionScope.TimerScreen(
when (it) { when (it) {
TimerMode.BRAND -> TimerMode.BRAND ->
Text( Text(
stringResource(R.string.app_name), if (!isPlus) stringResource(R.string.app_name)
else stringResource(R.string.app_name_plus),
style = TextStyle( style = TextStyle(
fontFamily = robotoFlexTopBar, fontFamily = robotoFlexTopBar,
fontSize = 32.sp, fontSize = 32.sp,
@@ -552,6 +554,7 @@ fun TimerScreenPreview() {
SharedTransitionLayout { SharedTransitionLayout {
TimerScreen( TimerScreen(
timerState, timerState,
isPlus = true,
{ 0.3f }, { 0.3f },
{} {}
) )