mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-02 20:26:47 +01:00
Hot change of theme - WIP
This commit is contained in:
parent
bfbb29b2cf
commit
2c2f517e52
@ -18,6 +18,7 @@ package im.vector.riotredesign
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import androidx.multidex.MultiDex
|
||||
import com.airbnb.epoxy.EpoxyAsyncUtil
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
@ -27,10 +28,12 @@ import com.github.piasy.biv.loader.glide.GlideImageLoader
|
||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.riotredesign.core.di.AppModule
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.home.HomeModule
|
||||
import im.vector.riotredesign.features.rageshake.VectorFileLogger
|
||||
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.log.EmptyLogger
|
||||
import org.koin.standalone.StandAloneContext.startKoin
|
||||
import timber.log.Timber
|
||||
@ -38,6 +41,8 @@ import timber.log.Timber
|
||||
|
||||
class VectorApplication : Application() {
|
||||
|
||||
val vectorConfiguration: VectorConfiguration by inject()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
@ -61,6 +66,8 @@ class VectorApplication : Application() {
|
||||
startKoin(listOf(appModule, homeModule, roomDirectoryModule), logger = EmptyLogger())
|
||||
|
||||
Matrix.getInstance().setApplicationFlavor(BuildConfig.FLAVOR_DESCRIPTION)
|
||||
|
||||
vectorConfiguration.initConfiguration()
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
@ -68,4 +75,10 @@ class VectorApplication : Application() {
|
||||
MultiDex.install(this)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
vectorConfiguration.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ import im.vector.riotredesign.core.error.ErrorFormatter
|
||||
import im.vector.riotredesign.core.resources.LocaleProvider
|
||||
import im.vector.riotredesign.core.resources.StringArrayProvider
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
||||
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||
import im.vector.riotredesign.features.home.room.list.AlphabeticalRoomComparator
|
||||
@ -37,6 +38,10 @@ class AppModule(private val context: Context) {
|
||||
|
||||
val definition = module {
|
||||
|
||||
single {
|
||||
VectorConfiguration(context)
|
||||
}
|
||||
|
||||
single {
|
||||
LocaleProvider(context.resources)
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2019 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.riotredesign.core.platform
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import org.koin.standalone.KoinComponent
|
||||
import org.koin.standalone.inject
|
||||
import timber.log.Timber
|
||||
|
||||
class ConfigurationViewModel : ViewModel(), KoinComponent {
|
||||
|
||||
private val vectorConfiguration: VectorConfiguration by inject()
|
||||
|
||||
private var currentConfigurationValue: String? = null
|
||||
|
||||
private val _activityRestarter = MutableLiveData<LiveEvent<Unit>>()
|
||||
val activityRestarter: LiveData<LiveEvent<Unit>>
|
||||
get() = _activityRestarter
|
||||
|
||||
|
||||
fun onActivityResumed() {
|
||||
if (currentConfigurationValue == null) {
|
||||
currentConfigurationValue = vectorConfiguration.getHash()
|
||||
Timber.v("Configuration: init to $currentConfigurationValue")
|
||||
} else {
|
||||
val newHash = vectorConfiguration.getHash()
|
||||
Timber.v("Configuration: newHash $newHash")
|
||||
|
||||
if (newHash != currentConfigurationValue) {
|
||||
Timber.v("Configuration: recreate the Activity")
|
||||
currentConfigurationValue = newHash
|
||||
|
||||
_activityRestarter.postValue(LiveEvent(Unit))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
|
||||
package im.vector.riotredesign.core.platform
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
@ -24,6 +25,8 @@ import android.view.View
|
||||
import androidx.annotation.*
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.Unbinder
|
||||
@ -33,6 +36,7 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import im.vector.riotredesign.BuildConfig
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.utils.toast
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.rageshake.BugReportActivity
|
||||
import im.vector.riotredesign.features.rageshake.BugReporter
|
||||
import im.vector.riotredesign.features.rageshake.RageShake
|
||||
@ -41,6 +45,7 @@ import im.vector.riotredesign.receivers.DebugReceiver
|
||||
import im.vector.ui.themes.ActivityOtherThemes
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import org.koin.android.ext.android.inject
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
@ -58,6 +63,10 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
* DATA
|
||||
* ========================================================================================== */
|
||||
|
||||
private val vectorConfiguration: VectorConfiguration by inject()
|
||||
|
||||
private lateinit var configurationViewModel: ConfigurationViewModel
|
||||
|
||||
private var unBinder: Unbinder? = null
|
||||
|
||||
private var savedInstanceState: Bundle? = null
|
||||
@ -70,6 +79,10 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
|
||||
private var rageShake: RageShake? = null
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(vectorConfiguration.getLocalisedContext(base))
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
restorables.forEach { it.onSaveInstanceState(outState) }
|
||||
@ -95,6 +108,16 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
configurationViewModel = ViewModelProviders.of(this).get(ConfigurationViewModel::class.java)
|
||||
|
||||
configurationViewModel.activityRestarter.observe(this, Observer {
|
||||
if (!it.hasBeenHandled) {
|
||||
// Recreate the Activity because configuration has changed
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
})
|
||||
|
||||
// Shake detector
|
||||
rageShake = RageShake(this)
|
||||
|
||||
@ -136,6 +159,8 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
|
||||
Timber.d("onResume Activity ${this.javaClass.simpleName}")
|
||||
|
||||
configurationViewModel.onActivityResumed()
|
||||
|
||||
if (this !is BugReportActivity) {
|
||||
rageShake?.start()
|
||||
}
|
||||
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2019 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.riotredesign.features.configuration
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import im.vector.riotredesign.features.settings.FontScale
|
||||
import im.vector.riotredesign.features.settings.VectorLocale
|
||||
import im.vector.riotredesign.features.themes.ThemeUtils
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Handle locale configuration change, such as theme, font size and locale chosen by the user
|
||||
*/
|
||||
class VectorConfiguration(private val context: Context) {
|
||||
|
||||
// TODO Import mLanguageReceiver From Riot?
|
||||
fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
if (Locale.getDefault().toString() != VectorLocale.applicationLocale.toString()) {
|
||||
Timber.v("## onConfigurationChanged() : the locale has been updated to " + Locale.getDefault().toString()
|
||||
+ ", restore the expected value " + VectorLocale.applicationLocale.toString())
|
||||
updateApplicationSettings(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)
|
||||
config.locale = locale
|
||||
config.fontScale = FontScale.getFontScale(context)
|
||||
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 theme = ThemeUtils.getApplicationTheme(context)
|
||||
|
||||
Locale.setDefault(locale)
|
||||
val config = Configuration(context.resources.configuration)
|
||||
config.locale = locale
|
||||
config.fontScale = fontScale
|
||||
context.resources.updateConfiguration(config, context.resources.displayMetrics)
|
||||
|
||||
// init the 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
|
||||
*
|
||||
* @param context the context
|
||||
* @return the localised context
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun getLocalisedContext(context: Context): Context {
|
||||
try {
|
||||
val resources = context.resources
|
||||
val locale = VectorLocale.applicationLocale
|
||||
val configuration = resources.configuration
|
||||
configuration.fontScale = FontScale.getFontScale(context)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
configuration.setLocale(locale)
|
||||
configuration.setLayoutDirection(locale)
|
||||
return context.createConfigurationContext(configuration)
|
||||
} else {
|
||||
configuration.locale = locale
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
configuration.setLayoutDirection(locale)
|
||||
}
|
||||
resources.updateConfiguration(configuration, resources.displayMetrics)
|
||||
return context
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "## getLocalisedContext() failed")
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the locale status value
|
||||
* @param activity the activity
|
||||
* @return the local status value
|
||||
*/
|
||||
// TODO Create data class for this
|
||||
fun getHash(): String {
|
||||
return (VectorLocale.applicationLocale.toString()
|
||||
+ "_" + FontScale.getFontScalePrefValue(context)
|
||||
+ "_" + ThemeUtils.getApplicationTheme(context))
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@ import im.vector.riotredesign.core.preference.UserAvatarPreference
|
||||
import im.vector.riotredesign.core.preference.VectorPreference
|
||||
import im.vector.riotredesign.core.utils.*
|
||||
import im.vector.riotredesign.features.MainActivity
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.themes.ThemeUtils
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.lang.ref.WeakReference
|
||||
@ -99,6 +100,8 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
|
||||
// used to avoid requesting to enter the password for each deletion
|
||||
private var mAccountPassword: String? = null
|
||||
|
||||
private val vectorConfiguration by inject<VectorConfiguration>()
|
||||
|
||||
// current publicised group list
|
||||
private var mPublicisedGroups: MutableSet<String>? = null
|
||||
|
||||
@ -366,8 +369,10 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
|
||||
findPreference(ThemeUtils.APPLICATION_THEME_KEY)
|
||||
.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
if (newValue is String) {
|
||||
// TODO VectorApp.updateApplicationTheme(newValue)
|
||||
vectorConfiguration.updateApplicationTheme(newValue)
|
||||
// Restart the Activity
|
||||
activity?.let {
|
||||
// Note: recreate does not apply the color correctly
|
||||
it.startActivity(it.intent)
|
||||
it.finish()
|
||||
}
|
||||
@ -1359,7 +1364,7 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
|
||||
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
REQUEST_CALL_RINGTONE -> {
|
||||
REQUEST_CALL_RINGTONE -> {
|
||||
val callRingtoneUri: Uri? = data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
val thisActivity = activity
|
||||
if (callRingtoneUri != null && thisActivity != null) {
|
||||
@ -1368,9 +1373,9 @@ class VectorSettingsPreferencesFragment : VectorPreferenceFragment(), SharedPref
|
||||
}
|
||||
}
|
||||
REQUEST_E2E_FILE_REQUEST_CODE -> importKeys(data)
|
||||
REQUEST_NEW_PHONE_NUMBER -> refreshPhoneNumbersList()
|
||||
REQUEST_PHONEBOOK_COUNTRY -> onPhonebookCountryUpdate(data)
|
||||
REQUEST_LOCALE -> {
|
||||
REQUEST_NEW_PHONE_NUMBER -> refreshPhoneNumbersList()
|
||||
REQUEST_PHONEBOOK_COUNTRY -> onPhonebookCountryUpdate(data)
|
||||
REQUEST_LOCALE -> {
|
||||
activity?.let {
|
||||
startActivity(it.intent)
|
||||
it.finish()
|
||||
|
@ -55,7 +55,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)!!
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,20 +64,14 @@ object ThemeUtils {
|
||||
* @param aTheme the new theme
|
||||
*/
|
||||
fun setApplicationTheme(context: Context, aTheme: String) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit()
|
||||
.putString(APPLICATION_THEME_KEY, aTheme)
|
||||
.apply()
|
||||
|
||||
/* TODO
|
||||
when (aTheme) {
|
||||
THEME_DARK_VALUE -> VectorApp.getInstance().setTheme(R.style.AppTheme_Dark)
|
||||
THEME_BLACK_VALUE -> VectorApp.getInstance().setTheme(R.style.AppTheme_Black)
|
||||
THEME_STATUS_VALUE -> VectorApp.getInstance().setTheme(R.style.AppTheme_Status)
|
||||
else -> VectorApp.getInstance().setTheme(R.style.AppTheme_Light)
|
||||
THEME_DARK_VALUE -> context.setTheme(R.style.AppTheme_Dark)
|
||||
THEME_BLACK_VALUE -> context.setTheme(R.style.AppTheme_Black)
|
||||
THEME_STATUS_VALUE -> context.setTheme(R.style.AppTheme_Status)
|
||||
else -> context.setTheme(R.style.AppTheme_Light)
|
||||
}
|
||||
*/
|
||||
|
||||
// Clear the cache
|
||||
mColorByAttr.clear()
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user