feat(ui): Implement an about screen

This commit is contained in:
Nishant Mishra
2025-11-30 23:30:47 +05:30
parent 0f9f793bc5
commit 6c32743d22
18 changed files with 510 additions and 157 deletions

View File

@@ -17,18 +17,12 @@
package org.nsh07.pomodoro.ui.settingsScreen.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.Text
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
@@ -38,56 +32,44 @@ import org.nsh07.pomodoro.R
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun TopButton(
buttonColors: ButtonColors,
modifier: Modifier = Modifier
) {
fun TopButton(modifier: Modifier = Modifier) {
val uriHandler = LocalUriHandler.current
Button(
colors = buttonColors,
onClick = { uriHandler.openUri("https://coff.ee/nsh07") },
shapes = ButtonDefaults.shapes(),
modifier = modifier
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
ClickableListItem(
leadingContent = {
Icon(
painterResource(R.drawable.bmc),
tint = colorScheme.primary,
contentDescription = null,
modifier = Modifier.height(24.dp)
modifier = Modifier.size(24.dp)
)
Text(text = stringResource(R.string.bmc))
}
}
},
headlineContent = { Text(stringResource(R.string.bmc)) },
supportingContent = { Text(stringResource(R.string.bmc_desc)) },
trailingContent = { Icon(painterResource(R.drawable.open_in_browser), null) },
items = 2,
index = 0,
modifier = modifier
) { uriHandler.openUri("https://coff.ee/nsh07") }
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun BottomButton(
buttonColors: ButtonColors,
modifier: Modifier = Modifier
) {
fun BottomButton(modifier: Modifier = Modifier) {
val uriHandler = LocalUriHandler.current
Button(
colors = buttonColors,
onClick = { uriHandler.openUri("https://hosted.weblate.org/engage/tomato/") },
shapes = ButtonDefaults.shapes(),
modifier = modifier
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
ClickableListItem(
leadingContent = {
Icon(
painterResource(R.drawable.weblate),
tint = colorScheme.secondary,
contentDescription = null,
modifier = Modifier.size(20.dp)
modifier = Modifier.size(24.dp)
)
Text(text = stringResource(R.string.help_with_translation))
}
}
},
headlineContent = { Text(stringResource(R.string.help_with_translation)) },
supportingContent = { Text(stringResource(R.string.help_with_translation_desc)) },
trailingContent = { Icon(painterResource(R.drawable.open_in_browser), null) },
items = 2,
index = 1,
modifier = modifier
) { uriHandler.openUri("https://hosted.weblate.org/engage/tomato/") }
}

View File

@@ -0,0 +1,39 @@
/*
* 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
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalLayoutDirection
@Composable
fun mergePaddingValues(
topSource: PaddingValues,
restSource: PaddingValues
): PaddingValues {
val layoutDirection = LocalLayoutDirection.current
return PaddingValues(
top = topSource.calculateTopPadding(),
bottom = restSource.calculateBottomPadding(),
start = restSource.calculateStartPadding(layoutDirection),
end = restSource.calculateEndPadding(layoutDirection)
)
}

View File

@@ -29,8 +29,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -58,7 +56,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
@@ -70,6 +67,7 @@ import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.ui.NavDisplay
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.Screen
import org.nsh07.pomodoro.ui.mergePaddingValues
import org.nsh07.pomodoro.ui.settingsScreen.components.AboutCard
import org.nsh07.pomodoro.ui.settingsScreen.components.ClickableListItem
import org.nsh07.pomodoro.ui.settingsScreen.components.LocaleBottomSheet
@@ -153,7 +151,6 @@ private fun SettingsScreen(
modifier: Modifier = Modifier
) {
val context = LocalContext.current
val layoutDirection = LocalLayoutDirection.current
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val currentLocales =
@@ -210,12 +207,7 @@ private fun SettingsScreen(
},
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val insets = PaddingValues(
bottom = contentPadding.calculateBottomPadding(),
top = innerPadding.calculateTopPadding(),
start = innerPadding.calculateStartPadding(layoutDirection),
end = innerPadding.calculateEndPadding(layoutDirection)
)
val insets = mergePaddingValues(innerPadding, contentPadding)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp),
contentPadding = insets,

View File

@@ -26,7 +26,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
@@ -67,11 +66,6 @@ fun AboutCard(
),
shape = shapes.extraLarge
) {
val buttonColors = ButtonDefaults.buttonColors(
containerColor = colorScheme.onPrimaryContainer,
contentColor = colorScheme.primaryContainer
)
Row(
modifier = Modifier
.padding(16.dp)
@@ -122,8 +116,8 @@ fun AboutCard(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TopButton(buttonColors)
BottomButton(buttonColors)
// TopButton(buttonColors)
// BottomButton(buttonColors)
}
}
}

View File

@@ -0,0 +1,270 @@
/*
* 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.screens
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
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.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.MaterialShapes
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.toShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import org.nsh07.pomodoro.BuildConfig
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.mergePaddingValues
import org.nsh07.pomodoro.ui.settingsScreen.components.BottomButton
import org.nsh07.pomodoro.ui.settingsScreen.components.TopButton
import org.nsh07.pomodoro.ui.theme.AppFonts.googleFlex600
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
import org.nsh07.pomodoro.ui.theme.TomatoTheme
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun AboutScreen(
contentPadding: PaddingValues,
isPlus: Boolean,
onBack: () -> Unit,
modifier: Modifier = Modifier
) {
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val context = LocalContext.current
val uriHandler = LocalUriHandler.current
val socialLinks = remember {
listOf(
SocialLink(R.drawable.github, "https://github.com/nsh07"),
SocialLink(R.drawable.x, "https://x.com/nsh_zero7"),
SocialLink(R.drawable.globe, "https://nsh07.github.io"),
SocialLink(R.drawable.email, "mailto:nishant.28@outlook.com")
)
}
Scaffold(
topBar = {
LargeFlexibleTopAppBar(
title = {
Text(stringResource(R.string.about), fontFamily = robotoFlexTopBar)
},
subtitle = {
Text(stringResource(R.string.app_name))
},
navigationIcon = {
FilledTonalIconButton(
onClick = onBack,
shapes = IconButtonDefaults.shapes(),
colors = IconButtonDefaults.filledTonalIconButtonColors(containerColor = listItemColors.containerColor)
) {
Icon(
painterResource(R.drawable.arrow_back),
null
)
}
},
colors = topBarColors,
scrollBehavior = scrollBehavior
)
},
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val insets = mergePaddingValues(innerPadding, contentPadding)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp),
contentPadding = insets,
modifier = Modifier
.background(topBarColors.containerColor)
.fillMaxSize()
.padding(horizontal = 16.dp)
) {
item {
Box(Modifier.background(listItemColors.containerColor, topListItemShape)) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(16.dp)
) {
Icon(
painterResource(R.drawable.ic_launcher_monochrome),
tint = colorScheme.onPrimaryContainer,
contentDescription = null,
modifier = Modifier
.size(64.dp)
.background(
colorScheme.primaryContainer,
MaterialShapes.Cookie12Sided.toShape()
)
)
Spacer(Modifier.width(16.dp))
Column {
Text(
if (!isPlus) stringResource(R.string.app_name)
else stringResource(R.string.app_name_plus),
color = colorScheme.onSurface,
style = typography.titleLarge,
fontFamily = googleFlex600
)
Text(
text = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
style = typography.labelLarge,
color = colorScheme.primary
)
}
Spacer(Modifier.weight(1f))
Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
FilledTonalIconButton(
onClick = {
Toast.makeText(context, "Coming soon...", Toast.LENGTH_SHORT)
.show()
},
shapes = IconButtonDefaults.shapes()
) {
Icon(
painterResource(R.drawable.discord),
contentDescription = "Discord",
modifier = Modifier.size(24.dp)
)
}
FilledTonalIconButton(
onClick = { uriHandler.openUri("https://github.com/nsh07/Tomato") },
shapes = IconButtonDefaults.shapes()
) {
Icon(
painterResource(R.drawable.github),
contentDescription = "GitHub",
modifier = Modifier.size(24.dp)
)
}
}
}
}
}
item {
Box(Modifier.background(listItemColors.containerColor, bottomListItemShape)) {
Column(modifier = Modifier.padding(16.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painterResource(R.drawable.pfp),
tint = colorScheme.onSecondaryContainer,
contentDescription = null,
modifier = Modifier
.size(64.dp)
.background(
colorScheme.secondaryContainer,
MaterialShapes.Square.toShape()
)
.padding(8.dp)
)
Spacer(Modifier.width(16.dp))
Column {
Text(
"Nishant Mishra",
style = typography.titleLarge,
color = colorScheme.onSurface,
fontFamily = googleFlex600
)
Text(
"Developer",
style = typography.labelLarge,
color = colorScheme.secondary
)
}
Spacer(Modifier.weight(1f))
}
Spacer(Modifier.height(8.dp))
Row {
Spacer(Modifier.width((64 + 16).dp))
FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
socialLinks.fastForEach {
FilledTonalIconButton(
onClick = { uriHandler.openUri(it.url) },
shapes = IconButtonDefaults.shapes(),
modifier = Modifier.width(52.dp)
) {
Icon(
painterResource(it.icon),
null,
modifier = Modifier.size(ButtonDefaults.SmallIconSize)
)
}
}
}
}
}
}
}
item { Spacer(Modifier.height(12.dp)) }
item { TopButton() }
item { BottomButton() }
}
}
}
@Preview
@Composable
private fun AboutScreenPreview() {
TomatoTheme(dynamicColor = false) {
AboutScreen(
contentPadding = PaddingValues(),
isPlus = true,
onBack = {}
)
}
}
data class SocialLink(
@param:DrawableRes val icon: Int,
val url: String
)

View File

@@ -30,8 +30,6 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -60,7 +58,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -69,6 +66,7 @@ import androidx.core.net.toUri
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.mergePaddingValues
import org.nsh07.pomodoro.ui.settingsScreen.SettingsSwitchItem
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsAction
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsState
@@ -91,7 +89,6 @@ fun AlarmSettings(
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val context = LocalContext.current
val layoutDirection = LocalLayoutDirection.current
var alarmName by remember { mutableStateOf("...") }
@@ -181,12 +178,7 @@ fun AlarmSettings(
},
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val insets = PaddingValues(
bottom = contentPadding.calculateBottomPadding(),
top = innerPadding.calculateTopPadding(),
start = innerPadding.calculateStartPadding(layoutDirection),
end = innerPadding.calculateEndPadding(layoutDirection)
)
val insets = mergePaddingValues(innerPadding, contentPadding)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp),
contentPadding = insets,

View File

@@ -21,8 +21,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -44,12 +42,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.mergePaddingValues
import org.nsh07.pomodoro.ui.settingsScreen.SettingsSwitchItem
import org.nsh07.pomodoro.ui.settingsScreen.components.ColorSchemePickerListItem
import org.nsh07.pomodoro.ui.settingsScreen.components.PlusDivider
@@ -76,7 +74,6 @@ fun AppearanceSettings(
modifier: Modifier = Modifier
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val layoutDirection = LocalLayoutDirection.current
Scaffold(
topBar = {
@@ -105,12 +102,7 @@ fun AppearanceSettings(
},
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val insets = PaddingValues(
bottom = contentPadding.calculateBottomPadding(),
top = innerPadding.calculateTopPadding(),
start = innerPadding.calculateStartPadding(layoutDirection),
end = innerPadding.calculateEndPadding(layoutDirection)
)
val insets = mergePaddingValues(innerPadding, contentPadding)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp),
contentPadding = insets,

View File

@@ -30,8 +30,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -75,13 +73,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.mergePaddingValues
import org.nsh07.pomodoro.ui.settingsScreen.SettingsSwitchItem
import org.nsh07.pomodoro.ui.settingsScreen.components.MinuteInputField
import org.nsh07.pomodoro.ui.settingsScreen.components.PlusDivider
@@ -115,7 +113,6 @@ fun TimerSettings(
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val context = LocalContext.current
val layoutDirection = LocalLayoutDirection.current
val appName = stringResource(R.string.app_name)
val notificationManagerService =
remember { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
@@ -175,12 +172,7 @@ fun TimerSettings(
},
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val insets = PaddingValues(
bottom = contentPadding.calculateBottomPadding(),
top = innerPadding.calculateTopPadding(),
start = innerPadding.calculateStartPadding(layoutDirection),
end = innerPadding.calculateEndPadding(layoutDirection)
)
val insets = mergePaddingValues(innerPadding, contentPadding)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(2.dp),
contentPadding = insets,

View File

@@ -27,8 +27,6 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -64,7 +62,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalFontFamilyResolver
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -80,6 +77,7 @@ import com.patrykandpatrick.vico.core.common.data.ExtraStore
import org.nsh07.pomodoro.BuildConfig
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.data.Stat
import org.nsh07.pomodoro.ui.mergePaddingValues
import org.nsh07.pomodoro.ui.statsScreen.viewModel.StatsViewModel
import org.nsh07.pomodoro.ui.theme.AppFonts.googleFlex400
import org.nsh07.pomodoro.ui.theme.AppFonts.googleFlex600
@@ -133,7 +131,6 @@ fun StatsScreen(
modifier: Modifier = Modifier
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val layoutDirection = LocalLayoutDirection.current
val hoursFormat = stringResource(R.string.hours_format)
val hoursMinutesFormat = stringResource(R.string.hours_and_minutes_format)
@@ -200,12 +197,7 @@ fun StatsScreen(
},
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val insets = PaddingValues(
bottom = contentPadding.calculateBottomPadding(),
top = innerPadding.calculateTopPadding(),
start = innerPadding.calculateStartPadding(layoutDirection),
end = innerPadding.calculateEndPadding(layoutDirection)
)
val insets = mergePaddingValues(innerPadding, contentPadding)
LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
contentPadding = insets,

View File

@@ -41,8 +41,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -87,7 +85,6 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
@@ -98,6 +95,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation3.ui.LocalNavAnimatedContentScope
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.ui.mergePaddingValues
import org.nsh07.pomodoro.ui.theme.AppFonts.googleFlex600
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
import org.nsh07.pomodoro.ui.theme.TomatoTheme
@@ -118,7 +116,6 @@ fun SharedTransitionScope.TimerScreen(
) {
val motionScheme = motionScheme
val haptic = LocalHapticFeedback.current
val layoutDirection = LocalLayoutDirection.current
val color by animateColorAsState(
if (timerState.timerMode == TimerMode.FOCUS) colorScheme.primary
@@ -221,12 +218,7 @@ fun SharedTransitionScope.TimerScreen(
modifier = modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val insets = PaddingValues(
bottom = contentPadding.calculateBottomPadding(),
top = innerPadding.calculateTopPadding(),
start = innerPadding.calculateStartPadding(layoutDirection),
end = innerPadding.calculateEndPadding(layoutDirection)
)
val insets = mergePaddingValues(innerPadding, contentPadding)
LazyColumn(
verticalArrangement = Arrangement.Center,
horizontalAlignment = CenterHorizontally,

View File

@@ -22,5 +22,5 @@
android:viewportHeight="960">
<path
android:fillColor="#e3e3e3"
android:pathData="m321,880 l-71,-71 329,-329 -329,-329 71,-71 400,400L321,880Z" />
android:pathData="M579,480 L285,186q-15,-15 -14.5,-35.5T286,115q15,-15 35.5,-15t35.5,15l307,308q12,12 18,27t6,30q0,15 -6,30t-18,27L356,845q-15,15 -35,14.5T286,844q-15,-15 -15,-35.5t15,-35.5l293,-293Z" />
</vector>

View File

@@ -0,0 +1,26 @@
<!--
~ 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/>.
-->
<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="#e3e3e3"
android:pathData="M480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480v58q0,59 -40.5,100.5T740,680q-35,0 -66,-15t-52,-43q-29,29 -65.5,43.5T480,680q-83,0 -141.5,-58.5T280,480q0,-83 58.5,-141.5T480,280q83,0 141.5,58.5T680,480v58q0,26 17,44t43,18q26,0 43,-18t17,-44v-58q0,-134 -93,-227t-227,-93q-134,0 -227,93t-93,227q0,134 93,227t227,93h160q17,0 28.5,11.5T680,840q0,17 -11.5,28.5T640,880L480,880ZM480,600q50,0 85,-35t35,-85q0,-50 -35,-85t-85,-35q-50,0 -85,35t-35,85q0,50 35,85t85,35Z" />
</vector>

View File

@@ -0,0 +1,26 @@
<!--
~ 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/>.
-->
<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="#e3e3e3"
android:pathData="M480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880ZM480,800q134,0 227,-93t93,-227q0,-7 -0.5,-14.5T799,453q-5,29 -27,48t-52,19h-80q-33,0 -56.5,-23.5T560,440v-40L400,400v-80q0,-33 23.5,-56.5T480,240h40q0,-23 12.5,-40.5T563,171q-20,-5 -40.5,-8t-42.5,-3q-134,0 -227,93t-93,227h200q66,0 113,47t47,113v40L400,680v110q20,5 39.5,7.5T480,800Z" />
</vector>

View File

@@ -0,0 +1,26 @@
<!--
~ 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/>.
-->
<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="#e3e3e3"
android:pathData="M200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h240q17,0 28.5,11.5T480,160q0,17 -11.5,28.5T440,200L200,200v560h560v-240q0,-17 11.5,-28.5T800,480q17,0 28.5,11.5T840,520v240q0,33 -23.5,56.5T760,840L200,840ZM760,256L416,600q-11,11 -28,11t-28,-11q-11,-11 -11,-28t11,-28l344,-344L600,200q-17,0 -28.5,-11.5T560,160q0,-17 11.5,-28.5T600,120h200q17,0 28.5,11.5T840,160v200q0,17 -11.5,28.5T800,400q-17,0 -28.5,-11.5T760,360v-104Z" />
</vector>

View File

@@ -0,0 +1,26 @@
<!--
~ 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/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="655.36dp"
android:height="655.36dp"
android:viewportWidth="655.36"
android:viewportHeight="655.36">
<path
android:fillColor="#000000"
android:pathData="m167.68,493.2c-2.01,-0.71 -4.85,-3.61 -5.55,-5.66 -0.5,-1.47 -0.54,-12.62 -0.54,-159.86 0,-147.24 0.04,-158.4 0.54,-159.86 0.36,-1.07 1.11,-2.15 2.32,-3.37 2.92,-2.92 2.87,-2.91 17.68,-2.78 11.96,0.11 12.67,0.15 14.42,0.81 4.82,1.8 6.26,3.01 15,12.56 4.43,4.83 78.74,85.46 136.27,147.85 22.97,24.91 55,59.65 71.17,77.19 22.69,24.62 29.65,31.98 30.48,32.25 1.94,0.64 5.11,-0.56 6.2,-2.35 0.62,-1.02 0.63,-2.21 0.55,-102.73l-0.08,-101.7 -1.17,-1.07c-1.65,-1.51 -4.07,-2.12 -5.8,-1.47 -0.42,0.16 -4.86,4.73 -9.86,10.16 -26.15,28.39 -31.85,34.48 -33.05,35.36 -2.05,1.49 -4.2,2.49 -6.84,3.16 -2.24,0.57 -3.65,0.62 -18.48,0.62H364.89l-1.06,-0.72c-1.52,-1.03 -2.55,-3.15 -2.55,-5.24v-1.7l43.41,-47.13c23.88,-25.92 44.67,-48.38 46.21,-49.91 2.14,-2.13 3.35,-3.04 5.21,-3.93 4.02,-1.93 5.35,-2.08 18.32,-2.09 13.56,-0.01 13.64,0.01 16.48,2.85 1.21,1.21 1.96,2.29 2.32,3.36 0.5,1.47 0.54,12.62 0.54,159.86 0,147.24 -0.04,158.4 -0.54,159.86 -0.36,1.07 -1.11,2.15 -2.32,3.37 -2.92,2.92 -2.87,2.91 -17.68,2.78 -13.77,-0.12 -13.42,-0.08 -17.7,-2.25 -2.57,-1.3 -4.16,-2.81 -12.2,-11.6 -4.19,-4.58 -25.41,-27.62 -47.15,-51.2 -73.9,-80.15 -92.94,-100.8 -109.6,-118.88 -9.15,-9.93 -27.87,-30.23 -41.6,-45.12C231.23,249.76 217.12,234.45 213.6,230.62c-4.68,-5.09 -6.67,-7.03 -7.4,-7.23 -2.07,-0.58 -5.15,0.57 -6.21,2.3 -0.62,1.01 -0.62,2.41 -0.62,101.98 0,99.66 0.01,100.97 0.63,101.99 1.09,1.78 4.27,2.99 6.2,2.35 0.82,-0.27 5.7,-5.37 20.84,-21.8 10.87,-11.79 20.39,-21.99 21.15,-22.66 1.94,-1.71 4.79,-3.15 7.68,-3.89 2.3,-0.59 3.59,-0.63 18.78,-0.63h16.31l1.21,1.21c1.63,1.63 2.5,4.42 1.86,5.99 -0.24,0.59 -7.73,8.97 -16.91,18.92 -18.82,20.39 -45.98,49.88 -59.79,64.91 -13.19,14.35 -14.75,15.9 -17.35,17.27 -4.27,2.25 -5.61,2.41 -18.99,2.38 -9.97,-0.02 -12.1,-0.11 -13.31,-0.53z" />
</vector>

View File

@@ -0,0 +1,26 @@
<!--
~ 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/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M14.234,10.162 L22.977,0h-2.072l-7.591,8.824L7.251,0L0.258,0l9.168,13.343L0.258,24L2.33,24l8.016,-9.318L16.749,24h6.993zM11.397,13.461 L10.468,12.132L3.076,1.56h3.182l5.965,8.532 0.929,1.329 7.754,11.09h-3.182z" />
</vector>

View File

@@ -45,7 +45,7 @@
<string name="focus">Focus</string>
<string name="focus_per_day_avg">focus per day (avg)</string>
<string name="get_plus">Get Tomato+</string>
<string name="help_with_translation">Help with translation</string>
<string name="help_with_translation">Help translate Tomato</string>
<string name="hours_and_minutes_format">%dh %dm</string>
<string name="hours_format">%dh</string>
<string name="language">Language</string>
@@ -97,4 +97,8 @@
<string name="vibrate">Vibration</string>
<string name="vibrate_desc">Vibrate when a timer completes</string>
<string name="weekly_productivity_analysis">Weekly productivity analysis</string>
<string name="about">About</string>
<string name="help_with_translation_desc">Translate Tomato into your language</string>
<string name="rate_on_google_play_desc">Liked the app? Write a review!</string>
<string name="bmc_desc">Support me with a small donation</string>
</resources>

View File

@@ -17,17 +17,12 @@
package org.nsh07.pomodoro.ui.settingsScreen.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.Text
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
@@ -37,56 +32,43 @@ import org.nsh07.pomodoro.R
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun TopButton(
buttonColors: ButtonColors,
modifier: Modifier = Modifier
) {
fun TopButton(modifier: Modifier = Modifier) {
val uriHandler = LocalUriHandler.current
Button(
colors = buttonColors,
onClick = { uriHandler.openUri("https://hosted.weblate.org/engage/tomato/") },
shapes = ButtonDefaults.shapes(),
modifier = modifier
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
ClickableListItem(
leadingContent = {
Icon(
painterResource(R.drawable.weblate),
tint = colorScheme.primary,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
Text(text = stringResource(R.string.help_with_translation))
}
}
},
headlineContent = { Text(stringResource(R.string.help_with_translation)) },
supportingContent = { Text(stringResource(R.string.help_with_translation_desc)) },
trailingContent = { Icon(painterResource(R.drawable.open_in_browser), null) },
items = 2,
index = 0,
modifier = modifier
) { uriHandler.openUri("https://hosted.weblate.org/engage/tomato/") }
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun BottomButton(
buttonColors: ButtonColors,
modifier: Modifier = Modifier
) {
fun BottomButton(modifier: Modifier = Modifier) {
val uriHandler = LocalUriHandler.current
Button(
colors = buttonColors,
onClick = { uriHandler.openUri("https://play.google.com/store/apps/details?id=org.nsh07.pomodoro") },
shapes = ButtonDefaults.shapes(),
modifier = modifier
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
ClickableListItem(
leadingContent = {
Icon(
painterResource(R.drawable.play_store),
tint = colorScheme.secondary,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
Text(text = stringResource(R.string.rate_on_google_play))
}
}
},
headlineContent = { Text(stringResource(R.string.rate_on_google_play)) },
supportingContent = { Text(stringResource(R.string.rate_on_google_play_desc)) },
items = 2,
index = 1,
modifier = modifier
) { uriHandler.openUri("https://play.google.com/store/apps/details?id=org.nsh07.pomodoro") }
}