Fix `StaticFieldLeak` issue (context).

Make VectorLocal an injectable class.
This commit is contained in:
Benoit Marty 2022-09-16 11:41:41 +02:00 committed by Benoit Marty
parent 7e8a39e6de
commit d8436874e2
18 changed files with 78 additions and 47 deletions

View File

@ -80,6 +80,7 @@
<issue id="KotlinPropertyAccess" severity="error" />
<issue id="DefaultLocale" severity="error" />
<issue id="CheckResult" severity="error" />
<issue id="StaticFieldLeak" severity="error" />
<issue id="InvalidPackage">
<!-- Ignore error from HtmlCompressor lib -->

View File

@ -109,6 +109,7 @@ class VectorApplication :
@Inject lateinit var fcmHelper: FcmHelper
@Inject lateinit var buildMeta: BuildMeta
@Inject lateinit var leakDetector: LeakDetector
@Inject lateinit var vectorLocale: VectorLocale
// font thread handler
private var fontThreadHandler: Handler? = null
@ -159,7 +160,7 @@ class VectorApplication :
R.array.com_google_android_gms_fonts_certs
)
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
VectorLocale.init(this, buildMeta)
vectorLocale.init()
ThemeUtils.init(this)
vectorConfiguration.applyToApplicationContext()

View File

@ -84,6 +84,7 @@ import im.vector.app.features.rageshake.RageShake
import im.vector.app.features.session.SessionListener
import im.vector.app.features.settings.FontScalePreferences
import im.vector.app.features.settings.FontScalePreferencesImpl
import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.themes.ThemeUtils
@ -155,6 +156,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
@Inject lateinit var rageShake: RageShake
@Inject lateinit var buildMeta: BuildMeta
@Inject lateinit var fontScalePreferences: FontScalePreferences
@Inject lateinit var vectorLocale: VectorLocale
// For debug only
@Inject lateinit var debugReceiver: DebugReceiver
@ -177,7 +179,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
override fun attachBaseContext(base: Context) {
val fontScalePreferences = FontScalePreferencesImpl(PreferenceManager.getDefaultSharedPreferences(base), AndroidSystemSettingsProvider(base))
val vectorConfiguration = VectorConfiguration(this, fontScalePreferences)
val vectorConfiguration = VectorConfiguration(this, fontScalePreferences, vectorLocale)
super.attachBaseContext(vectorConfiguration.getLocalisedContext(base))
}

View File

@ -49,6 +49,7 @@ class JitsiService @Inject constructor(
private val themeProvider: ThemeProvider,
private val jitsiJWTFactory: JitsiJWTFactory,
private val clock: Clock,
private val vectorLocale: VectorLocale,
) {
companion object {
@ -163,7 +164,7 @@ class JitsiService @Inject constructor(
if (widgetSessionId.length > 8) {
widgetSessionId = widgetSessionId.substring(0, 7)
}
roomId.substring(1, roomId.indexOf(":") - 1) + widgetSessionId.lowercase(VectorLocale.applicationLocale)
roomId.substring(1, roomId.indexOf(":") - 1) + widgetSessionId.lowercase(vectorLocale.applicationLocale)
}
}

View File

@ -20,12 +20,15 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.addChildFragment
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetCallDialPadBinding
import im.vector.app.features.settings.VectorLocale
import javax.inject.Inject
@AndroidEntryPoint
class CallDialPadBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetCallDialPadBinding>() {
companion object {
@ -41,6 +44,8 @@ class CallDialPadBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetCa
}
}
@Inject lateinit var vectorLocale: VectorLocale
override val showExpanded = true
var callback: DialPadFragment.Callback? = null
@ -62,7 +67,7 @@ class CallDialPadBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetCa
putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, showActions)
putBoolean(DialPadFragment.EXTRA_ENABLE_OK, showActions)
putBoolean(DialPadFragment.EXTRA_CURSOR_VISIBLE, false)
putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
putString(DialPadFragment.EXTRA_REGION_CODE, vectorLocale.applicationLocale.country)
}
callback = DialPadFragmentCallbackWrapper(this@CallDialPadBottomSheet.callback)
}.also {

View File

@ -78,7 +78,7 @@ class PstnDialActivity : SimpleFragmentActivity() {
arguments = Bundle().apply {
putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, true)
putBoolean(DialPadFragment.EXTRA_ENABLE_OK, true)
putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
putString(DialPadFragment.EXTRA_REGION_CODE, vectorLocale.applicationLocale.country)
}
callback = object : DialPadFragment.Callback {
override fun onOkClicked(formatted: String?, raw: String?) {

View File

@ -59,7 +59,7 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
}
}
sectionsPagerAdapter = CallTransferPagerAdapter(this)
sectionsPagerAdapter = CallTransferPagerAdapter(this, vectorLocale)
views.callTransferViewPager.adapter = sectionsPagerAdapter
TabLayoutMediator(views.callTransferTabLayout, views.callTransferViewPager) { tab, position ->

View File

@ -27,7 +27,8 @@ import im.vector.app.features.userdirectory.UserListFragment
import im.vector.app.features.userdirectory.UserListFragmentArgs
class CallTransferPagerAdapter(
private val fragmentActivity: FragmentActivity
private val fragmentActivity: FragmentActivity,
private val vectorLocale: VectorLocale,
) : FragmentStateAdapter(fragmentActivity) {
companion object {
@ -61,7 +62,7 @@ class CallTransferPagerAdapter(
arguments = Bundle().apply {
putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, true)
putBoolean(DialPadFragment.EXTRA_ENABLE_OK, false)
putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
putString(DialPadFragment.EXTRA_REGION_CODE, vectorLocale.applicationLocale.country)
}
}
}

View File

@ -33,21 +33,22 @@ import javax.inject.Inject
*/
class VectorConfiguration @Inject constructor(
private val context: Context,
private val fontScalePreferences: FontScalePreferences
private val fontScalePreferences: FontScalePreferences,
private val vectorLocale: VectorLocale,
) {
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(): restore the expected value ${VectorLocale.applicationLocale}")
Locale.setDefault(VectorLocale.applicationLocale)
Timber.v("## onConfigurationChanged(): restore the expected value ${vectorLocale.applicationLocale}")
Locale.setDefault(vectorLocale.applicationLocale)
}
// Night mode may have changed
ThemeUtils.init(context)
}
fun applyToApplicationContext() {
val locale = VectorLocale.applicationLocale
val locale = vectorLocale.applicationLocale
val fontScale = fontScalePreferences.getResolvedFontScaleValue()
Locale.setDefault(locale)
@ -67,7 +68,7 @@ class VectorConfiguration @Inject constructor(
*/
fun getLocalisedContext(context: Context): Context {
try {
val locale = VectorLocale.applicationLocale
val locale = vectorLocale.applicationLocale
// create new configuration passing old configuration from original Context
val configuration = Configuration(context.resources.configuration)
@ -107,7 +108,7 @@ class VectorConfiguration @Inject constructor(
* @return the local status value
*/
fun getHash(): String {
return (VectorLocale.applicationLocale.toString() +
return (vectorLocale.applicationLocale.toString() +
"_" + fontScalePreferences.getResolvedFontScaleValue().preferenceValue +
"_" + ThemeUtils.getApplicationTheme(context))
}

View File

@ -32,6 +32,7 @@ import im.vector.app.databinding.FragmentKeysBackupSetupStep2Binding
import im.vector.app.features.settings.VectorLocale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint
class KeysBackupSetupStep2Fragment :
@ -43,6 +44,8 @@ class KeysBackupSetupStep2Fragment :
private val zxcvbn = Zxcvbn()
@Inject lateinit var vectorLocale: VectorLocale
private fun onPassphraseChanged() {
viewModel.passphrase.value = views.keysBackupSetupStep2PassphraseEnterEdittext.text.toString()
viewModel.confirmPassphraseError.value = null
@ -78,12 +81,12 @@ class KeysBackupSetupStep2Fragment :
views.keysBackupSetupStep2PassphraseStrengthLevel.strength = score
if (score in 1..3) {
val warning = strength.feedback?.getWarning(VectorLocale.applicationLocale)
val warning = strength.feedback?.getWarning(vectorLocale.applicationLocale)
if (warning != null) {
views.keysBackupSetupStep2PassphraseEnterTil.error = warning
}
val suggestions = strength.feedback?.getSuggestions(VectorLocale.applicationLocale)
val suggestions = strength.feedback?.getSuggestions(vectorLocale.applicationLocale)
if (suggestions != null) {
views.keysBackupSetupStep2PassphraseEnterTil.error = suggestions.firstOrNull()
}

View File

@ -34,6 +34,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.editorActionEvents
import reactivecircus.flowbinding.android.widget.textChanges
import javax.inject.Inject
@AndroidEntryPoint
class BootstrapEnterPassphraseFragment :
@ -43,6 +44,8 @@ class BootstrapEnterPassphraseFragment :
return FragmentBootstrapEnterPassphraseBinding.inflate(inflater, container, false)
}
@Inject lateinit var vectorLocale: VectorLocale
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -105,8 +108,8 @@ class BootstrapEnterPassphraseFragment :
views.ssssPassphraseSecurityProgress.strength = score
if (score in 1..3) {
val hint =
strength.feedback?.getWarning(VectorLocale.applicationLocale)?.takeIf { it.isNotBlank() }
?: strength.feedback?.getSuggestions(VectorLocale.applicationLocale)?.firstOrNull()
strength.feedback?.getWarning(vectorLocale.applicationLocale)?.takeIf { it.isNotBlank() }
?: strength.feedback?.getSuggestions(vectorLocale.applicationLocale)?.firstOrNull()
if (hint != null && hint != views.ssssPassphraseEnterTil.error.toString()) {
views.ssssPassphraseEnterTil.error = hint
}

View File

@ -75,6 +75,7 @@ class HomeDetailFragment :
@Inject lateinit var callManager: WebRtcCallManager
@Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var spaceStateHandler: SpaceStateHandler
@Inject lateinit var vectorLocale: VectorLocale
private val viewModel: HomeDetailViewModel by fragmentViewModel()
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
@ -378,7 +379,7 @@ class HomeDetailFragment :
arguments = Bundle().apply {
putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, true)
putBoolean(DialPadFragment.EXTRA_ENABLE_OK, true)
putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
putString(DialPadFragment.EXTRA_REGION_CODE, vectorLocale.applicationLocale.country)
}
applyCallback()
}

View File

@ -80,6 +80,7 @@ class BugReporter @Inject constructor(
private val buildMeta: BuildMeta,
private val processInfo: ProcessInfo,
private val sdkIntProvider: BuildVersionSdkIntProvider,
private val vectorLocale: VectorLocale,
) {
var inMultiWindowMode = false
@ -294,7 +295,7 @@ class BugReporter @Inject constructor(
Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME
)
.addFormDataPart("locale", Locale.getDefault().toString())
.addFormDataPart("app_language", VectorLocale.applicationLocale.toString())
.addFormDataPart("app_language", vectorLocale.applicationLocale.toString())
.addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString())
.addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
.addFormDataPart("server_version", serverVersion)

View File

@ -27,19 +27,27 @@ import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.IllformedLocaleException
import java.util.Locale
import javax.inject.Inject
import javax.inject.Singleton
/**
* Object to manage the Locale choice of the user.
*/
object VectorLocale {
private const val APPLICATION_LOCALE_COUNTRY_KEY = "APPLICATION_LOCALE_COUNTRY_KEY"
private const val APPLICATION_LOCALE_VARIANT_KEY = "APPLICATION_LOCALE_VARIANT_KEY"
private const val APPLICATION_LOCALE_LANGUAGE_KEY = "APPLICATION_LOCALE_LANGUAGE_KEY"
private const val APPLICATION_LOCALE_SCRIPT_KEY = "APPLICATION_LOCALE_SCRIPT_KEY"
@Singleton
class VectorLocale @Inject constructor(
private val context: Context,
private val buildMeta: BuildMeta,
) {
companion object {
private const val APPLICATION_LOCALE_COUNTRY_KEY = "APPLICATION_LOCALE_COUNTRY_KEY"
private const val APPLICATION_LOCALE_VARIANT_KEY = "APPLICATION_LOCALE_VARIANT_KEY"
private const val APPLICATION_LOCALE_LANGUAGE_KEY = "APPLICATION_LOCALE_LANGUAGE_KEY"
private const val APPLICATION_LOCALE_SCRIPT_KEY = "APPLICATION_LOCALE_SCRIPT_KEY"
private const val ISO_15924_LATN = "Latn"
}
private val defaultLocale = Locale("en", "US")
private const val ISO_15924_LATN = "Latn"
/**
* The cache of supported application languages.
@ -52,15 +60,10 @@ object VectorLocale {
var applicationLocale = defaultLocale
private set
private lateinit var context: Context
private lateinit var buildMeta: BuildMeta
/**
* Init this object.
* Init this singleton.
*/
fun init(context: Context, buildMeta: BuildMeta) {
this.context = context
this.buildMeta = buildMeta
fun init() {
val preferences = DefaultSharedPreferences.getInstance(context)
if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) {

View File

@ -46,6 +46,7 @@ class VectorSettingsPreferencesFragment :
@Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var fontScalePreferences: FontScalePreferences
@Inject lateinit var vectorFeatures: VectorFeatures
@Inject lateinit var vectorLocale: VectorLocale
override var titleRes = R.string.settings_preferences
override val preferenceXmlRes = R.xml.vector_settings_preferences
@ -198,7 +199,7 @@ class VectorSettingsPreferencesFragment :
private fun setUserInterfacePreferences() {
// Selected language
selectedLanguagePreference.summary = VectorLocale.localeToLocalisedString(VectorLocale.applicationLocale)
selectedLanguagePreference.summary = vectorLocale.localeToLocalisedString(vectorLocale.applicationLocale)
// Text size
textSizePreference.summary = getString(fontScalePreferences.getResolvedFontScaleValue().nameResId)

View File

@ -37,13 +37,15 @@ import javax.inject.Inject
class LocalePickerController @Inject constructor(
private val vectorPreferences: VectorPreferences,
private val stringProvider: StringProvider,
private val errorFormatter: ErrorFormatter
private val errorFormatter: ErrorFormatter,
private val vectorLocale: VectorLocale,
) : TypedEpoxyController<LocalePickerViewState>() {
var listener: Listener? = null
override fun buildModels(data: LocalePickerViewState?) {
val list = data?.locales ?: return
val currentLocale = data.currentLocale ?: return
val host = this
profileSectionItem {
@ -51,10 +53,10 @@ class LocalePickerController @Inject constructor(
title(host.stringProvider.getString(R.string.choose_locale_current_locale_title))
}
localeItem {
id(data.currentLocale.toString())
title(VectorLocale.localeToLocalisedString(data.currentLocale).safeCapitalize(data.currentLocale))
id(currentLocale.toString())
title(host.vectorLocale.localeToLocalisedString(currentLocale).safeCapitalize(currentLocale))
if (host.vectorPreferences.developerMode()) {
subtitle(VectorLocale.localeToLocalisedStringInfo(data.currentLocale))
subtitle(host.vectorLocale.localeToLocalisedStringInfo(currentLocale))
}
clickListener { host.listener?.onUseCurrentClicked() }
}
@ -78,13 +80,13 @@ class LocalePickerController @Inject constructor(
}
} else {
list()
.filter { it.toString() != data.currentLocale.toString() }
.filter { it.toString() != currentLocale.toString() }
.forEach { locale ->
localeItem {
id(locale.toString())
title(VectorLocale.localeToLocalisedString(locale).safeCapitalize(locale))
title(host.vectorLocale.localeToLocalisedString(locale).safeCapitalize(locale))
if (host.vectorPreferences.developerMode()) {
subtitle(VectorLocale.localeToLocalisedStringInfo(locale))
subtitle(host.vectorLocale.localeToLocalisedStringInfo(locale))
}
clickListener { host.listener?.onLocaleClicked(locale) }
}

View File

@ -30,7 +30,8 @@ import kotlinx.coroutines.launch
class LocalePickerViewModel @AssistedInject constructor(
@Assisted initialState: LocalePickerViewState,
private val vectorConfiguration: VectorConfiguration
private val vectorConfiguration: VectorConfiguration,
private val vectorLocale: VectorLocale,
) : VectorViewModel<LocalePickerViewState, LocalePickerAction, LocalePickerViewEvents>(initialState) {
@AssistedFactory
@ -39,8 +40,13 @@ class LocalePickerViewModel @AssistedInject constructor(
}
init {
setState {
copy(
currentLocale = vectorLocale.applicationLocale
)
}
viewModelScope.launch {
val result = VectorLocale.getSupportedLocales()
val result = vectorLocale.getSupportedLocales()
setState {
copy(
@ -59,7 +65,7 @@ class LocalePickerViewModel @AssistedInject constructor(
}
private fun handleSelectLocale(action: LocalePickerAction.SelectLocale) {
VectorLocale.saveApplicationLocale(action.locale)
vectorLocale.saveApplicationLocale(action.locale)
vectorConfiguration.applyToApplicationContext()
_viewEvents.post(LocalePickerViewEvents.RestartActivity)
}

View File

@ -19,10 +19,9 @@ package im.vector.app.features.settings.locale
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized
import im.vector.app.features.settings.VectorLocale
import java.util.Locale
data class LocalePickerViewState(
val currentLocale: Locale = VectorLocale.applicationLocale,
val currentLocale: Locale? = null,
val locales: Async<List<Locale>> = Uninitialized
) : MavericksState