Skip to content

Commit 64f9da0

Browse files
authored
Merge pull request #408 from synonymdev/feat/language-settings
Language Settings
2 parents f717317 + 505e549 commit 64f9da0

File tree

12 files changed

+327
-11
lines changed

12 files changed

+327
-11
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ android {
129129
androidResources {
130130
@Suppress("UnstableApiUsage")
131131
localeFilters.addAll(listOf("en", "ar", "ca", "cs", "de", "el", "es", "fr", "it", "nl", "pl", "pt", "ru"))
132+
@Suppress("UnstableApiUsage")
133+
generateLocaleConfig = true
132134
}
133135
packaging {
134136
resources {

app/src/main/AndroidManifest.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
android:enabled="true"
3636
android:exported="false"/>
3737

38+
<service
39+
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
40+
android:enabled="false"
41+
android:exported="false">
42+
<meta-data
43+
android:name="autoStoreLocales"
44+
android:value="true" />
45+
</service>
46+
3847
<activity
3948
android:name=".ui.MainActivity"
4049
android:exported="true"

app/src/main/java/to/bitkit/data/dto/AppUpdaterDTO.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,12 @@ import kotlinx.serialization.Serializable
88
*/
99
@Serializable
1010
data class ReleaseInfoDTO(
11-
@SerialName("platforms")
1211
val platforms: Platforms,
1312
)
1413

1514
@Serializable
1615
data class Platforms(
17-
@SerialName("android")
1816
val android: PlatformDetails,
19-
@SerialName("ios")
2017
val ios: PlatformDetails?,
2118
)
2219

@@ -25,19 +22,15 @@ data class Platforms(
2522
*/
2623
@Serializable
2724
data class PlatformDetails(
28-
@SerialName("version")
2925
val version: String,
3026

31-
@SerialName("buildNumber")
3227
val buildNumber: Int,
3328

34-
@SerialName("notes")
3529
val notes: String,
3630

3731
@SerialName("pub_date")
3832
val pubDate: String,
3933

40-
@SerialName("url")
4134
val url: String,
4235

4336
@SerialName("critical")
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package to.bitkit.models
2+
3+
import kotlinx.serialization.Serializable
4+
import java.util.Locale
5+
6+
@Serializable
7+
enum class Language(
8+
val displayName: String,
9+
val languageCode: String,
10+
val countryCode: String? = null,
11+
val isSystemDefault: Boolean = false,
12+
) {
13+
SYSTEM_DEFAULT(
14+
displayName = "System Default",
15+
languageCode = "system",
16+
countryCode = null,
17+
isSystemDefault = true
18+
),
19+
ARABIC("العربية", "ar"),
20+
CATALAN("Català", "ca"),
21+
CZECH("Čeština", "cs"),
22+
DUTCH("Nederlands", "nl"),
23+
ENGLISH("English", "en", "US"),
24+
FRENCH("Français", "fr", "FR"),
25+
GERMAN("Deutsch", "de"),
26+
GREEK("Ελληνικά", "el"),
27+
ITALIAN("Italiano", "it"),
28+
POLISH("Polski", "pl"),
29+
PORTUGUESE("Português", "pt", "BR"),
30+
RUSSIAN("Русский", "ru"),
31+
SPANISH("Español", "es", "ES"),
32+
SPANISH_LATIN_AMERICA("Español (Latinoamérica)", "es", "419");
33+
34+
companion object {
35+
fun fromLanguageCode(languageCode: String, countryCode: String? = null): Language? {
36+
return entries.find { language ->
37+
language.languageCode == languageCode &&
38+
(countryCode == null || language.countryCode == countryCode)
39+
}
40+
}
41+
42+
fun fromLocale(locale: Locale): Language? {
43+
return fromLanguageCode(locale.language, locale.country.ifEmpty { null })
44+
}
45+
}
46+
}
47+
48+
fun Language.getLanguageTag(): String {
49+
return if (isSystemDefault) {
50+
""
51+
} else {
52+
if (countryCode != null) {
53+
"$languageCode-$countryCode"
54+
} else {
55+
languageCode
56+
}
57+
}
58+
}
59+
60+
fun Locale.toLanguage(): Language? {
61+
return Language.fromLocale(this)
62+
}

app/src/main/java/to/bitkit/ui/ContentView.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ import to.bitkit.ui.settings.BackupSettingsScreen
101101
import to.bitkit.ui.settings.BlocktankRegtestScreen
102102
import to.bitkit.ui.settings.CJitDetailScreen
103103
import to.bitkit.ui.settings.ChannelOrdersScreen
104+
import to.bitkit.ui.settings.LanguageSettingsScreen
104105
import to.bitkit.ui.settings.LogDetailScreen
105106
import to.bitkit.ui.settings.LogsScreen
106107
import to.bitkit.ui.settings.OrderDetailScreen
@@ -677,6 +678,12 @@ private fun NavGraphBuilder.settings(
677678
composableWithDefaultTransitions<Routes.RegtestSettings> {
678679
BlocktankRegtestScreen(navController)
679680
}
681+
composableWithDefaultTransitions<Routes.LanguageSettings> {
682+
LanguageSettingsScreen(
683+
onBackClick = { navController.popBackStack() },
684+
onCloseClick = { navController.navigateToHome() },
685+
)
686+
}
680687
}
681688

682689
private fun NavGraphBuilder.profile(
@@ -1374,6 +1381,10 @@ fun NavController.navigateToTagsSettings() = navigate(
13741381
route = Routes.TagsSettings,
13751382
)
13761383

1384+
fun NavController.navigateToLanguageSettings() = navigate(
1385+
route = Routes.LanguageSettings,
1386+
)
1387+
13771388
fun NavController.navigateToAdvancedSettings() = navigate(
13781389
route = Routes.AdvancedSettings,
13791390
)
@@ -1596,6 +1607,9 @@ sealed interface Routes {
15961607
@Serializable
15971608
data object QuickPaySettings : Routes
15981609

1610+
@Serializable
1611+
data object LanguageSettings : Routes
1612+
15991613
@Serializable
16001614
data object ProfileIntro : Routes
16011615

app/src/main/java/to/bitkit/ui/screens/CriticalUpdateScreen.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import android.content.Intent
44
import androidx.compose.foundation.Image
55
import androidx.compose.foundation.layout.Column
66
import androidx.compose.foundation.layout.aspectRatio
7-
import androidx.compose.foundation.layout.fillMaxSize
87
import androidx.compose.foundation.layout.fillMaxWidth
98
import androidx.compose.foundation.layout.padding
10-
import androidx.compose.foundation.layout.systemBarsPadding
119
import androidx.compose.runtime.Composable
1210
import androidx.compose.ui.Modifier
1311
import androidx.compose.ui.platform.LocalContext
@@ -23,6 +21,7 @@ import to.bitkit.ui.components.Display
2321
import to.bitkit.ui.components.PrimaryButton
2422
import to.bitkit.ui.components.VerticalSpacer
2523
import to.bitkit.ui.scaffold.AppTopBar
24+
import to.bitkit.ui.shared.util.screen
2625
import to.bitkit.ui.theme.AppThemeSurface
2726
import to.bitkit.ui.theme.Colors
2827
import to.bitkit.ui.utils.withAccent
@@ -33,9 +32,8 @@ fun CriticalUpdateScreen() {
3332

3433
Column(
3534
modifier = Modifier
36-
.fillMaxSize()
35+
.screen()
3736
.padding(horizontal = 32.dp)
38-
.systemBarsPadding()
3937
) {
4038
AppTopBar(
4139
titleText = stringResource(R.string.other__update_critical_nav_title),
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package to.bitkit.ui.settings
2+
3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.padding
5+
import androidx.compose.foundation.lazy.LazyColumn
6+
import androidx.compose.foundation.lazy.items
7+
import androidx.compose.runtime.Composable
8+
import androidx.compose.runtime.LaunchedEffect
9+
import androidx.compose.runtime.getValue
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.tooling.preview.Preview
12+
import androidx.compose.ui.unit.dp
13+
import androidx.hilt.navigation.compose.hiltViewModel
14+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
15+
import to.bitkit.models.Language
16+
import to.bitkit.ui.components.Text13Up
17+
import to.bitkit.ui.components.settings.SettingsButtonRow
18+
import to.bitkit.ui.components.settings.SettingsButtonValue
19+
import to.bitkit.ui.scaffold.AppTopBar
20+
import to.bitkit.ui.scaffold.CloseNavIcon
21+
import to.bitkit.ui.shared.util.screen
22+
import to.bitkit.ui.theme.AppThemeSurface
23+
import to.bitkit.ui.theme.Colors
24+
import to.bitkit.viewmodels.LanguageUiState
25+
import to.bitkit.viewmodels.LanguageViewModel
26+
27+
@Composable
28+
fun LanguageSettingsScreen(
29+
onBackClick: () -> Unit,
30+
onCloseClick: () -> Unit,
31+
modifier: Modifier = Modifier,
32+
viewmodel: LanguageViewModel = hiltViewModel(),
33+
) {
34+
val uiState by viewmodel.uiState.collectAsStateWithLifecycle()
35+
36+
LaunchedEffect(Unit) { viewmodel.fetchLanguageInfo() }
37+
38+
Content(
39+
uiState = uiState,
40+
onCloseClick = onCloseClick,
41+
onBackClick = onBackClick,
42+
onClickLanguage = { selectedLanguage -> viewmodel.selectLanguage(selectedLanguage) },
43+
modifier = modifier,
44+
)
45+
}
46+
47+
@Composable
48+
private fun Content(
49+
uiState: LanguageUiState,
50+
onBackClick: () -> Unit,
51+
onCloseClick: () -> Unit,
52+
onClickLanguage: (Language) -> Unit,
53+
modifier: Modifier = Modifier,
54+
) {
55+
Column(
56+
modifier = modifier.screen()
57+
) {
58+
AppTopBar(
59+
titleText = "Language", // TODO Transifex
60+
onBackClick = onBackClick,
61+
actions = { CloseNavIcon(onClick = onCloseClick) }
62+
)
63+
64+
Column(
65+
modifier = Modifier
66+
.padding(horizontal = 16.dp)
67+
) {
68+
Text13Up("Interface Language", color = Colors.White64, modifier = Modifier.padding(vertical = 16.dp))
69+
70+
LazyColumn {
71+
items(uiState.languages, { item -> item.displayName }) { item ->
72+
SettingsButtonRow(
73+
title = item.displayName,
74+
value = SettingsButtonValue.BooleanValue(item == uiState.selectedLanguage),
75+
onClick = { onClickLanguage(item) }
76+
)
77+
}
78+
}
79+
}
80+
}
81+
}
82+
83+
@Preview
84+
@Composable
85+
private fun Preview() {
86+
AppThemeSurface {
87+
Content(
88+
uiState = LanguageUiState(
89+
selectedLanguage = Language.SPANISH,
90+
languages = Language.entries
91+
),
92+
onBackClick = {},
93+
onCloseClick = {},
94+
onClickLanguage = {},
95+
)
96+
}
97+
}

app/src/main/java/to/bitkit/ui/settings/general/GeneralSettingsScreen.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ import androidx.compose.foundation.layout.padding
55
import androidx.compose.foundation.rememberScrollState
66
import androidx.compose.foundation.verticalScroll
77
import androidx.compose.runtime.Composable
8+
import androidx.compose.runtime.LaunchedEffect
89
import androidx.compose.runtime.getValue
910
import androidx.compose.ui.Modifier
1011
import androidx.compose.ui.platform.testTag
1112
import androidx.compose.ui.res.stringResource
1213
import androidx.compose.ui.tooling.preview.Preview
1314
import androidx.compose.ui.unit.dp
15+
import androidx.hilt.navigation.compose.hiltViewModel
1416
import androidx.lifecycle.compose.collectAsStateWithLifecycle
1517
import androidx.navigation.NavController
1618
import to.bitkit.R
19+
import to.bitkit.models.Language
1720
import to.bitkit.models.PrimaryDisplay
1821
import to.bitkit.models.TransactionSpeed
1922
import to.bitkit.models.transactionSpeedUiText
@@ -22,6 +25,7 @@ import to.bitkit.ui.components.settings.SettingsButtonRow
2225
import to.bitkit.ui.components.settings.SettingsButtonValue
2326
import to.bitkit.ui.navigateToDefaultUnitSettings
2427
import to.bitkit.ui.navigateToHome
28+
import to.bitkit.ui.navigateToLanguageSettings
2529
import to.bitkit.ui.navigateToLocalCurrencySettings
2630
import to.bitkit.ui.navigateToQuickPaySettings
2731
import to.bitkit.ui.navigateToTagsSettings
@@ -32,16 +36,21 @@ import to.bitkit.ui.scaffold.CloseNavIcon
3236
import to.bitkit.ui.scaffold.ScreenColumn
3337
import to.bitkit.ui.settingsViewModel
3438
import to.bitkit.ui.theme.AppThemeSurface
39+
import to.bitkit.viewmodels.LanguageViewModel
3540

3641
@Composable
3742
fun GeneralSettingsScreen(
3843
navController: NavController,
44+
languageViewModel: LanguageViewModel = hiltViewModel(),
3945
) {
4046
val settings = settingsViewModel ?: return
4147
val currencies = LocalCurrencies.current
4248
val defaultTransactionSpeed by settings.defaultTransactionSpeed.collectAsStateWithLifecycle()
4349
val lastUsedTags by settings.lastUsedTags.collectAsStateWithLifecycle()
4450
val quickPayIntroSeen by settings.quickPayIntroSeen.collectAsStateWithLifecycle()
51+
val languageUiState by languageViewModel.uiState.collectAsStateWithLifecycle()
52+
53+
LaunchedEffect(Unit) { languageViewModel.fetchLanguageInfo() }
4554

4655
GeneralSettingsContent(
4756
selectedCurrency = currencies.selectedCurrency,
@@ -56,6 +65,8 @@ fun GeneralSettingsScreen(
5665
onWidgetsClick = { navController.navigateToWidgetsSettings() },
5766
onQuickPayClick = { navController.navigateToQuickPaySettings(quickPayIntroSeen) },
5867
onTagsClick = { navController.navigateToTagsSettings() },
68+
onLanguageSettingsClick = { navController.navigateToLanguageSettings() },
69+
selectedLanguage = languageUiState.selectedLanguage.displayName
5970
)
6071
}
6172

@@ -65,13 +76,15 @@ private fun GeneralSettingsContent(
6576
primaryDisplay: PrimaryDisplay,
6677
defaultTransactionSpeed: TransactionSpeed,
6778
showTagsButton: Boolean = false,
79+
selectedLanguage: String,
6880
onBackClick: () -> Unit = {},
6981
onCloseClick: () -> Unit = {},
7082
onLocalCurrencyClick: () -> Unit = {},
7183
onDefaultUnitClick: () -> Unit = {},
7284
onTransactionSpeedClick: () -> Unit = {},
7385
onWidgetsClick: () -> Unit = {},
7486
onQuickPayClick: () -> Unit = {},
87+
onLanguageSettingsClick: () -> Unit = {},
7588
onTagsClick: () -> Unit = {},
7689
) {
7790
ScreenColumn {
@@ -85,6 +98,12 @@ private fun GeneralSettingsContent(
8598
.padding(horizontal = 16.dp)
8699
.verticalScroll(rememberScrollState())
87100
) {
101+
SettingsButtonRow(
102+
title = "Language",
103+
value = SettingsButtonValue.StringValue(selectedLanguage),
104+
onClick = onLanguageSettingsClick,
105+
modifier = Modifier.testTag("LanguageSettings")
106+
)
88107
SettingsButtonRow(
89108
title = stringResource(R.string.settings__general__currency_local),
90109
value = SettingsButtonValue.StringValue(selectedCurrency),
@@ -138,6 +157,7 @@ private fun Preview() {
138157
primaryDisplay = PrimaryDisplay.BITCOIN,
139158
defaultTransactionSpeed = TransactionSpeed.Medium,
140159
showTagsButton = true,
160+
selectedLanguage = Language.SYSTEM_DEFAULT.displayName
141161
)
142162
}
143163
}

0 commit comments

Comments
 (0)