mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-17 12:30:07 +03:00
Merge pull request #1368 from vector-im/feature/switch_language
Feature/switch language
This commit is contained in:
commit
adac80062c
26 changed files with 607 additions and 268 deletions
|
@ -2,13 +2,13 @@ Changes in RiotX 0.21.0 (2020-XX-XX)
|
|||
===================================================
|
||||
|
||||
Features ✨:
|
||||
-
|
||||
- Switch language support (#41)
|
||||
|
||||
Improvements 🙌:
|
||||
- Better connectivity lost indicator when airplane mode is on
|
||||
|
||||
Bugfix 🐛:
|
||||
-
|
||||
- Fix issues with FontScale switch (#69, #645)
|
||||
|
||||
Translations 🗣:
|
||||
-
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.airbnb.mvrx.MvRxViewModelFactory
|
|||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.riotx.core.extensions.exhaustive
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
|
||||
<#if createViewEvents>
|
||||
|
@ -38,7 +39,8 @@ class ${viewModelClass} @AssistedInject constructor(@Assisted initialState: ${vi
|
|||
}
|
||||
|
||||
override fun handle(action: ${actionClass}) {
|
||||
//TODO
|
||||
}
|
||||
when (action) {
|
||||
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,8 +61,6 @@ import im.vector.riotx.features.login.LoginSplashFragment
|
|||
import im.vector.riotx.features.login.LoginWaitForEmailFragment
|
||||
import im.vector.riotx.features.login.LoginWebFragment
|
||||
import im.vector.riotx.features.login.terms.LoginTermsFragment
|
||||
import im.vector.riotx.features.userdirectory.KnownUsersFragment
|
||||
import im.vector.riotx.features.userdirectory.UserDirectoryFragment
|
||||
import im.vector.riotx.features.qrcode.QrCodeScannerFragment
|
||||
import im.vector.riotx.features.reactions.EmojiChooserFragment
|
||||
import im.vector.riotx.features.reactions.EmojiSearchResultFragment
|
||||
|
@ -92,9 +90,12 @@ import im.vector.riotx.features.settings.devtools.IncomingKeyRequestListFragment
|
|||
import im.vector.riotx.features.settings.devtools.KeyRequestsFragment
|
||||
import im.vector.riotx.features.settings.devtools.OutgoingKeyRequestListFragment
|
||||
import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
|
||||
import im.vector.riotx.features.settings.locale.LocalePickerFragment
|
||||
import im.vector.riotx.features.settings.push.PushGatewaysFragment
|
||||
import im.vector.riotx.features.share.IncomingShareFragment
|
||||
import im.vector.riotx.features.signout.soft.SoftLogoutFragment
|
||||
import im.vector.riotx.features.userdirectory.KnownUsersFragment
|
||||
import im.vector.riotx.features.userdirectory.UserDirectoryFragment
|
||||
|
||||
@Module
|
||||
interface FragmentModule {
|
||||
|
@ -109,6 +110,11 @@ interface FragmentModule {
|
|||
@FragmentKey(RoomListFragment::class)
|
||||
fun bindRoomListFragment(fragment: RoomListFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(LocalePickerFragment::class)
|
||||
fun bindLocalePickerFragment(fragment: LocalePickerFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(GroupListFragment::class)
|
||||
|
|
|
@ -130,9 +130,9 @@ interface VectorComponent {
|
|||
|
||||
fun emojiDataSource(): EmojiDataSource
|
||||
|
||||
fun alertManager() : PopupAlertManager
|
||||
fun alertManager(): PopupAlertManager
|
||||
|
||||
fun reAuthHelper() : ReAuthHelper
|
||||
fun reAuthHelper(): ReAuthHelper
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.riotx.core.extensions
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Parcelable
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
|
@ -59,3 +60,8 @@ fun <T : Fragment> VectorBaseActivity.addFragmentToBackstack(frameId: Int,
|
|||
fun VectorBaseActivity.hideKeyboard() {
|
||||
currentFocus?.hideKeyboard()
|
||||
}
|
||||
|
||||
fun Activity.restart() {
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
|
|||
}
|
||||
})
|
||||
|
||||
sessionListener = getVectorComponent().sessionListener()
|
||||
sessionListener = vectorComponent.sessionListener()
|
||||
sessionListener.globalErrorLiveData.observeEvent(this) {
|
||||
handleGlobalError(it)
|
||||
}
|
||||
|
|
|
@ -33,9 +33,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.features.notifications.NotificationUtils
|
||||
import im.vector.riotx.features.settings.VectorLocale
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Tells if the application ignores battery optimizations.
|
||||
|
@ -94,24 +91,6 @@ fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = t
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the device locale
|
||||
*
|
||||
* @return the device locale
|
||||
*/
|
||||
fun getDeviceLocale(context: Context): Locale {
|
||||
return try {
|
||||
val packageManager = context.packageManager
|
||||
val resources = packageManager.getResourcesForApplication("android")
|
||||
@Suppress("DEPRECATION")
|
||||
resources.configuration.locale
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## getDeviceLocale() failed")
|
||||
// Fallback to application locale
|
||||
VectorLocale.applicationLocale
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows notification settings for the current app.
|
||||
* In android O will directly opens the notification settings, in lower version it will show the App settings
|
||||
|
|
|
@ -30,62 +30,30 @@ import javax.inject.Inject
|
|||
/**
|
||||
* Handle locale configuration change, such as theme, font size and locale chosen by the user
|
||||
*/
|
||||
|
||||
class VectorConfiguration @Inject constructor(private val context: Context) {
|
||||
|
||||
// TODO Import mLanguageReceiver From Riot?
|
||||
fun onConfigurationChanged() {
|
||||
if (Locale.getDefault().toString() != VectorLocale.applicationLocale.toString()) {
|
||||
Timber.v("## onConfigurationChanged(): the locale has been updated to ${Locale.getDefault()}")
|
||||
Timber.v("## onConfigurationChanged(): restore the expected value ${VectorLocale.applicationLocale}")
|
||||
updateApplicationSettings(VectorLocale.applicationLocale,
|
||||
FontScale.getFontScalePrefValue(context),
|
||||
ThemeUtils.getApplicationTheme(context))
|
||||
Locale.setDefault(VectorLocale.applicationLocale)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateApplicationSettings(locale: Locale, textSize: String, theme: String) {
|
||||
VectorLocale.saveApplicationLocale(context, locale)
|
||||
FontScale.saveFontScale(context, textSize)
|
||||
Locale.setDefault(locale)
|
||||
|
||||
val config = Configuration(context.resources.configuration)
|
||||
@Suppress("DEPRECATION")
|
||||
config.locale = locale
|
||||
config.fontScale = FontScale.getFontScale(context)
|
||||
@Suppress("DEPRECATION")
|
||||
context.resources.updateConfiguration(config, context.resources.displayMetrics)
|
||||
|
||||
ThemeUtils.setApplicationTheme(context, theme)
|
||||
// TODO PhoneNumberUtils.onLocaleUpdate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the application theme
|
||||
*
|
||||
* @param theme the new theme
|
||||
*/
|
||||
fun updateApplicationTheme(theme: String) {
|
||||
ThemeUtils.setApplicationTheme(context, theme)
|
||||
updateApplicationSettings(VectorLocale.applicationLocale,
|
||||
FontScale.getFontScalePrefValue(context),
|
||||
theme)
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the configuration from the saved one
|
||||
*/
|
||||
fun initConfiguration() {
|
||||
VectorLocale.init(context)
|
||||
val locale = VectorLocale.applicationLocale
|
||||
val fontScale = FontScale.getFontScale(context)
|
||||
val fontScale = FontScale.getFontScaleValue(context)
|
||||
val theme = ThemeUtils.getApplicationTheme(context)
|
||||
|
||||
Locale.setDefault(locale)
|
||||
val config = Configuration(context.resources.configuration)
|
||||
@Suppress("DEPRECATION")
|
||||
config.locale = locale
|
||||
config.fontScale = fontScale
|
||||
config.fontScale = fontScale.scale
|
||||
@Suppress("DEPRECATION")
|
||||
context.resources.updateConfiguration(config, context.resources.displayMetrics)
|
||||
|
||||
|
@ -93,16 +61,6 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
|
|||
ThemeUtils.setApplicationTheme(context, theme)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the application locale
|
||||
*
|
||||
* @param locale
|
||||
*/
|
||||
// TODO Call from LanguagePickerActivity
|
||||
fun updateApplicationLocale(locale: Locale) {
|
||||
updateApplicationSettings(locale, FontScale.getFontScalePrefValue(context), ThemeUtils.getApplicationTheme(context))
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a localised context
|
||||
*
|
||||
|
@ -115,7 +73,7 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
|
|||
val resources = context.resources
|
||||
val locale = VectorLocale.applicationLocale
|
||||
val configuration = resources.configuration
|
||||
configuration.fontScale = FontScale.getFontScale(context)
|
||||
configuration.fontScale = FontScale.getFontScaleValue(context).scale
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
configuration.setLocale(locale)
|
||||
|
@ -142,10 +100,9 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
|
|||
* Compute the locale status value
|
||||
* @return the local status value
|
||||
*/
|
||||
// TODO Create data class for this
|
||||
fun getHash(): String {
|
||||
return (VectorLocale.applicationLocale.toString()
|
||||
+ "_" + FontScale.getFontScalePrefValue(context)
|
||||
+ "_" + FontScale.getFontScaleValue(context).preferenceValue
|
||||
+ "_" + ThemeUtils.getApplicationTheme(context))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,16 +164,16 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment()
|
|||
@OnClick(R.id.keys_backup_setup_step2_button)
|
||||
fun doNext() {
|
||||
when {
|
||||
viewModel.passphrase.value.isNullOrEmpty() -> {
|
||||
viewModel.passphrase.value.isNullOrEmpty() -> {
|
||||
viewModel.passphraseError.value = context?.getString(R.string.passphrase_empty_error_message)
|
||||
}
|
||||
viewModel.passphrase.value != viewModel.confirmPassphrase.value -> {
|
||||
viewModel.confirmPassphraseError.value = context?.getString(R.string.passphrase_passphrase_does_not_match)
|
||||
}
|
||||
viewModel.passwordStrength.value?.score ?: 0 < 4 -> {
|
||||
viewModel.passwordStrength.value?.score ?: 0 < 4 -> {
|
||||
viewModel.passphraseError.value = context?.getString(R.string.passphrase_passphrase_too_weak)
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
viewModel.megolmBackupCreationInfo = null
|
||||
|
||||
viewModel.prepareRecoveryKey(activity!!, viewModel.passphrase.value)
|
||||
|
@ -190,7 +190,7 @@ class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment()
|
|||
|
||||
viewModel.prepareRecoveryKey(activity!!, null)
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
// User has entered a passphrase but want to skip this step.
|
||||
viewModel.passphraseError.value = context?.getString(R.string.keys_backup_passphrase_not_empty_error_message)
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@ import im.vector.riotx.BuildConfig
|
|||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||
import im.vector.riotx.core.extensions.toOnOff
|
||||
import im.vector.riotx.core.utils.getDeviceLocale
|
||||
import im.vector.riotx.features.settings.VectorLocale
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import im.vector.riotx.features.settings.locale.SystemLocaleProvider
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import im.vector.riotx.features.version.VersionProvider
|
||||
import okhttp3.Call
|
||||
|
@ -58,10 +58,13 @@ import javax.inject.Singleton
|
|||
* BugReporter creates and sends the bug reports.
|
||||
*/
|
||||
@Singleton
|
||||
class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val versionProvider: VersionProvider,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val vectorFileLogger: VectorFileLogger) {
|
||||
class BugReporter @Inject constructor(
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val versionProvider: VersionProvider,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val vectorFileLogger: VectorFileLogger,
|
||||
private val systemLocaleProvider: SystemLocaleProvider
|
||||
) {
|
||||
var inMultiWindowMode = false
|
||||
|
||||
companion object {
|
||||
|
@ -240,7 +243,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
|||
+ Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME)
|
||||
.addFormDataPart("locale", Locale.getDefault().toString())
|
||||
.addFormDataPart("app_language", VectorLocale.applicationLocale.toString())
|
||||
.addFormDataPart("default_app_language", getDeviceLocale(context).toString())
|
||||
.addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString())
|
||||
.addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
|
||||
|
||||
val buildNumber = context.getString(R.string.build_number)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package im.vector.riotx.features.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import im.vector.riotx.R
|
||||
|
@ -29,124 +29,59 @@ object FontScale {
|
|||
// Key for the SharedPrefs
|
||||
private const val APPLICATION_FONT_SCALE_KEY = "APPLICATION_FONT_SCALE_KEY"
|
||||
|
||||
// Possible values for the SharedPrefs
|
||||
private const val FONT_SCALE_TINY = "FONT_SCALE_TINY"
|
||||
private const val FONT_SCALE_SMALL = "FONT_SCALE_SMALL"
|
||||
private const val FONT_SCALE_NORMAL = "FONT_SCALE_NORMAL"
|
||||
private const val FONT_SCALE_LARGE = "FONT_SCALE_LARGE"
|
||||
private const val FONT_SCALE_LARGER = "FONT_SCALE_LARGER"
|
||||
private const val FONT_SCALE_LARGEST = "FONT_SCALE_LARGEST"
|
||||
private const val FONT_SCALE_HUGE = "FONT_SCALE_HUGE"
|
||||
|
||||
private val fontScaleToPrefValue = mapOf(
|
||||
0.70f to FONT_SCALE_TINY,
|
||||
0.85f to FONT_SCALE_SMALL,
|
||||
1.00f to FONT_SCALE_NORMAL,
|
||||
1.15f to FONT_SCALE_LARGE,
|
||||
1.30f to FONT_SCALE_LARGER,
|
||||
1.45f to FONT_SCALE_LARGEST,
|
||||
1.60f to FONT_SCALE_HUGE
|
||||
data class FontScaleValue(
|
||||
val index: Int,
|
||||
// Possible values for the SharedPrefs
|
||||
val preferenceValue: String,
|
||||
val scale: Float,
|
||||
@StringRes
|
||||
val nameResId: Int
|
||||
)
|
||||
|
||||
private val prefValueToNameResId = mapOf(
|
||||
FONT_SCALE_TINY to R.string.tiny,
|
||||
FONT_SCALE_SMALL to R.string.small,
|
||||
FONT_SCALE_NORMAL to R.string.normal,
|
||||
FONT_SCALE_LARGE to R.string.large,
|
||||
FONT_SCALE_LARGER to R.string.larger,
|
||||
FONT_SCALE_LARGEST to R.string.largest,
|
||||
FONT_SCALE_HUGE to R.string.huge
|
||||
private val fontScaleValues = listOf(
|
||||
FontScaleValue(0, "FONT_SCALE_TINY", 0.70f, R.string.tiny),
|
||||
FontScaleValue(1, "FONT_SCALE_SMALL", 0.85f, R.string.small),
|
||||
FontScaleValue(2, "FONT_SCALE_NORMAL", 1.00f, R.string.normal),
|
||||
FontScaleValue(3, "FONT_SCALE_LARGE", 1.15f, R.string.large),
|
||||
FontScaleValue(4, "FONT_SCALE_LARGER", 1.30f, R.string.larger),
|
||||
FontScaleValue(5, "FONT_SCALE_LARGEST", 1.45f, R.string.largest),
|
||||
FontScaleValue(6, "FONT_SCALE_HUGE", 1.60f, R.string.huge)
|
||||
)
|
||||
|
||||
private val normalFontScaleValue = fontScaleValues[2]
|
||||
|
||||
/**
|
||||
* Get the font scale value from SharedPrefs. Init the SharedPrefs if necessary
|
||||
*
|
||||
* @return the font scale
|
||||
* @return the font scale value
|
||||
*/
|
||||
fun getFontScalePrefValue(context: Context): String {
|
||||
fun getFontScaleValue(context: Context): FontScaleValue {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
var scalePreferenceValue: String
|
||||
|
||||
if (APPLICATION_FONT_SCALE_KEY !in preferences) {
|
||||
return if (APPLICATION_FONT_SCALE_KEY !in preferences) {
|
||||
val fontScale = context.resources.configuration.fontScale
|
||||
|
||||
scalePreferenceValue = FONT_SCALE_NORMAL
|
||||
|
||||
if (fontScaleToPrefValue.containsKey(fontScale)) {
|
||||
scalePreferenceValue = fontScaleToPrefValue[fontScale] as String
|
||||
}
|
||||
|
||||
preferences.edit {
|
||||
putString(APPLICATION_FONT_SCALE_KEY, scalePreferenceValue)
|
||||
}
|
||||
(fontScaleValues.firstOrNull { it.scale == fontScale } ?: normalFontScaleValue)
|
||||
.also { preferences.edit { putString(APPLICATION_FONT_SCALE_KEY, it.preferenceValue) } }
|
||||
} else {
|
||||
scalePreferenceValue = preferences.getString(APPLICATION_FONT_SCALE_KEY, FONT_SCALE_NORMAL)!!
|
||||
val pref = preferences.getString(APPLICATION_FONT_SCALE_KEY, null)
|
||||
fontScaleValues.firstOrNull { it.preferenceValue == pref } ?: normalFontScaleValue
|
||||
}
|
||||
}
|
||||
|
||||
return scalePreferenceValue
|
||||
fun updateFontScale(context: Context, index: Int) {
|
||||
fontScaleValues.getOrNull(index)?.let {
|
||||
saveFontScaleValue(context, it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the font scale value
|
||||
* Store the font scale vale
|
||||
*
|
||||
* @return the font scale
|
||||
* @param fontScaleValue the font scale value to store
|
||||
*/
|
||||
fun getFontScale(context: Context): Float {
|
||||
val fontScale = getFontScalePrefValue(context)
|
||||
|
||||
if (fontScaleToPrefValue.containsValue(fontScale)) {
|
||||
for ((key, value) in fontScaleToPrefValue) {
|
||||
if (value == fontScale) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the font scale description
|
||||
*
|
||||
* @return the font description
|
||||
*/
|
||||
fun getFontScaleDescription(context: Context): String {
|
||||
val fontScale = getFontScalePrefValue(context)
|
||||
|
||||
return if (prefValueToNameResId.containsKey(fontScale)) {
|
||||
context.getString(prefValueToNameResId[fontScale] as Int)
|
||||
} else context.getString(R.string.normal)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the font size from the locale description.
|
||||
*
|
||||
* @param fontScaleDescription the font scale description
|
||||
*/
|
||||
fun updateFontScale(context: Context, fontScaleDescription: String) {
|
||||
for ((key, value) in prefValueToNameResId) {
|
||||
if (context.getString(value) == fontScaleDescription) {
|
||||
saveFontScale(context, key)
|
||||
}
|
||||
}
|
||||
|
||||
val config = Configuration(context.resources.configuration)
|
||||
config.fontScale = getFontScale(context)
|
||||
@Suppress("DEPRECATION")
|
||||
context.resources.updateConfiguration(config, context.resources.displayMetrics)
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the new font scale
|
||||
*
|
||||
* @param scaleValue the text scale
|
||||
*/
|
||||
fun saveFontScale(context: Context, scaleValue: String) {
|
||||
if (scaleValue.isNotEmpty()) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit {
|
||||
putString(APPLICATION_FONT_SCALE_KEY, scaleValue)
|
||||
}
|
||||
}
|
||||
private fun saveFontScaleValue(context: Context, fontScaleValue: FontScaleValue) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit { putString(APPLICATION_FONT_SCALE_KEY, fontScaleValue.preferenceValue) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,11 @@ package im.vector.riotx.features.settings
|
|||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.core.content.edit
|
||||
import im.vector.riotx.BuildConfig
|
||||
import androidx.preference.PreferenceManager
|
||||
import im.vector.riotx.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -41,10 +39,9 @@ object VectorLocale {
|
|||
private val defaultLocale = Locale("en", "US")
|
||||
|
||||
/**
|
||||
* The supported application languages
|
||||
* The cache of supported application languages
|
||||
*/
|
||||
var supportedLocales = ArrayList<Locale>()
|
||||
private set
|
||||
private val supportedLocales = mutableListOf<Locale>()
|
||||
|
||||
/**
|
||||
* Provides the current application locale
|
||||
|
@ -52,10 +49,13 @@ object VectorLocale {
|
|||
var applicationLocale = defaultLocale
|
||||
private set
|
||||
|
||||
lateinit var context: Context
|
||||
|
||||
/**
|
||||
* Init this object
|
||||
*/
|
||||
fun init(context: Context) {
|
||||
this.context = context
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) {
|
||||
|
@ -72,19 +72,14 @@ object VectorLocale {
|
|||
applicationLocale = defaultLocale
|
||||
}
|
||||
|
||||
saveApplicationLocale(context, applicationLocale)
|
||||
}
|
||||
|
||||
// init the known locales in background, using kotlin coroutines
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
initApplicationLocales(context)
|
||||
saveApplicationLocale(applicationLocale)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the new application locale.
|
||||
*/
|
||||
fun saveApplicationLocale(context: Context, locale: Locale) {
|
||||
fun saveApplicationLocale(locale: Locale) {
|
||||
applicationLocale = locale
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit {
|
||||
|
@ -144,6 +139,7 @@ object VectorLocale {
|
|||
} else {
|
||||
val resources = context.resources
|
||||
val conf = resources.configuration
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val savedLocale = conf.locale
|
||||
@Suppress("DEPRECATION")
|
||||
|
@ -165,11 +161,9 @@ object VectorLocale {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provides the supported application locales list
|
||||
*
|
||||
* @param context the context
|
||||
* Init the supported application locales list
|
||||
*/
|
||||
private fun initApplicationLocales(context: Context) {
|
||||
private fun initApplicationLocales() {
|
||||
val knownLocalesSet = HashSet<Triple<String, String, String>>()
|
||||
|
||||
try {
|
||||
|
@ -195,9 +189,7 @@ object VectorLocale {
|
|||
)
|
||||
}
|
||||
|
||||
supportedLocales.clear()
|
||||
|
||||
knownLocalesSet.mapTo(supportedLocales) { (language, country, script) ->
|
||||
val list = knownLocalesSet.map { (language, country, script) ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Locale.Builder()
|
||||
.setLanguage(language)
|
||||
|
@ -208,9 +200,11 @@ object VectorLocale {
|
|||
Locale(language, country)
|
||||
}
|
||||
}
|
||||
// sort by human display names
|
||||
.sortedBy { localeToLocalisedString(it).toLowerCase(it) }
|
||||
|
||||
// sort by human display names
|
||||
supportedLocales.sortWith(Comparator { lhs, rhs -> localeToLocalisedString(lhs).compareTo(localeToLocalisedString(rhs)) })
|
||||
supportedLocales.clear()
|
||||
supportedLocales.addAll(list)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,22 +229,39 @@ object VectorLocale {
|
|||
append(locale.getDisplayCountry(locale))
|
||||
append(")")
|
||||
}
|
||||
|
||||
// In debug mode, also display information about the locale in the current locale.
|
||||
if (BuildConfig.DEBUG) {
|
||||
append("\n[")
|
||||
append(locale.displayLanguage)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && locale.script != "Latn") {
|
||||
append(" - ")
|
||||
append(locale.displayScript)
|
||||
}
|
||||
if (locale.displayCountry.isNotEmpty()) {
|
||||
append(" (")
|
||||
append(locale.displayCountry)
|
||||
append(")")
|
||||
}
|
||||
append("]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about the locale in the current locale
|
||||
*
|
||||
* @param locale the locale to get info from
|
||||
* @return the string
|
||||
*/
|
||||
fun localeToLocalisedStringInfo(locale: Locale): String {
|
||||
return buildString {
|
||||
append("[")
|
||||
append(locale.displayLanguage)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && locale.script != "Latn") {
|
||||
append(" - ")
|
||||
append(locale.displayScript)
|
||||
}
|
||||
if (locale.displayCountry.isNotEmpty()) {
|
||||
append(" (")
|
||||
append(locale.displayCountry)
|
||||
append(")")
|
||||
}
|
||||
append("]")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getSupportedLocales(): List<Locale> {
|
||||
if (supportedLocales.isEmpty()) {
|
||||
// init the known locales in background
|
||||
withContext(Dispatchers.IO) {
|
||||
initApplicationLocales()
|
||||
}
|
||||
}
|
||||
return supportedLocales
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ package im.vector.riotx.features.settings
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.CheckedTextView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreference
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.restart
|
||||
import im.vector.riotx.core.preference.VectorListPreference
|
||||
import im.vector.riotx.core.preference.VectorPreference
|
||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
||||
|
@ -54,13 +54,9 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
|||
findPreference<VectorListPreference>(ThemeUtils.APPLICATION_THEME_KEY)!!
|
||||
.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
if (newValue is String) {
|
||||
vectorConfiguration.updateApplicationTheme(newValue)
|
||||
ThemeUtils.setApplicationTheme(requireContext(), newValue)
|
||||
// Restart the Activity
|
||||
activity?.let {
|
||||
// Note: recreate does not apply the color correctly
|
||||
it.startActivity(it.intent)
|
||||
it.finish()
|
||||
}
|
||||
activity?.restart()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -129,21 +125,6 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
REQUEST_LOCALE -> {
|
||||
activity?.let {
|
||||
startActivity(it.intent)
|
||||
it.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================================================================================================
|
||||
// user interface management
|
||||
// ==============================================================================================================
|
||||
|
@ -152,14 +133,8 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
|||
// Selected language
|
||||
selectedLanguagePreference.summary = VectorLocale.localeToLocalisedString(VectorLocale.applicationLocale)
|
||||
|
||||
selectedLanguagePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
notImplemented()
|
||||
// TODO startActivityForResult(LanguagePickerActivity.getIntent(activity), REQUEST_LOCALE)
|
||||
true
|
||||
}
|
||||
|
||||
// Text size
|
||||
textSizePreference.summary = FontScale.getFontScaleDescription(activity!!)
|
||||
textSizePreference.summary = getString(FontScale.getFontScaleValue(activity!!).nameResId)
|
||||
|
||||
textSizePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
activity?.let { displayTextSizeSelection(it) }
|
||||
|
@ -182,25 +157,20 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
|||
|
||||
val childCount = linearLayout.childCount
|
||||
|
||||
val scaleText = FontScale.getFontScaleDescription(activity)
|
||||
val index = FontScale.getFontScaleValue(activity).index
|
||||
|
||||
for (i in 0 until childCount) {
|
||||
val v = linearLayout.getChildAt(i)
|
||||
|
||||
if (v is CheckedTextView) {
|
||||
v.isChecked = v.text == scaleText
|
||||
v.isChecked = i == index
|
||||
|
||||
v.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
FontScale.updateFontScale(activity, v.text.toString())
|
||||
activity.startActivity(activity.intent)
|
||||
activity.finish()
|
||||
FontScale.updateFontScale(activity, i)
|
||||
activity.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val REQUEST_LOCALE = 777
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.locale
|
||||
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.ClickListener
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotx.core.epoxy.onClick
|
||||
import im.vector.riotx.core.extensions.setTextOrHide
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_locale)
|
||||
abstract class LocaleItem : VectorEpoxyModel<LocaleItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute var title: String? = null
|
||||
@EpoxyAttribute var subtitle: String? = null
|
||||
@EpoxyAttribute var clickListener: ClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
|
||||
holder.view.onClick { clickListener?.invoke() }
|
||||
holder.titleView.setTextOrHide(title)
|
||||
holder.subtitleView.setTextOrHide(subtitle)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val titleView by bind<TextView>(R.id.localeTitle)
|
||||
val subtitleView by bind<TextView>(R.id.localeSubtitle)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.locale
|
||||
|
||||
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||
import java.util.Locale
|
||||
|
||||
sealed class LocalePickerAction : VectorViewModelAction {
|
||||
data class SelectLocale(val locale: Locale) : LocalePickerAction()
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.locale
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Incomplete
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.loadingItem
|
||||
import im.vector.riotx.core.epoxy.noResultItem
|
||||
import im.vector.riotx.core.epoxy.profiles.profileSectionItem
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.settings.VectorLocale
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class LocalePickerController @Inject constructor(
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val stringProvider: StringProvider
|
||||
) : TypedEpoxyController<LocalePickerViewState>() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
override fun buildModels(data: LocalePickerViewState?) {
|
||||
val list = data?.locales ?: return
|
||||
|
||||
profileSectionItem {
|
||||
id("currentTitle")
|
||||
title(stringProvider.getString(R.string.choose_locale_current_locale_title))
|
||||
}
|
||||
localeItem {
|
||||
id(data.currentLocale.toString())
|
||||
title(VectorLocale.localeToLocalisedString(data.currentLocale).capitalize(data.currentLocale))
|
||||
if (vectorPreferences.developerMode()) {
|
||||
subtitle(VectorLocale.localeToLocalisedStringInfo(data.currentLocale))
|
||||
}
|
||||
clickListener { listener?.onUseCurrentClicked() }
|
||||
}
|
||||
profileSectionItem {
|
||||
id("otherTitle")
|
||||
title(stringProvider.getString(R.string.choose_locale_other_locales_title))
|
||||
}
|
||||
when (list) {
|
||||
is Incomplete -> {
|
||||
loadingItem {
|
||||
id("loading")
|
||||
loadingText(stringProvider.getString(R.string.choose_locale_loading_locales))
|
||||
}
|
||||
}
|
||||
is Success ->
|
||||
if (list().isEmpty()) {
|
||||
noResultItem {
|
||||
id("noResult")
|
||||
text(stringProvider.getString(R.string.no_result_placeholder))
|
||||
}
|
||||
} else {
|
||||
list()
|
||||
.filter { it.toString() != data.currentLocale.toString() }
|
||||
.forEach {
|
||||
localeItem {
|
||||
id(it.toString())
|
||||
title(VectorLocale.localeToLocalisedString(it).capitalize(it))
|
||||
if (vectorPreferences.developerMode()) {
|
||||
subtitle(VectorLocale.localeToLocalisedStringInfo(it))
|
||||
}
|
||||
clickListener { listener?.onLocaleClicked(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onUseCurrentClicked()
|
||||
fun onLocaleClicked(locale: Locale)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.locale
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.extensions.exhaustive
|
||||
import im.vector.riotx.core.extensions.restart
|
||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import kotlinx.android.synthetic.main.fragment_locale_picker.*
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class LocalePickerFragment @Inject constructor(
|
||||
private val viewModelFactory: LocalePickerViewModel.Factory,
|
||||
private val controller: LocalePickerController
|
||||
) : VectorBaseFragment(), LocalePickerViewModel.Factory by viewModelFactory, LocalePickerController.Listener {
|
||||
|
||||
private val viewModel: LocalePickerViewModel by fragmentViewModel()
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_locale_picker
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
localeRecyclerView.configureWith(controller)
|
||||
controller.listener = this
|
||||
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
LocalePickerViewEvents.RestartActivity -> {
|
||||
activity?.restart()
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
localeRecyclerView.cleanup()
|
||||
controller.listener = null
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
controller.setData(state)
|
||||
}
|
||||
|
||||
override fun onUseCurrentClicked() {
|
||||
// Just close the fragment
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
|
||||
override fun onLocaleClicked(locale: Locale) {
|
||||
viewModel.handle(LocalePickerAction.SelectLocale(locale))
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_select_language)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.locale
|
||||
|
||||
import im.vector.riotx.core.platform.VectorViewEvents
|
||||
|
||||
sealed class LocalePickerViewEvents : VectorViewEvents {
|
||||
object RestartActivity : LocalePickerViewEvents()
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.locale
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.riotx.core.extensions.exhaustive
|
||||
import im.vector.riotx.core.platform.VectorViewModel
|
||||
import im.vector.riotx.features.settings.VectorLocale
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class LocalePickerViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: LocalePickerViewState
|
||||
) : VectorViewModel<LocalePickerViewState, LocalePickerAction, LocalePickerViewEvents>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: LocalePickerViewState): LocalePickerViewModel
|
||||
}
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val result = VectorLocale.getSupportedLocales()
|
||||
|
||||
setState {
|
||||
copy(
|
||||
locales = Success(result)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<LocalePickerViewModel, LocalePickerViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: LocalePickerViewState): LocalePickerViewModel? {
|
||||
val factory = when (viewModelContext) {
|
||||
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
|
||||
is ActivityViewModelContext -> viewModelContext.activity as? Factory
|
||||
}
|
||||
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: LocalePickerAction) {
|
||||
when (action) {
|
||||
is LocalePickerAction.SelectLocale -> handleSelectLocale(action)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleSelectLocale(action: LocalePickerAction.SelectLocale) {
|
||||
VectorLocale.saveApplicationLocale(action.locale)
|
||||
_viewEvents.post(LocalePickerViewEvents.RestartActivity)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.locale
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.riotx.features.settings.VectorLocale
|
||||
import java.util.Locale
|
||||
|
||||
data class LocalePickerViewState(
|
||||
val currentLocale: Locale = VectorLocale.applicationLocale,
|
||||
val locales: Async<List<Locale>> = Uninitialized
|
||||
) : MvRxState
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.settings.locale
|
||||
|
||||
import android.content.Context
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class SystemLocaleProvider @Inject constructor(
|
||||
private val context: Context
|
||||
) {
|
||||
|
||||
/**
|
||||
* Provides the device locale
|
||||
*
|
||||
* @return the device locale, or null in case of error
|
||||
*/
|
||||
fun getSystemLocale(): Locale? {
|
||||
return try {
|
||||
val packageManager = context.packageManager
|
||||
val resources = packageManager.getResourcesForApplication("android")
|
||||
@Suppress("DEPRECATION")
|
||||
resources.configuration.locale
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## getDeviceLocale() failed")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@ object ThemeUtils {
|
|||
*/
|
||||
fun getApplicationTheme(context: Context): String {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE)!!
|
||||
.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
8
vector/src/main/res/layout/fragment_locale_picker.xml
Normal file
8
vector/src/main/res/layout/fragment_locale_picker.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/localeRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?riotx_background"
|
||||
tools:listitem="@layout/item_locale" />
|
41
vector/src/main/res/layout/item_locale.xml
Normal file
41
vector/src/main/res/layout/item_locale.xml
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:minHeight="64dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/localeTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/layout_horizontal_margin"
|
||||
android:paddingEnd="@dimen/layout_horizontal_margin"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/localeSubtitle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="English" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/localeSubtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/layout_horizontal_margin"
|
||||
android:paddingEnd="@dimen/layout_horizontal_margin"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="14sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/localeTitle"
|
||||
tools:text="details"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -2393,4 +2393,8 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
|
|||
</plurals>
|
||||
<string name="invite_users_to_room_failure">We could not invite users. Please check the users you want to invite and try again.</string>
|
||||
|
||||
<string name="choose_locale_current_locale_title">Current language</string>
|
||||
<string name="choose_locale_other_locales_title">Other available languages</string>
|
||||
<string name="choose_locale_loading_locales">Loading available languages…</string>
|
||||
|
||||
</resources>
|
|
@ -7,9 +7,10 @@
|
|||
android:title="@string/settings_user_interface">
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreference
|
||||
android:dialogTitle="@string/settings_select_language"
|
||||
android:key="SETTINGS_INTERFACE_LANGUAGE_PREFERENCE_KEY"
|
||||
android:title="@string/settings_interface_language" />
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_interface_language"
|
||||
app:fragment="im.vector.riotx.features.settings.locale.LocalePickerFragment" />
|
||||
|
||||
<im.vector.riotx.core.preference.VectorListPreference
|
||||
android:defaultValue="light"
|
||||
|
@ -23,6 +24,7 @@
|
|||
<im.vector.riotx.core.preference.VectorPreference
|
||||
android:dialogTitle="@string/font_size"
|
||||
android:key="SETTINGS_INTERFACE_TEXT_SIZE_KEY"
|
||||
android:persistent="false"
|
||||
android:title="@string/font_size" />
|
||||
|
||||
</im.vector.riotx.core.preference.VectorPreferenceCategory>
|
||||
|
|
Loading…
Add table
Reference in a new issue