From 1b4080520ab80b13522c599310fde66d8ff9cd34 Mon Sep 17 00:00:00 2001 From: Infi Date: Thu, 14 Nov 2024 22:57:02 +0100 Subject: [PATCH] feat(settings): language picker Signed-off-by: Infi --- app/src/main/AndroidManifest.xml | 9 + .../chat/revolt/activities/MainActivity.kt | 6 +- .../revolt/internals/extensions/Locales.kt | 27 +++ .../settings/LanguagePickerSettingsScreen.kt | 166 ++++++++++++++++++ .../revolt/screens/settings/SettingsScreen.kt | 19 ++ app/src/main/res/drawable/ic_earth_24dp.xml | 9 + app/src/main/res/values/strings.xml | 3 + 7 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/chat/revolt/internals/extensions/Locales.kt create mode 100644 app/src/main/java/chat/revolt/screens/settings/LanguagePickerSettingsScreen.kt create mode 100644 app/src/main/res/drawable/ic_earth_24dp.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 208963e1..87d4d177 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -147,6 +147,15 @@ android:value="" /> + + + + \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/activities/MainActivity.kt b/app/src/main/java/chat/revolt/activities/MainActivity.kt index efc814b4..a5e8332e 100644 --- a/app/src/main/java/chat/revolt/activities/MainActivity.kt +++ b/app/src/main/java/chat/revolt/activities/MainActivity.kt @@ -9,6 +9,7 @@ import android.util.Log import android.widget.Toast import androidx.activity.compose.setContent import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.core.EaseInOutExpo import androidx.compose.animation.core.FiniteAnimationSpec @@ -30,7 +31,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat -import androidx.fragment.app.FragmentActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.navigation.compose.NavHost @@ -72,6 +72,7 @@ import chat.revolt.screens.settings.ChangelogsSettingsScreen import chat.revolt.screens.settings.ChatSettingsScreen import chat.revolt.screens.settings.DebugSettingsScreen import chat.revolt.screens.settings.ExperimentsSettingsScreen +import chat.revolt.screens.settings.LanguagePickerSettingsScreen import chat.revolt.screens.settings.ProfileSettingsScreen import chat.revolt.screens.settings.SessionSettingsScreen import chat.revolt.screens.settings.SettingsScreen @@ -256,7 +257,7 @@ class MainActivityViewModel @Inject constructor( } @AndroidEntryPoint -class MainActivity : FragmentActivity() { +class MainActivity : AppCompatActivity() { private val viewModel by viewModels() // Fix for SDK >=31, where core-splashscreen accidentally removes dynamic colours @@ -454,6 +455,7 @@ fun AppEntrypoint( composable("settings/debug") { DebugSettingsScreen(navController) } composable("settings/experiments") { ExperimentsSettingsScreen(navController) } composable("settings/changelogs") { ChangelogsSettingsScreen(navController) } + composable("settings/language") { LanguagePickerSettingsScreen(navController) } composable("settings/channel/{channelId}") { backStackEntry -> val channelId = backStackEntry.arguments?.getString("channelId") ?: "" diff --git a/app/src/main/java/chat/revolt/internals/extensions/Locales.kt b/app/src/main/java/chat/revolt/internals/extensions/Locales.kt new file mode 100644 index 00000000..a972e44f --- /dev/null +++ b/app/src/main/java/chat/revolt/internals/extensions/Locales.kt @@ -0,0 +1,27 @@ +package chat.revolt.internals.extensions + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.XmlResourceParser +import java.util.Locale + +@SuppressLint("DiscouragedApi") +fun Context.getSupportedLocales(): List { + val id = resources.getIdentifier( + "_generated_res_locale_config", + "xml", + packageName + ) + val localeXml = resources.getXml(id) + val locales = ArrayList() + var event = localeXml.next() + while (event != XmlResourceParser.END_DOCUMENT) { + if (event == XmlResourceParser.START_TAG && localeXml.name == "locale") { + locales.add(localeXml.getAttributeValue(0)) + } + event = localeXml.next() + } + return locales.map { + Locale.forLanguageTag(it) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/settings/LanguagePickerSettingsScreen.kt b/app/src/main/java/chat/revolt/screens/settings/LanguagePickerSettingsScreen.kt new file mode 100644 index 00000000..75d89dc6 --- /dev/null +++ b/app/src/main/java/chat/revolt/screens/settings/LanguagePickerSettingsScreen.kt @@ -0,0 +1,166 @@ +package chat.revolt.screens.settings + +import android.content.Context +import androidx.appcompat.app.AppCompatDelegate +import androidx.compose.foundation.clickable +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LargeTopAppBar +import androidx.compose.material3.ListItem +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.core.os.LocaleListCompat +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import chat.revolt.R +import chat.revolt.internals.extensions.getSupportedLocales +import java.util.Locale + +class LanguagePickerSettingsScreen : ViewModel() { + val locales = mutableStateListOf() + + var currentLocale by mutableStateOf(null) + + fun initLanguages(context: Context) { + locales.clear() + locales.addAll(context.getSupportedLocales()) + currentLocale = determineCurrentlySelectedLocale() + } + + private fun determineCurrentlySelectedLocale(): Locale? { + return AppCompatDelegate.getApplicationLocales()[0] + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun LanguagePickerSettingsScreen( + navController: NavController, + viewModel: LanguagePickerSettingsScreen = viewModel() +) { + val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() + val context = LocalContext.current + + LaunchedEffect(Unit) { + viewModel.initLanguages(context) + } + + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + LargeTopAppBar( + scrollBehavior = scrollBehavior, + title = { + Text( + stringResource(id = R.string.settings_language), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + navigationIcon = { + navController?.let { + IconButton(onClick = { + navController.popBackStack() + }) { + Icon( + imageVector = Icons.AutoMirrored.Default.ArrowBack, + contentDescription = stringResource(id = R.string.back) + ) + } + } + }, + ) + }, + ) { pv -> + LazyColumn( + contentPadding = pv, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) + ) { + item(key = "auto") { + ListItem( + headlineContent = { + Text( + stringResource(id = R.string.settings_language_auto), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + leadingContent = { + RadioButton( + selected = viewModel.currentLocale == null, + onClick = null + ) + }, + modifier = Modifier.clickable { + AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList()) + viewModel.currentLocale = null + } + ) + } + items( + viewModel.locales.size, + key = { index -> viewModel.locales[index].toLanguageTag() } + ) { index -> + val locale = viewModel.locales[index] + ListItem( + headlineContent = { + Text( + locale.displayLanguage, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + supportingContent = { + if (!locale.displayCountry.isNullOrEmpty()) { + Text( + locale.displayCountry, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + }, + trailingContent = { + Text( + locale.getDisplayLanguage(locale), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + }, + leadingContent = { + RadioButton( + selected = locale.toLanguageTag() == viewModel.currentLocale?.toLanguageTag(), + onClick = null + ) + }, + modifier = Modifier + .clickable { + AppCompatDelegate.setApplicationLocales( + LocaleListCompat.forLanguageTags( + locale.toLanguageTag() + ) + ) + viewModel.currentLocale = locale + } + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt b/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt index f4c5adf7..b0cb1f30 100644 --- a/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt @@ -174,6 +174,25 @@ fun SettingsScreen( } ) + ListItem( + headlineContent = { + Text( + text = stringResource(id = R.string.settings_language) + ) + }, + leadingContent = { + Icon( + painter = painterResource(R.drawable.ic_earth_24dp), + contentDescription = null, + ) + }, + modifier = Modifier + .testTag("settings_view_language") + .clickable { + navController.navigate("settings/language") + } + ) + ListItem( headlineContent = { Text( diff --git a/app/src/main/res/drawable/ic_earth_24dp.xml b/app/src/main/res/drawable/ic_earth_24dp.xml new file mode 100644 index 00000000..e06fdb99 --- /dev/null +++ b/app/src/main/res/drawable/ic_earth_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d1d3fd1d..f594f4bb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -655,6 +655,9 @@ Changelog for %1$s that version + Language + System default + Channel Settings #%1$s Overview