Merge pull request #114 from nsh07/language-selection
Language selection on API 33+
This commit is contained in:
@@ -94,9 +94,8 @@ translating this project into languages you know.
|
||||
|
||||
## Download
|
||||
|
||||
- **Google Play Store** (recommended): Tomato will soon be available (currently in closed testing)
|
||||
on the Google Play Store.
|
||||
[You can find it through this link](https://play.google.com/store/apps/details?id=org.nsh07.pomodoro).
|
||||
- **Google Play Store** (recommended): Tomato is available on the Google Play Store.
|
||||
[You can download it through this link](https://play.google.com/store/apps/details?id=org.nsh07.pomodoro).
|
||||
- **F-Droid** (recommended): Tomato is available on the official F-Droid repository. Simply open
|
||||
your preferred F-Droid app and search for Tomato. Updates on F-Droid are generally a week late. To
|
||||
get faster updates, you can install it through
|
||||
@@ -109,7 +108,7 @@ translating this project into languages you know.
|
||||
> To [verify](https://developer.android.com/studio/command-line/apksigner#usage-verify) the APK
|
||||
> downloaded from GitHub, use the following signing certificate fingerprints:
|
||||
> ```
|
||||
> SHA1: B1:4E:17:93:11:E8:DB:D5:35:EF:8D:E9:FB:8F:FF:08:F8:EC:65:08
|
||||
> SHA1: B1:4E:17:93:11:E8:DB:D5:35:EF:8D:E9:FB:8F:FF:08:F8:EC:65:08
|
||||
> SHA256: 07:BE:F3:05:81:BA:EE:8F:45:EC:93:E4:7E:E6:8E:F2:08:74:E5:0E:F5:70:9C:78:B2:EE:67:AC:86:BE:4C:3D
|
||||
> ```
|
||||
> The SHA256 and MD5 hashes of the individual APK files are also available in the `checksum.txt`
|
||||
|
||||
@@ -47,6 +47,10 @@ android {
|
||||
versionName = "1.6.2"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -78,7 +78,7 @@ fun TomatoPlusPaywallDialog(
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Button(onClick = { uriHandler.openUri("https://coff.ee/nsh07") }) {
|
||||
Text("Buy Me A Coffee")
|
||||
Text(stringResource(R.string.bmc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
package org.nsh07.pomodoro.ui.settingsScreen
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.LocaleManager
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
@@ -46,8 +48,10 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -68,6 +72,7 @@ import org.nsh07.pomodoro.service.TimerService
|
||||
import org.nsh07.pomodoro.ui.Screen
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.components.AboutCard
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.components.ClickableListItem
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.components.LocaleBottomSheet
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.components.PlusPromo
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.screens.AlarmSettings
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.screens.AppearanceSettings
|
||||
@@ -76,6 +81,7 @@ import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState
|
||||
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
|
||||
import org.nsh07.pomodoro.ui.settingsScreens
|
||||
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
|
||||
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
|
||||
import org.nsh07.pomodoro.ui.theme.CustomColors.topBarColors
|
||||
|
||||
|
||||
@@ -182,6 +188,22 @@ private fun SettingsScreen(
|
||||
val context = LocalContext.current
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
|
||||
val currentLocales =
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
context
|
||||
.getSystemService(LocaleManager::class.java)
|
||||
.applicationLocales
|
||||
} else null
|
||||
val currentLocalesSize = currentLocales?.size() ?: 0
|
||||
|
||||
var showLocaleSheet by remember { mutableStateOf(false) }
|
||||
|
||||
if (showLocaleSheet && currentLocales != null)
|
||||
LocaleBottomSheet(
|
||||
currentLocales = currentLocales,
|
||||
setShowSheet = { showLocaleSheet = it }
|
||||
)
|
||||
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
onBack = backStack::removeLastOrNull,
|
||||
@@ -266,6 +288,30 @@ private fun SettingsScreen(
|
||||
}
|
||||
|
||||
item { Spacer(Modifier.height(12.dp)) }
|
||||
|
||||
if (currentLocales != null)
|
||||
item {
|
||||
ClickableListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painterResource(R.drawable.language),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
headlineContent = { Text(stringResource(R.string.language)) },
|
||||
supportingContent = {
|
||||
Text(
|
||||
if (currentLocalesSize > 0) currentLocales.get(0).displayName
|
||||
else stringResource(R.string.system_default)
|
||||
)
|
||||
},
|
||||
colors = listItemColors,
|
||||
items = 1,
|
||||
index = 0
|
||||
) { showLocaleSheet = true }
|
||||
}
|
||||
|
||||
item { Spacer(Modifier.height(12.dp)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,11 +129,11 @@ fun AboutCard(
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.bmc),
|
||||
contentDescription = "Buy me a coffee",
|
||||
contentDescription = null,
|
||||
modifier = Modifier.height(24.dp)
|
||||
)
|
||||
|
||||
Text(text = "Buy me a coffee")
|
||||
Text(text = stringResource(R.string.bmc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,11 +147,11 @@ fun AboutCard(
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.play_store),
|
||||
contentDescription = "Rate on Google Play",
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
|
||||
Text(text = "Rate on Google Play")
|
||||
Text(text = stringResource(R.string.rate_on_google_play))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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 android.app.LocaleConfig
|
||||
import android.app.LocaleManager
|
||||
import android.os.Build
|
||||
import android.os.LocaleList
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.shapes
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import org.nsh07.pomodoro.R
|
||||
import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
|
||||
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape
|
||||
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape
|
||||
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
|
||||
import java.util.Locale
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun LocaleBottomSheet(
|
||||
currentLocales: LocaleList,
|
||||
setShowSheet: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val supportedLocales = remember {
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
LocaleConfig(context).supportedLocales
|
||||
} else null
|
||||
}
|
||||
val supportedLocalesSize = supportedLocales?.size() ?: 0
|
||||
|
||||
val supportedLocalesList: List<AppLocale>? = remember {
|
||||
if (supportedLocales != null) {
|
||||
buildList {
|
||||
for (i in 0 until supportedLocalesSize) {
|
||||
add(AppLocale(supportedLocales.get(i), supportedLocales.get(i).displayName))
|
||||
}
|
||||
sortWith(compareBy { it.name })
|
||||
}
|
||||
} else null
|
||||
}
|
||||
|
||||
val bottomSheetState = rememberModalBottomSheetState()
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { setShowSheet(false) },
|
||||
sheetState = bottomSheetState,
|
||||
modifier = modifier
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = stringResource(R.string.choose_language),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
if (supportedLocalesList != null) {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
state = listState,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(shapes.largeIncreased)
|
||||
) {
|
||||
item {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(stringResource(R.string.system_default))
|
||||
},
|
||||
trailingContent = {
|
||||
if (currentLocales.isEmpty)
|
||||
Icon(
|
||||
painterResource(R.drawable.check),
|
||||
contentDescription = stringResource(R.string.selected)
|
||||
)
|
||||
},
|
||||
colors =
|
||||
if (currentLocales.isEmpty)
|
||||
ListItemDefaults.colors(
|
||||
containerColor = colorScheme.primaryContainer.copy(
|
||||
0.3f
|
||||
)
|
||||
)
|
||||
else listItemColors,
|
||||
modifier = Modifier
|
||||
.clip(if (currentLocales.isEmpty) CircleShape else shapes.largeIncreased)
|
||||
.clickable(
|
||||
onClick = {
|
||||
scope
|
||||
.launch { bottomSheetState.hide() }
|
||||
.invokeOnCompletion {
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
context
|
||||
.getSystemService(LocaleManager::class.java)
|
||||
.applicationLocales = LocaleList()
|
||||
}
|
||||
setShowSheet(false)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(Modifier.height(12.dp))
|
||||
}
|
||||
itemsIndexed(
|
||||
supportedLocalesList,
|
||||
key = { _: Int, it: AppLocale -> it.name }
|
||||
) { index, it ->
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(it.name)
|
||||
},
|
||||
trailingContent = {
|
||||
if (!currentLocales.isEmpty && it.locale == currentLocales.get(0))
|
||||
Icon(
|
||||
painterResource(R.drawable.check),
|
||||
tint = colorScheme.primary,
|
||||
contentDescription = stringResource(R.string.selected)
|
||||
)
|
||||
},
|
||||
colors =
|
||||
if (!currentLocales.isEmpty && it.locale == currentLocales.get(0))
|
||||
ListItemDefaults.colors(containerColor = colorScheme.primaryContainer)
|
||||
else listItemColors,
|
||||
modifier = Modifier
|
||||
.clip(
|
||||
if (!currentLocales.isEmpty && it.locale == currentLocales.get(0))
|
||||
CircleShape
|
||||
else when (index) {
|
||||
0 -> topListItemShape
|
||||
supportedLocalesSize - 1 -> bottomListItemShape
|
||||
else -> middleListItemShape
|
||||
}
|
||||
)
|
||||
.clickable(
|
||||
onClick = {
|
||||
scope
|
||||
.launch { bottomSheetState.hide() }
|
||||
.invokeOnCompletion { _ ->
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
context.getSystemService(LocaleManager::class.java)
|
||||
.applicationLocales =
|
||||
LocaleList(it.locale)
|
||||
}
|
||||
setShowSheet(false)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class AppLocale(
|
||||
val locale: Locale,
|
||||
val name: String
|
||||
)
|
||||
26
app/src/main/res/drawable/language.xml
Normal file
26
app/src/main/res/drawable/language.xml
Normal 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-82,0 -155,-31.5t-127.5,-86Q143,708 111.5,635T80,480q0,-83 31.5,-155.5t86,-127Q252,143 325,111.5T480,80q83,0 155.5,31.5t127,86q54.5,54.5 86,127T880,480q0,82 -31.5,155t-86,127.5q-54.5,54.5 -127,86T480,880ZM480,798q26,-36 45,-75t31,-83L404,640q12,44 31,83t45,75ZM376,782q-18,-33 -31.5,-68.5T322,640L204,640q29,50 72.5,87t99.5,55ZM584,782q56,-18 99.5,-55t72.5,-87L638,640q-9,38 -22.5,73.5T584,782ZM170,560h136q-3,-20 -4.5,-39.5T300,480q0,-21 1.5,-40.5T306,400L170,400q-5,20 -7.5,39.5T160,480q0,21 2.5,40.5T170,560ZM386,560h188q3,-20 4.5,-39.5T580,480q0,-21 -1.5,-40.5T574,400L386,400q-3,20 -4.5,39.5T380,480q0,21 1.5,40.5T386,560ZM654,560h136q5,-20 7.5,-39.5T800,480q0,-21 -2.5,-40.5T790,400L654,400q3,20 4.5,39.5T660,480q0,21 -1.5,40.5T654,560ZM638,320h118q-29,-50 -72.5,-87T584,178q18,33 31.5,68.5T638,320ZM404,320h152q-12,-44 -31,-83t-45,-75q-26,36 -45,75t-31,83ZM204,320h118q9,-38 22.5,-73.5T376,178q-56,18 -99.5,55T204,320Z" />
|
||||
</vector>
|
||||
17
app/src/main/res/resources.properties
Normal file
17
app/src/main/res/resources.properties
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
unqualifiedResLocale=en
|
||||
@@ -87,4 +87,9 @@
|
||||
<string name="dynamic_color_desc">Adapt theme colors from your wallpaper</string>
|
||||
<string name="tomato_foss">Tomato FOSS</string>
|
||||
<string name="tomato_foss_desc">All features are unlocked in this version. If my app made a difference in your life, please consider supporting me by donating on %1$s.</string>
|
||||
<string name="language">Language</string>
|
||||
<string name="choose_language">Choose language</string>
|
||||
<string name="rate_on_google_play">Rate on Google Play</string>
|
||||
<string name="bmc">BuyMeACoffee</string>
|
||||
<string name="selected">Selected</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user