Merge pull request #1368 from vector-im/feature/switch_language
Feature/switch language
This commit is contained in:
commit
adac80062c
|
@ -2,13 +2,13 @@ Changes in RiotX 0.21.0 (2020-XX-XX)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
Features ✨:
|
Features ✨:
|
||||||
-
|
- Switch language support (#41)
|
||||||
|
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
- Better connectivity lost indicator when airplane mode is on
|
- Better connectivity lost indicator when airplane mode is on
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
-
|
- Fix issues with FontScale switch (#69, #645)
|
||||||
|
|
||||||
Translations 🗣:
|
Translations 🗣:
|
||||||
-
|
-
|
||||||
|
|
|
@ -6,6 +6,7 @@ import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
|
import im.vector.riotx.core.extensions.exhaustive
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
|
||||||
<#if createViewEvents>
|
<#if createViewEvents>
|
||||||
|
@ -38,7 +39,8 @@ class ${viewModelClass} @AssistedInject constructor(@Assisted initialState: ${vi
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(action: ${actionClass}) {
|
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.LoginWaitForEmailFragment
|
||||||
import im.vector.riotx.features.login.LoginWebFragment
|
import im.vector.riotx.features.login.LoginWebFragment
|
||||||
import im.vector.riotx.features.login.terms.LoginTermsFragment
|
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.qrcode.QrCodeScannerFragment
|
||||||
import im.vector.riotx.features.reactions.EmojiChooserFragment
|
import im.vector.riotx.features.reactions.EmojiChooserFragment
|
||||||
import im.vector.riotx.features.reactions.EmojiSearchResultFragment
|
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.KeyRequestsFragment
|
||||||
import im.vector.riotx.features.settings.devtools.OutgoingKeyRequestListFragment
|
import im.vector.riotx.features.settings.devtools.OutgoingKeyRequestListFragment
|
||||||
import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
|
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.settings.push.PushGatewaysFragment
|
||||||
import im.vector.riotx.features.share.IncomingShareFragment
|
import im.vector.riotx.features.share.IncomingShareFragment
|
||||||
import im.vector.riotx.features.signout.soft.SoftLogoutFragment
|
import im.vector.riotx.features.signout.soft.SoftLogoutFragment
|
||||||
|
import im.vector.riotx.features.userdirectory.KnownUsersFragment
|
||||||
|
import im.vector.riotx.features.userdirectory.UserDirectoryFragment
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
interface FragmentModule {
|
interface FragmentModule {
|
||||||
|
@ -109,6 +110,11 @@ interface FragmentModule {
|
||||||
@FragmentKey(RoomListFragment::class)
|
@FragmentKey(RoomListFragment::class)
|
||||||
fun bindRoomListFragment(fragment: RoomListFragment): Fragment
|
fun bindRoomListFragment(fragment: RoomListFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(LocalePickerFragment::class)
|
||||||
|
fun bindLocalePickerFragment(fragment: LocalePickerFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(GroupListFragment::class)
|
@FragmentKey(GroupListFragment::class)
|
||||||
|
|
|
@ -130,9 +130,9 @@ interface VectorComponent {
|
||||||
|
|
||||||
fun emojiDataSource(): EmojiDataSource
|
fun emojiDataSource(): EmojiDataSource
|
||||||
|
|
||||||
fun alertManager() : PopupAlertManager
|
fun alertManager(): PopupAlertManager
|
||||||
|
|
||||||
fun reAuthHelper() : ReAuthHelper
|
fun reAuthHelper(): ReAuthHelper
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.riotx.core.extensions
|
package im.vector.riotx.core.extensions
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentTransaction
|
import androidx.fragment.app.FragmentTransaction
|
||||||
|
@ -59,3 +60,8 @@ fun <T : Fragment> VectorBaseActivity.addFragmentToBackstack(frameId: Int,
|
||||||
fun VectorBaseActivity.hideKeyboard() {
|
fun VectorBaseActivity.hideKeyboard() {
|
||||||
currentFocus?.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) {
|
sessionListener.globalErrorLiveData.observeEvent(this) {
|
||||||
handleGlobalError(it)
|
handleGlobalError(it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,9 +33,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.features.notifications.NotificationUtils
|
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.
|
* 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.
|
* 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
|
* 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
|
* Handle locale configuration change, such as theme, font size and locale chosen by the user
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class VectorConfiguration @Inject constructor(private val context: Context) {
|
class VectorConfiguration @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
// TODO Import mLanguageReceiver From Riot?
|
|
||||||
fun onConfigurationChanged() {
|
fun onConfigurationChanged() {
|
||||||
if (Locale.getDefault().toString() != VectorLocale.applicationLocale.toString()) {
|
if (Locale.getDefault().toString() != VectorLocale.applicationLocale.toString()) {
|
||||||
Timber.v("## onConfigurationChanged(): the locale has been updated to ${Locale.getDefault()}")
|
Timber.v("## onConfigurationChanged(): the locale has been updated to ${Locale.getDefault()}")
|
||||||
Timber.v("## onConfigurationChanged(): restore the expected value ${VectorLocale.applicationLocale}")
|
Timber.v("## onConfigurationChanged(): restore the expected value ${VectorLocale.applicationLocale}")
|
||||||
updateApplicationSettings(VectorLocale.applicationLocale,
|
Locale.setDefault(VectorLocale.applicationLocale)
|
||||||
FontScale.getFontScalePrefValue(context),
|
|
||||||
ThemeUtils.getApplicationTheme(context))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* Init the configuration from the saved one
|
||||||
*/
|
*/
|
||||||
fun initConfiguration() {
|
fun initConfiguration() {
|
||||||
VectorLocale.init(context)
|
VectorLocale.init(context)
|
||||||
val locale = VectorLocale.applicationLocale
|
val locale = VectorLocale.applicationLocale
|
||||||
val fontScale = FontScale.getFontScale(context)
|
val fontScale = FontScale.getFontScaleValue(context)
|
||||||
val theme = ThemeUtils.getApplicationTheme(context)
|
val theme = ThemeUtils.getApplicationTheme(context)
|
||||||
|
|
||||||
Locale.setDefault(locale)
|
Locale.setDefault(locale)
|
||||||
val config = Configuration(context.resources.configuration)
|
val config = Configuration(context.resources.configuration)
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
config.locale = locale
|
config.locale = locale
|
||||||
config.fontScale = fontScale
|
config.fontScale = fontScale.scale
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
context.resources.updateConfiguration(config, context.resources.displayMetrics)
|
context.resources.updateConfiguration(config, context.resources.displayMetrics)
|
||||||
|
|
||||||
|
@ -93,16 +61,6 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
|
||||||
ThemeUtils.setApplicationTheme(context, theme)
|
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
|
* Compute a localised context
|
||||||
*
|
*
|
||||||
|
@ -115,7 +73,7 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
|
||||||
val resources = context.resources
|
val resources = context.resources
|
||||||
val locale = VectorLocale.applicationLocale
|
val locale = VectorLocale.applicationLocale
|
||||||
val configuration = resources.configuration
|
val configuration = resources.configuration
|
||||||
configuration.fontScale = FontScale.getFontScale(context)
|
configuration.fontScale = FontScale.getFontScaleValue(context).scale
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
configuration.setLocale(locale)
|
configuration.setLocale(locale)
|
||||||
|
@ -142,10 +100,9 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
|
||||||
* Compute the locale status value
|
* Compute the locale status value
|
||||||
* @return the local status value
|
* @return the local status value
|
||||||
*/
|
*/
|
||||||
// TODO Create data class for this
|
|
||||||
fun getHash(): String {
|
fun getHash(): String {
|
||||||
return (VectorLocale.applicationLocale.toString()
|
return (VectorLocale.applicationLocale.toString()
|
||||||
+ "_" + FontScale.getFontScalePrefValue(context)
|
+ "_" + FontScale.getFontScaleValue(context).preferenceValue
|
||||||
+ "_" + ThemeUtils.getApplicationTheme(context))
|
+ "_" + ThemeUtils.getApplicationTheme(context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,9 @@ import im.vector.riotx.BuildConfig
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotx.core.extensions.toOnOff
|
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.VectorLocale
|
||||||
import im.vector.riotx.features.settings.VectorPreferences
|
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.themes.ThemeUtils
|
||||||
import im.vector.riotx.features.version.VersionProvider
|
import im.vector.riotx.features.version.VersionProvider
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
|
@ -58,10 +58,13 @@ import javax.inject.Singleton
|
||||||
* BugReporter creates and sends the bug reports.
|
* BugReporter creates and sends the bug reports.
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
class BugReporter @Inject constructor(
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
private val versionProvider: VersionProvider,
|
private val versionProvider: VersionProvider,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val vectorFileLogger: VectorFileLogger) {
|
private val vectorFileLogger: VectorFileLogger,
|
||||||
|
private val systemLocaleProvider: SystemLocaleProvider
|
||||||
|
) {
|
||||||
var inMultiWindowMode = false
|
var inMultiWindowMode = false
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -240,7 +243,7 @@ class BugReporter @Inject constructor(private val activeSessionHolder: ActiveSes
|
||||||
+ Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME)
|
+ Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME)
|
||||||
.addFormDataPart("locale", Locale.getDefault().toString())
|
.addFormDataPart("locale", Locale.getDefault().toString())
|
||||||
.addFormDataPart("app_language", VectorLocale.applicationLocale.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))
|
.addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
|
||||||
|
|
||||||
val buildNumber = context.getString(R.string.build_number)
|
val buildNumber = context.getString(R.string.build_number)
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package im.vector.riotx.features.settings
|
package im.vector.riotx.features.settings
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
@ -29,124 +29,59 @@ object FontScale {
|
||||||
// Key for the SharedPrefs
|
// Key for the SharedPrefs
|
||||||
private const val APPLICATION_FONT_SCALE_KEY = "APPLICATION_FONT_SCALE_KEY"
|
private const val APPLICATION_FONT_SCALE_KEY = "APPLICATION_FONT_SCALE_KEY"
|
||||||
|
|
||||||
|
data class FontScaleValue(
|
||||||
|
val index: Int,
|
||||||
// Possible values for the SharedPrefs
|
// Possible values for the SharedPrefs
|
||||||
private const val FONT_SCALE_TINY = "FONT_SCALE_TINY"
|
val preferenceValue: String,
|
||||||
private const val FONT_SCALE_SMALL = "FONT_SCALE_SMALL"
|
val scale: Float,
|
||||||
private const val FONT_SCALE_NORMAL = "FONT_SCALE_NORMAL"
|
@StringRes
|
||||||
private const val FONT_SCALE_LARGE = "FONT_SCALE_LARGE"
|
val nameResId: Int
|
||||||
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
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val prefValueToNameResId = mapOf(
|
private val fontScaleValues = listOf(
|
||||||
FONT_SCALE_TINY to R.string.tiny,
|
FontScaleValue(0, "FONT_SCALE_TINY", 0.70f, R.string.tiny),
|
||||||
FONT_SCALE_SMALL to R.string.small,
|
FontScaleValue(1, "FONT_SCALE_SMALL", 0.85f, R.string.small),
|
||||||
FONT_SCALE_NORMAL to R.string.normal,
|
FontScaleValue(2, "FONT_SCALE_NORMAL", 1.00f, R.string.normal),
|
||||||
FONT_SCALE_LARGE to R.string.large,
|
FontScaleValue(3, "FONT_SCALE_LARGE", 1.15f, R.string.large),
|
||||||
FONT_SCALE_LARGER to R.string.larger,
|
FontScaleValue(4, "FONT_SCALE_LARGER", 1.30f, R.string.larger),
|
||||||
FONT_SCALE_LARGEST to R.string.largest,
|
FontScaleValue(5, "FONT_SCALE_LARGEST", 1.45f, R.string.largest),
|
||||||
FONT_SCALE_HUGE to R.string.huge
|
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
|
* 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)
|
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
|
val fontScale = context.resources.configuration.fontScale
|
||||||
|
|
||||||
scalePreferenceValue = FONT_SCALE_NORMAL
|
(fontScaleValues.firstOrNull { it.scale == fontScale } ?: normalFontScaleValue)
|
||||||
|
.also { preferences.edit { putString(APPLICATION_FONT_SCALE_KEY, it.preferenceValue) } }
|
||||||
if (fontScaleToPrefValue.containsKey(fontScale)) {
|
|
||||||
scalePreferenceValue = fontScaleToPrefValue[fontScale] as String
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences.edit {
|
|
||||||
putString(APPLICATION_FONT_SCALE_KEY, scalePreferenceValue)
|
|
||||||
}
|
|
||||||
} else {
|
} 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 {
|
private fun saveFontScaleValue(context: Context, fontScaleValue: FontScaleValue) {
|
||||||
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)
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.edit {
|
.edit { putString(APPLICATION_FONT_SCALE_KEY, fontScaleValue.preferenceValue) }
|
||||||
putString(APPLICATION_FONT_SCALE_KEY, scaleValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,11 @@ package im.vector.riotx.features.settings
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import im.vector.riotx.BuildConfig
|
import androidx.preference.PreferenceManager
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
@ -41,10 +39,9 @@ object VectorLocale {
|
||||||
private val defaultLocale = Locale("en", "US")
|
private val defaultLocale = Locale("en", "US")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The supported application languages
|
* The cache of supported application languages
|
||||||
*/
|
*/
|
||||||
var supportedLocales = ArrayList<Locale>()
|
private val supportedLocales = mutableListOf<Locale>()
|
||||||
private set
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the current application locale
|
* Provides the current application locale
|
||||||
|
@ -52,10 +49,13 @@ object VectorLocale {
|
||||||
var applicationLocale = defaultLocale
|
var applicationLocale = defaultLocale
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
lateinit var context: Context
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init this object
|
* Init this object
|
||||||
*/
|
*/
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
|
this.context = context
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) {
|
if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) {
|
||||||
|
@ -72,19 +72,14 @@ object VectorLocale {
|
||||||
applicationLocale = defaultLocale
|
applicationLocale = defaultLocale
|
||||||
}
|
}
|
||||||
|
|
||||||
saveApplicationLocale(context, applicationLocale)
|
saveApplicationLocale(applicationLocale)
|
||||||
}
|
|
||||||
|
|
||||||
// init the known locales in background, using kotlin coroutines
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
|
||||||
initApplicationLocales(context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the new application locale.
|
* Save the new application locale.
|
||||||
*/
|
*/
|
||||||
fun saveApplicationLocale(context: Context, locale: Locale) {
|
fun saveApplicationLocale(locale: Locale) {
|
||||||
applicationLocale = locale
|
applicationLocale = locale
|
||||||
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(context).edit {
|
PreferenceManager.getDefaultSharedPreferences(context).edit {
|
||||||
|
@ -144,6 +139,7 @@ object VectorLocale {
|
||||||
} else {
|
} else {
|
||||||
val resources = context.resources
|
val resources = context.resources
|
||||||
val conf = resources.configuration
|
val conf = resources.configuration
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
val savedLocale = conf.locale
|
val savedLocale = conf.locale
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@ -165,11 +161,9 @@ object VectorLocale {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the supported application locales list
|
* Init the supported application locales list
|
||||||
*
|
|
||||||
* @param context the context
|
|
||||||
*/
|
*/
|
||||||
private fun initApplicationLocales(context: Context) {
|
private fun initApplicationLocales() {
|
||||||
val knownLocalesSet = HashSet<Triple<String, String, String>>()
|
val knownLocalesSet = HashSet<Triple<String, String, String>>()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -195,9 +189,7 @@ object VectorLocale {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
supportedLocales.clear()
|
val list = knownLocalesSet.map { (language, country, script) ->
|
||||||
|
|
||||||
knownLocalesSet.mapTo(supportedLocales) { (language, country, script) ->
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
Locale.Builder()
|
Locale.Builder()
|
||||||
.setLanguage(language)
|
.setLanguage(language)
|
||||||
|
@ -208,9 +200,11 @@ object VectorLocale {
|
||||||
Locale(language, country)
|
Locale(language, country)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort by human display names
|
// sort by human display names
|
||||||
supportedLocales.sortWith(Comparator { lhs, rhs -> localeToLocalisedString(lhs).compareTo(localeToLocalisedString(rhs)) })
|
.sortedBy { localeToLocalisedString(it).toLowerCase(it) }
|
||||||
|
|
||||||
|
supportedLocales.clear()
|
||||||
|
supportedLocales.addAll(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -235,10 +229,18 @@ object VectorLocale {
|
||||||
append(locale.getDisplayCountry(locale))
|
append(locale.getDisplayCountry(locale))
|
||||||
append(")")
|
append(")")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// In debug mode, also display information about the locale in the current locale.
|
/**
|
||||||
if (BuildConfig.DEBUG) {
|
* Information about the locale in the current locale
|
||||||
append("\n[")
|
*
|
||||||
|
* @param locale the locale to get info from
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
fun localeToLocalisedStringInfo(locale: Locale): String {
|
||||||
|
return buildString {
|
||||||
|
append("[")
|
||||||
append(locale.displayLanguage)
|
append(locale.displayLanguage)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && locale.script != "Latn") {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && locale.script != "Latn") {
|
||||||
append(" - ")
|
append(" - ")
|
||||||
|
@ -252,5 +254,14 @@ object VectorLocale {
|
||||||
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.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.widget.CheckedTextView
|
import android.widget.CheckedTextView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import im.vector.riotx.R
|
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.VectorListPreference
|
||||||
import im.vector.riotx.core.preference.VectorPreference
|
import im.vector.riotx.core.preference.VectorPreference
|
||||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
import im.vector.riotx.features.configuration.VectorConfiguration
|
||||||
|
@ -54,13 +54,9 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
||||||
findPreference<VectorListPreference>(ThemeUtils.APPLICATION_THEME_KEY)!!
|
findPreference<VectorListPreference>(ThemeUtils.APPLICATION_THEME_KEY)!!
|
||||||
.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
if (newValue is String) {
|
if (newValue is String) {
|
||||||
vectorConfiguration.updateApplicationTheme(newValue)
|
ThemeUtils.setApplicationTheme(requireContext(), newValue)
|
||||||
// Restart the Activity
|
// Restart the Activity
|
||||||
activity?.let {
|
activity?.restart()
|
||||||
// Note: recreate does not apply the color correctly
|
|
||||||
it.startActivity(it.intent)
|
|
||||||
it.finish()
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
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
|
// user interface management
|
||||||
// ==============================================================================================================
|
// ==============================================================================================================
|
||||||
|
@ -152,14 +133,8 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
||||||
// Selected language
|
// Selected language
|
||||||
selectedLanguagePreference.summary = VectorLocale.localeToLocalisedString(VectorLocale.applicationLocale)
|
selectedLanguagePreference.summary = VectorLocale.localeToLocalisedString(VectorLocale.applicationLocale)
|
||||||
|
|
||||||
selectedLanguagePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
notImplemented()
|
|
||||||
// TODO startActivityForResult(LanguagePickerActivity.getIntent(activity), REQUEST_LOCALE)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text size
|
// Text size
|
||||||
textSizePreference.summary = FontScale.getFontScaleDescription(activity!!)
|
textSizePreference.summary = getString(FontScale.getFontScaleValue(activity!!).nameResId)
|
||||||
|
|
||||||
textSizePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
textSizePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
activity?.let { displayTextSizeSelection(it) }
|
activity?.let { displayTextSizeSelection(it) }
|
||||||
|
@ -182,25 +157,20 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
||||||
|
|
||||||
val childCount = linearLayout.childCount
|
val childCount = linearLayout.childCount
|
||||||
|
|
||||||
val scaleText = FontScale.getFontScaleDescription(activity)
|
val index = FontScale.getFontScaleValue(activity).index
|
||||||
|
|
||||||
for (i in 0 until childCount) {
|
for (i in 0 until childCount) {
|
||||||
val v = linearLayout.getChildAt(i)
|
val v = linearLayout.getChildAt(i)
|
||||||
|
|
||||||
if (v is CheckedTextView) {
|
if (v is CheckedTextView) {
|
||||||
v.isChecked = v.text == scaleText
|
v.isChecked = i == index
|
||||||
|
|
||||||
v.setOnClickListener {
|
v.setOnClickListener {
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
FontScale.updateFontScale(activity, v.text.toString())
|
FontScale.updateFontScale(activity, i)
|
||||||
activity.startActivity(activity.intent)
|
activity.restart()
|
||||||
activity.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
fun getApplicationTheme(context: Context): String {
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE)!!
|
.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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" />
|
|
@ -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>
|
</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="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>
|
</resources>
|
|
@ -7,9 +7,10 @@
|
||||||
android:title="@string/settings_user_interface">
|
android:title="@string/settings_user_interface">
|
||||||
|
|
||||||
<im.vector.riotx.core.preference.VectorPreference
|
<im.vector.riotx.core.preference.VectorPreference
|
||||||
android:dialogTitle="@string/settings_select_language"
|
|
||||||
android:key="SETTINGS_INTERFACE_LANGUAGE_PREFERENCE_KEY"
|
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
|
<im.vector.riotx.core.preference.VectorListPreference
|
||||||
android:defaultValue="light"
|
android:defaultValue="light"
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
<im.vector.riotx.core.preference.VectorPreference
|
<im.vector.riotx.core.preference.VectorPreference
|
||||||
android:dialogTitle="@string/font_size"
|
android:dialogTitle="@string/font_size"
|
||||||
android:key="SETTINGS_INTERFACE_TEXT_SIZE_KEY"
|
android:key="SETTINGS_INTERFACE_TEXT_SIZE_KEY"
|
||||||
|
android:persistent="false"
|
||||||
android:title="@string/font_size" />
|
android:title="@string/font_size" />
|
||||||
|
|
||||||
</im.vector.riotx.core.preference.VectorPreferenceCategory>
|
</im.vector.riotx.core.preference.VectorPreferenceCategory>
|
||||||
|
|
Loading…
Reference in New Issue