font scale setting screen (#6453)

This commit is contained in:
Nikita Fedrunov 2022-07-18 09:49:57 +02:00 committed by GitHub
parent cdbc197426
commit 79762d9133
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1195 additions and 221 deletions

1
changelog.d/5687.feature Normal file
View file

@ -0,0 +1 @@
Adds settings screen to change app font scale or enable using system setting

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/color_primary_alpha25" android:state_checked="true" android:state_enabled="false" />
<item android:color="?colorPrimary" android:state_checked="true" android:state_enabled="true" />
<item android:color="?vctr_content_quaternary"/>
</selector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?vctr_content_quaternary" android:state_enabled="false" />
<item android:color="?vctr_content_primary"/>
</selector>

View file

@ -21,7 +21,9 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import com.adevinta.android.barista.interaction.BaristaDialogInteractions.clickDialogNegativeButton
import im.vector.app.R
import im.vector.app.espresso.tools.waitUntilActivityVisible
import im.vector.app.espresso.tools.waitUntilViewVisible
import im.vector.app.features.settings.font.FontScaleSettingActivity
class SettingsPreferencesRobot {
@ -32,6 +34,8 @@ class SettingsPreferencesRobot {
clickOn(R.string.settings_theme)
clickDialogNegativeButton()
clickOn(R.string.font_size)
clickDialogNegativeButton()
waitUntilActivityVisible<FontScaleSettingActivity> {
pressBack()
}
}
}

View file

@ -347,6 +347,7 @@
<activity android:name=".features.poll.create.CreatePollActivity" />
<activity android:name=".features.location.LocationSharingActivity" />
<activity android:name=".features.location.live.map.LocationLiveMapViewActivity" />
<activity android:name=".features.settings.font.FontScaleSettingActivity"/>
<!-- Services -->

View file

@ -162,6 +162,7 @@ import im.vector.app.features.settings.devtools.GossipingEventsPaperTrailFragmen
import im.vector.app.features.settings.devtools.IncomingKeyRequestListFragment
import im.vector.app.features.settings.devtools.KeyRequestsFragment
import im.vector.app.features.settings.devtools.OutgoingKeyRequestListFragment
import im.vector.app.features.settings.font.FontScaleSettingFragment
import im.vector.app.features.settings.homeserver.HomeserverSettingsFragment
import im.vector.app.features.settings.ignored.VectorSettingsIgnoredUsersFragment
import im.vector.app.features.settings.legals.LegalsFragment
@ -586,6 +587,11 @@ interface FragmentModule {
@FragmentKey(HomeserverSettingsFragment::class)
fun bindHomeserverSettingsFragment(fragment: HomeserverSettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(FontScaleSettingFragment::class)
fun bindFontScaleSettingFragment(fragment: FontScaleSettingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsPinFragment::class)

View file

@ -90,6 +90,7 @@ import im.vector.app.features.settings.devtools.AccountDataViewModel
import im.vector.app.features.settings.devtools.GossipingEventsPaperTrailViewModel
import im.vector.app.features.settings.devtools.KeyRequestListViewModel
import im.vector.app.features.settings.devtools.KeyRequestViewModel
import im.vector.app.features.settings.font.FontScaleSettingViewModel
import im.vector.app.features.settings.homeserver.HomeserverSettingsViewModel
import im.vector.app.features.settings.ignored.IgnoredUsersViewModel
import im.vector.app.features.settings.legals.LegalsViewModel
@ -606,4 +607,9 @@ interface MavericksViewModelModule {
@IntoMap
@MavericksViewModelKey(LocationLiveMapViewModel::class)
fun locationLiveMapViewModelFactory(factory: LocationLiveMapViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(FontScaleSettingViewModel::class)
fun fontScaleSettingViewModelFactory(factory: FontScaleSettingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
}

View file

@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.di
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class DefaultPreferences
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class NamedGlobalScope

View file

@ -21,6 +21,7 @@ import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import android.content.res.Resources
import androidx.preference.PreferenceManager
import com.google.i18n.phonenumbers.PhoneNumberUtil
import dagger.Binds
import dagger.Module
@ -37,6 +38,8 @@ import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.BuildMeta
import im.vector.app.core.time.Clock
import im.vector.app.core.time.DefaultClock
import im.vector.app.core.utils.AndroidSystemSettingsProvider
import im.vector.app.core.utils.SystemSettingsProvider
import im.vector.app.features.analytics.AnalyticsConfig
import im.vector.app.features.analytics.AnalyticsTracker
import im.vector.app.features.analytics.VectorAnalytics
@ -48,6 +51,8 @@ import im.vector.app.features.navigation.Navigator
import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.SharedPrefPinCodeStore
import im.vector.app.features.room.VectorRoomDisplayNameFallbackProvider
import im.vector.app.features.settings.FontScalePreferences
import im.vector.app.features.settings.FontScalePreferencesImpl
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.ui.SharedPreferencesUiStateRepository
import im.vector.app.features.ui.UiStateRepository
@ -97,6 +102,12 @@ abstract class VectorBindModule {
@Binds
abstract fun bindEmojiSpanify(emojiCompatWrapper: EmojiCompatWrapper): EmojiSpanify
@Binds
abstract fun bindFontScale(fontScale: FontScalePreferencesImpl): FontScalePreferences
@Binds
abstract fun bindSystemSettingsProvide(provider: AndroidSystemSettingsProvider): SystemSettingsProvider
}
@InstallIn(SingletonComponent::class)
@ -200,4 +211,11 @@ object VectorStaticModule {
@Provides
@Singleton
fun providesBuildMeta() = BuildMeta()
@Provides
@Singleton
@DefaultPreferences
fun providesDefaultSharedPreferences(context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2022 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.app.core.epoxy
import android.util.TypedValue
import android.widget.CompoundButton
import android.widget.RadioButton
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.features.settings.FontScaleValue
@EpoxyModelClass
abstract class FontScaleItem : VectorEpoxyModel<FontScaleItem.Holder>(R.layout.item_font_scale) {
companion object {
const val MINIMAL_TEXT_SIZE_DP = 10f
}
@EpoxyAttribute var fontScale: FontScaleValue? = null
@EpoxyAttribute var selected: Boolean = true
@EpoxyAttribute var enabled: Boolean = true
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var checkChangeListener: CompoundButton.OnCheckedChangeListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
val context = holder.view.context
holder.textView.text = fontScale?.let {
context.resources.getString(it.nameResId)
}
val index = fontScale?.index ?: 0
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, MINIMAL_TEXT_SIZE_DP + index * 2)
holder.textView.isEnabled = enabled
holder.button.isChecked = selected
holder.button.isEnabled = enabled
holder.button.isClickable = enabled
holder.view.onClick {
holder.button.performClick()
}
holder.button.setOnCheckedChangeListener(checkChangeListener)
}
class Holder : VectorEpoxyHolder() {
val button by bind<RadioButton>(R.id.font_scale_radio_button)
val textView by bind<TextView>(R.id.font_scale_text)
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 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.app.core.epoxy
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@EpoxyModelClass
abstract class FontScaleSectionItem : VectorEpoxyModel<FontScaleSectionItem.Holder>(R.layout.item_font_scale_section) {
@EpoxyAttribute var sectionName: String = ""
override fun bind(holder: Holder) {
super.bind(holder)
holder.textView.text = sectionName
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.font_scale_section_name)
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2022 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.app.core.epoxy
import android.widget.CheckBox
import android.widget.CompoundButton
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@EpoxyModelClass
abstract class FontScaleUseSystemSettingsItem : VectorEpoxyModel<FontScaleUseSystemSettingsItem.Holder>(R.layout.item_font_scale_system) {
@EpoxyAttribute var useSystemSettings: Boolean = true
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var checkChangeListener: CompoundButton.OnCheckedChangeListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.checkBox.isChecked = useSystemSettings
holder.checkBox.setOnCheckedChangeListener(checkChangeListener)
holder.view.onClick {
holder.checkBox.performClick()
}
}
class Holder : VectorEpoxyHolder() {
val checkBox by bind<CheckBox>(R.id.font_scale_use_system_checkbox)
}
}

View file

@ -45,6 +45,7 @@ import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.viewbinding.ViewBinding
import com.airbnb.mvrx.MavericksView
import com.bumptech.glide.util.Util
@ -66,6 +67,7 @@ import im.vector.app.core.extensions.restart
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.extensions.singletonEntryPoint
import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.utils.AndroidSystemSettingsProvider
import im.vector.app.core.utils.ToolbarConfig
import im.vector.app.core.utils.toast
import im.vector.app.features.MainActivity
@ -82,7 +84,8 @@ import im.vector.app.features.rageshake.BugReportActivity
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.rageshake.RageShake
import im.vector.app.features.session.SessionListener
import im.vector.app.features.settings.FontScale
import im.vector.app.features.settings.FontScalePreferences
import im.vector.app.features.settings.FontScalePreferencesImpl
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.themes.ActivityOtherThemes
import im.vector.app.features.themes.ThemeUtils
@ -154,6 +157,10 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
@Inject
lateinit var rageShake: RageShake
@Inject
lateinit var fontScalePreferences: FontScalePreferences
lateinit var navigator: Navigator
private set
private lateinit var fragmentFactory: FragmentFactory
@ -172,7 +179,8 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
private val restorables = ArrayList<Restorable>()
override fun attachBaseContext(base: Context) {
val vectorConfiguration = VectorConfiguration(this)
val fontScalePreferences = FontScalePreferencesImpl(PreferenceManager.getDefaultSharedPreferences(base), AndroidSystemSettingsProvider(base))
val vectorConfiguration = VectorConfiguration(this, fontScalePreferences)
super.attachBaseContext(vectorConfiguration.getLocalisedContext(base))
}
@ -285,7 +293,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
* This method has to be called for the font size setting be supported correctly.
*/
private fun applyFontSize() {
resources.configuration.fontScale = FontScale.getFontScaleValue(this).scale
resources.configuration.fontScale = fontScalePreferences.getResolvedFontScaleValue().scale
@Suppress("DEPRECATION")
resources.updateConfiguration(resources.configuration, resources.displayMetrics)

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2022 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.app.core.utils
import android.content.Context
import android.provider.Settings
import javax.inject.Inject
/**
* A helper to get system settings.
*/
interface SystemSettingsProvider {
/**
* @return system setting for font scale
*/
fun getSystemFontScale(): Float
}
class AndroidSystemSettingsProvider @Inject constructor(
private val context: Context,
) : SystemSettingsProvider {
override fun getSystemFontScale(): Float {
return Settings.System.getFloat(context.contentResolver, Settings.System.FONT_SCALE)
}
}

View file

@ -21,7 +21,7 @@ import android.content.res.Configuration
import android.os.Build
import android.os.LocaleList
import androidx.annotation.RequiresApi
import im.vector.app.features.settings.FontScale
import im.vector.app.features.settings.FontScalePreferences
import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.themes.ThemeUtils
import timber.log.Timber
@ -31,7 +31,10 @@ import javax.inject.Inject
/**
* Handle locale configuration change, such as theme, font size and locale chosen by the user.
*/
class VectorConfiguration @Inject constructor(private val context: Context) {
class VectorConfiguration @Inject constructor(
private val context: Context,
private val fontScalePreferences: FontScalePreferences
) {
fun onConfigurationChanged() {
if (Locale.getDefault().toString() != VectorLocale.applicationLocale.toString()) {
@ -45,7 +48,7 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
fun applyToApplicationContext() {
val locale = VectorLocale.applicationLocale
val fontScale = FontScale.getFontScaleValue(context)
val fontScale = fontScalePreferences.getResolvedFontScaleValue()
Locale.setDefault(locale)
val config = Configuration(context.resources.configuration)
@ -69,7 +72,7 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
// create new configuration passing old configuration from original Context
val configuration = Configuration(context.resources.configuration)
configuration.fontScale = FontScale.getFontScaleValue(context).scale
configuration.fontScale = fontScalePreferences.getResolvedFontScaleValue().scale
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setLocaleForApi24(configuration, locale)
@ -105,7 +108,7 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
*/
fun getHash(): String {
return (VectorLocale.applicationLocale.toString() +
"_" + FontScale.getFontScaleValue(context).preferenceValue +
"_" + fontScalePreferences.getResolvedFontScaleValue().preferenceValue +
"_" + ThemeUtils.getApplicationTheme(context))
}
}

View file

@ -1,88 +0,0 @@
/*
* Copyright 2018 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.app.features.settings
import android.content.Context
import androidx.annotation.StringRes
import androidx.core.content.edit
import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
/**
* Object to manage the Font Scale choice of the user.
*/
object FontScale {
// Key for the SharedPrefs
private const val APPLICATION_FONT_SCALE_KEY = "APPLICATION_FONT_SCALE_KEY"
data class FontScaleValue(
val index: Int,
// Possible values for the SharedPrefs
val preferenceValue: String,
val scale: Float,
@StringRes
val nameResId: Int
)
private val fontScaleValues = listOf(
FontScaleValue(0, "FONT_SCALE_TINY", 0.70f, R.string.tiny),
FontScaleValue(1, "FONT_SCALE_SMALL", 0.85f, R.string.small),
FontScaleValue(2, "FONT_SCALE_NORMAL", 1.00f, R.string.normal),
FontScaleValue(3, "FONT_SCALE_LARGE", 1.15f, R.string.large),
FontScaleValue(4, "FONT_SCALE_LARGER", 1.30f, R.string.larger),
FontScaleValue(5, "FONT_SCALE_LARGEST", 1.45f, R.string.largest),
FontScaleValue(6, "FONT_SCALE_HUGE", 1.60f, R.string.huge)
)
private val normalFontScaleValue = fontScaleValues[2]
/**
* Get the font scale value from SharedPrefs. Init the SharedPrefs if necessary.
*
* @return the font scale value
*/
fun getFontScaleValue(context: Context): FontScaleValue {
val preferences = DefaultSharedPreferences.getInstance(context)
return if (APPLICATION_FONT_SCALE_KEY !in preferences) {
val fontScale = context.resources.configuration.fontScale
(fontScaleValues.firstOrNull { it.scale == fontScale } ?: normalFontScaleValue)
.also { preferences.edit { putString(APPLICATION_FONT_SCALE_KEY, it.preferenceValue) } }
} else {
val pref = preferences.getString(APPLICATION_FONT_SCALE_KEY, null)
fontScaleValues.firstOrNull { it.preferenceValue == pref } ?: normalFontScaleValue
}
}
fun updateFontScale(context: Context, index: Int) {
fontScaleValues.getOrNull(index)?.let {
saveFontScaleValue(context, it)
}
}
/**
* Store the font scale value.
*
* @param context the Android context
* @param fontScaleValue the font scale value to store
*/
private fun saveFontScaleValue(context: Context, fontScaleValue: FontScaleValue) {
DefaultSharedPreferences.getInstance(context)
.edit { putString(APPLICATION_FONT_SCALE_KEY, fontScaleValue.preferenceValue) }
}
}

View file

@ -0,0 +1,133 @@
/*
* Copyright 2018 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.app.features.settings
import android.content.SharedPreferences
import androidx.annotation.StringRes
import androidx.core.content.edit
import im.vector.app.R
import im.vector.app.core.di.DefaultPreferences
import im.vector.app.core.utils.SystemSettingsProvider
import javax.inject.Inject
/**
* Stores and returns font scale settings using shared preferences.
*/
interface FontScalePreferences {
/** Defines whether to use system settings for font scale or not.
* @param useSystem true to use system settings, false to use app settings
*/
fun setUseSystemScale(useSystem: Boolean)
/** Returns whether to use system settings for font scale or not.
* @return useSystem true to use system settings, false to use app settings
*/
fun getUseSystemScale(): Boolean
/** Returns font scale taking in account [useSystemScale] setting.
* @return App setting for font scale if [getUseSystemScale] returns false, system setting otherwise
*/
fun getResolvedFontScaleValue(): FontScaleValue
/** Returns persisted app font scale.
* @return app setting for font scale
*/
fun getAppFontScaleValue(): FontScaleValue
/** Sets and stores app font scale setting value.
* @param fontScaleValue value to be set and saved
*/
fun setFontScaleValue(fontScaleValue: FontScaleValue)
/** Returns list of all available font scale values.
* @return list of values
*/
fun getAvailableScales(): List<FontScaleValue>
}
/**
* Object to manage the Font Scale choice of the user.
*/
class FontScalePreferencesImpl @Inject constructor(
@DefaultPreferences private val preferences: SharedPreferences,
private val systemSettingsProvider: SystemSettingsProvider,
) : FontScalePreferences {
companion object {
private const val APPLICATION_FONT_SCALE_KEY = "APPLICATION_FONT_SCALE_KEY"
private const val APPLICATION_USE_SYSTEM_FONT_SCALE_KEY = "APPLICATION_USE_SYSTEM_FONT_SCALE_KEY"
}
private val fontScaleValues = listOf(
FontScaleValue(0, "FONT_SCALE_TINY", 0.70f, R.string.tiny),
FontScaleValue(1, "FONT_SCALE_SMALL", 0.85f, R.string.small),
FontScaleValue(2, "FONT_SCALE_NORMAL", 1.00f, R.string.normal),
FontScaleValue(3, "FONT_SCALE_LARGE", 1.15f, R.string.large),
FontScaleValue(4, "FONT_SCALE_LARGER", 1.30f, R.string.larger),
FontScaleValue(5, "FONT_SCALE_LARGEST", 1.45f, R.string.largest),
FontScaleValue(6, "FONT_SCALE_HUGE", 1.60f, R.string.huge)
)
private val normalFontScaleValue = fontScaleValues[2]
override fun getAppFontScaleValue(): FontScaleValue {
return if (APPLICATION_FONT_SCALE_KEY !in preferences) {
normalFontScaleValue
} else {
val pref = preferences.getString(APPLICATION_FONT_SCALE_KEY, null)
fontScaleValues.firstOrNull { it.preferenceValue == pref } ?: normalFontScaleValue
}
}
override fun getResolvedFontScaleValue(): FontScaleValue {
val useSystem = getUseSystemScale()
return if (useSystem) {
val fontScale = systemSettingsProvider.getSystemFontScale()
fontScaleValues.firstOrNull { it.scale == fontScale } ?: normalFontScaleValue
} else {
getAppFontScaleValue()
}
}
override fun setFontScaleValue(fontScaleValue: FontScaleValue) {
preferences
.edit()
.putString(APPLICATION_FONT_SCALE_KEY, fontScaleValue.preferenceValue)
.apply()
}
override fun getAvailableScales(): List<FontScaleValue> = fontScaleValues
override fun getUseSystemScale(): Boolean {
return preferences.getBoolean(APPLICATION_USE_SYSTEM_FONT_SCALE_KEY, true)
}
override fun setUseSystemScale(useSystem: Boolean) {
preferences
.edit { putBoolean(APPLICATION_USE_SYSTEM_FONT_SCALE_KEY, useSystem) }
}
}
data class FontScaleValue(
val index: Int,
// Possible values for the SharedPrefs
val preferenceValue: String,
val scale: Float,
@StringRes
val nameResId: Int
)

View file

@ -16,11 +16,9 @@
package im.vector.app.features.settings
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.CheckedTextView
import androidx.core.view.children
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -30,19 +28,18 @@ import im.vector.app.core.extensions.restart
import im.vector.app.core.preference.VectorListPreference
import im.vector.app.core.preference.VectorPreference
import im.vector.app.core.preference.VectorSwitchPreference
import im.vector.app.databinding.DialogSelectTextSizeBinding
import im.vector.app.features.MainActivity
import im.vector.app.features.MainActivityArgs
import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.settings.font.FontScaleSettingActivity
import im.vector.app.features.themes.ThemeUtils
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.presence.model.PresenceEnum
import javax.inject.Inject
class VectorSettingsPreferencesFragment @Inject constructor(
private val vectorConfiguration: VectorConfiguration,
private val vectorPreferences: VectorPreferences
private val vectorPreferences: VectorPreferences,
private val fontScalePreferences: FontScalePreferences,
) : VectorSettingsBaseFragment() {
override var titleRes = R.string.settings_preferences
@ -194,38 +191,11 @@ class VectorSettingsPreferencesFragment @Inject constructor(
selectedLanguagePreference.summary = VectorLocale.localeToLocalisedString(VectorLocale.applicationLocale)
// Text size
textSizePreference.summary = getString(FontScale.getFontScaleValue(requireActivity()).nameResId)
textSizePreference.summary = getString(fontScalePreferences.getResolvedFontScaleValue().nameResId)
textSizePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
activity?.let { displayTextSizeSelection(it) }
startActivity(Intent(activity, FontScaleSettingActivity::class.java))
true
}
}
private fun displayTextSizeSelection(activity: Activity) {
val layout = layoutInflater.inflate(R.layout.dialog_select_text_size, null)
val views = DialogSelectTextSizeBinding.bind(layout)
val dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.font_size)
.setView(layout)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.action_cancel, null)
.show()
val index = FontScale.getFontScaleValue(activity).index
views.textSelectionGroupView.children
.filterIsInstance(CheckedTextView::class.java)
.forEachIndexed { i, v ->
v.isChecked = i == index
v.debouncedClicks {
dialog.dismiss()
FontScale.updateFontScale(activity, i)
vectorConfiguration.applyToApplicationContext()
activity.restart()
}
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.settings.FontScaleValue
sealed class FontScaleSettingAction : VectorViewModelAction {
data class UseSystemSettingChangedAction(val useSystemSettings: Boolean) : FontScaleSettingAction()
data class FontScaleChangedAction(val fontScale: FontScaleValue) : FontScaleSettingAction()
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding
@AndroidEntryPoint
class FontScaleSettingActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(views.simpleFragmentContainer, FontScaleSettingFragment::class.java)
}
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R
import im.vector.app.core.epoxy.fontScaleItem
import im.vector.app.core.epoxy.fontScaleSectionItem
import im.vector.app.core.epoxy.fontScaleUseSystemSettingsItem
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.FontScaleValue
import javax.inject.Inject
class FontScaleSettingController @Inject constructor(
val stringProvider: StringProvider
) : TypedEpoxyController<FontScaleSettingViewState>() {
var callback: Callback? = null
override fun buildModels(data: FontScaleSettingViewState?) {
data?.let {
buildAutomaticallySection(data.useSystemSettings)
buildFontScaleItems(data.availableScaleOptions, data.persistedSettingIndex, data.useSystemSettings)
}
}
private fun buildAutomaticallySection(useSystemSettings: Boolean) {
val host = this
fontScaleSectionItem {
id("section_automatically")
sectionName(host.stringProvider.getString(R.string.font_size_section_auto))
}
fontScaleUseSystemSettingsItem {
id("use_system_settings")
useSystemSettings(useSystemSettings)
checkChangeListener { _, isChecked ->
host.callback?.onUseSystemSettingChanged(useSystemSettings = isChecked)
}
}
}
private fun buildFontScaleItems(scales: List<FontScaleValue>, persistedSettingIndex: Int, useSystemSettings: Boolean) {
val host = this
fontScaleSectionItem {
id("section_manually")
sectionName(host.stringProvider.getString(R.string.font_size_section_manually))
}
scales.forEachIndexed { index, scaleItem ->
fontScaleItem {
id(scaleItem.index)
fontScale(scaleItem)
selected(index == persistedSettingIndex)
enabled(!useSystemSettings)
checkChangeListener { _, isChecked ->
if (isChecked) {
host.callback?.oFontScaleSelected(fonScale = scaleItem)
}
}
}
}
}
interface Callback {
fun onUseSystemSettingChanged(useSystemSettings: Boolean)
fun oFontScaleSelected(fonScale: FontScaleValue)
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.restart
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentSettingsFontScalingBinding
import im.vector.app.features.settings.FontScaleValue
import javax.inject.Inject
class FontScaleSettingFragment @Inject constructor(
private val fontListController: FontScaleSettingController
) : VectorBaseFragment<FragmentSettingsFontScalingBinding>(), FontScaleSettingController.Callback {
private val viewModel: FontScaleSettingViewModel by fragmentViewModel()
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSettingsFontScalingBinding {
return FragmentSettingsFontScalingBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar(views.fontScaleToolbar)
.allowBack()
fontListController.callback = this
setupRecyclerView()
viewModel.observeViewEvents {
when (it) {
is FontScaleSettingViewEvents.RestartActivity -> {
requireActivity().restart()
}
}
}
}
private fun setupRecyclerView() {
views.fonsScaleRecycler.configureWith(fontListController)
}
override fun invalidate() = withState(viewModel) { state ->
fontListController.setData(state)
}
override fun onDestroyView() {
super.onDestroyView()
fontListController.callback = null
}
override fun onUseSystemSettingChanged(useSystemSettings: Boolean) {
viewModel.handle(FontScaleSettingAction.UseSystemSettingChangedAction(useSystemSettings))
}
override fun oFontScaleSelected(fonScale: FontScaleValue) {
viewModel.handle(FontScaleSettingAction.FontScaleChangedAction(fonScale))
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import im.vector.app.core.platform.VectorViewEvents
sealed class FontScaleSettingViewEvents : VectorViewEvents {
object RestartActivity : FontScaleSettingViewEvents()
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.settings.FontScalePreferences
class FontScaleSettingViewModel @AssistedInject constructor(
@Assisted initialState: FontScaleSettingViewState,
private val vectorConfiguration: VectorConfiguration,
private val fontScalePreferences: FontScalePreferences,
) : VectorViewModel<FontScaleSettingViewState, FontScaleSettingAction, FontScaleSettingViewEvents>(initialState) {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<FontScaleSettingViewModel, FontScaleSettingViewState> {
override fun create(initialState: FontScaleSettingViewState): FontScaleSettingViewModel
}
companion object : MavericksViewModelFactory<FontScaleSettingViewModel, FontScaleSettingViewState> by hiltMavericksViewModelFactory()
init {
setState {
copy(
availableScaleOptions = fontScalePreferences.getAvailableScales(),
useSystemSettings = fontScalePreferences.getUseSystemScale(),
persistedSettingIndex = fontScalePreferences.getAppFontScaleValue().index
)
}
}
override fun handle(action: FontScaleSettingAction) {
when (action) {
is FontScaleSettingAction.UseSystemSettingChangedAction -> handleUseSystemScale(action)
is FontScaleSettingAction.FontScaleChangedAction -> handleFontScaleChange(action)
}
}
private fun handleFontScaleChange(action: FontScaleSettingAction.FontScaleChangedAction) {
setState {
copy(persistedSettingIndex = fontScalePreferences.getAvailableScales().indexOf(action.fontScale))
}
fontScalePreferences.setFontScaleValue(action.fontScale)
vectorConfiguration.applyToApplicationContext()
_viewEvents.post(FontScaleSettingViewEvents.RestartActivity)
}
private fun handleUseSystemScale(action: FontScaleSettingAction.UseSystemSettingChangedAction) {
setState {
copy(useSystemSettings = action.useSystemSettings)
}
val oldScale = fontScalePreferences.getResolvedFontScaleValue()
fontScalePreferences.setUseSystemScale(action.useSystemSettings)
val newScale = fontScalePreferences.getResolvedFontScaleValue()
if (oldScale != newScale) {
vectorConfiguration.applyToApplicationContext()
_viewEvents.post(FontScaleSettingViewEvents.RestartActivity)
}
}
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import com.airbnb.mvrx.MavericksState
import im.vector.app.features.settings.FontScaleValue
data class FontScaleSettingViewState(
val availableScaleOptions: List<FontScaleValue> = emptyList(),
val persistedSettingIndex: Int = 0,
val useSystemSettings: Boolean = true,
) : MavericksState

View file

@ -1,87 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/text_selection_group_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?dialogPreferredPadding"
android:paddingTop="12dp"
android:paddingEnd="?dialogPreferredPadding"
tools:ignore="SpUsage">
<!-- keep the sizes in dp -->
<CheckedTextView
android:id="@+id/text_selection_tiny_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:gravity="center_vertical"
android:text="@string/tiny"
android:textSize="10dp" />
<CheckedTextView
android:id="@+id/text_selection_small_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:gravity="center_vertical"
android:text="@string/small"
android:textSize="12dp" />
<CheckedTextView
android:id="@+id/text_selection_normal_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:gravity="center_vertical"
android:text="@string/normal"
android:textSize="14dp" />
<CheckedTextView
android:id="@+id/text_selection_large_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:gravity="center_vertical"
android:text="@string/large"
android:textSize="16dp" />
<CheckedTextView
android:id="@+id/text_selection_larger_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:gravity="center_vertical"
android:text="@string/larger"
android:textSize="18dp" />
<CheckedTextView
android:id="@+id/text_selection_largest_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:gravity="center_vertical"
android:text="@string/largest"
android:textSize="20dp" />
<CheckedTextView
android:id="@+id/text_selection_huge_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:gravity="center_vertical"
android:text="@string/huge"
android:textSize="22dp" />
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,38 @@
<?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="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/font_scale_toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:title="@string/font_size_title" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/fons_scale_recycler"
android:layout_width="0dp"
android:layout_height="0dp"
android:fastScrollEnabled="true"
android:overScrollMode="always"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
tools:listitem="@layout/item_font_scale" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,33 @@
<?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="64dp"
android:orientation="horizontal">
<RadioButton
android:id="@+id/font_scale_radio_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="18dp"
android:buttonTint="@color/radio_button_tint_selector"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/font_scale_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="56dp"
android:gravity="start|center_vertical"
android:textColor="@color/vector_content_primary_tint_selector"
android:textSize="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="SpUsage"
tools:text="Tiny" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">
<TextView
android:id="@+id/font_scale_section_name"
style="@style/Widget.Vector.TextView.Subtitle.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="18dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="4dp"
android:gravity="start"
android:textColor="?vctr_content_primary"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Automatically" />
</FrameLayout>

View file

@ -0,0 +1,32 @@
<?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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/font_scale_use_system_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="18dp"
android:checked="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/font_scale_use_system_text"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="22dp"
android:layout_marginStart="56dp"
android:layout_marginEnd="12dp"
android:gravity="start"
android:text="@string/font_size_use_system"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/font_scale_use_system_checkbox"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1263,6 +1263,10 @@
<string name="notification_ticker_text_group">%1$s: %2$s %3$s</string>
<!-- text size selection -->
<string name="font_size_title">Choose font size</string>
<string name="font_size_section_auto">Set automatically</string>
<string name="font_size_section_manually">Choose manually</string>
<string name="font_size_use_system">Use system default</string>
<string name="font_size">Font size</string>
<string name="tiny">Tiny</string>
<string name="small">Small</string>

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import im.vector.app.features.settings.FontScalePreferencesImpl
import im.vector.app.test.fakes.FakeSharedPreferences
import im.vector.app.test.fakes.FakeSystemSettingsProvider
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
class FontScalePreferencesTest {
private val fakeSharedPreferences = FakeSharedPreferences()
private val fakeSystemSettingsProvider = FakeSystemSettingsProvider()
private val fontScalePreferences = FontScalePreferencesImpl(
preferences = fakeSharedPreferences,
systemSettingsProvider = fakeSystemSettingsProvider
)
@Test
fun `given app setting is different from system setting and useSystemSetting is set to true, then returns system-level setting`() {
val scaleOptions = fontScalePreferences.getAvailableScales()
val tinyScale = scaleOptions[0]
val normalScale = scaleOptions[2]
fakeSharedPreferences.givenFontScaleIsSetTo(tinyScale)
fakeSharedPreferences.givenUseSystemFontScaleIsSetTo(true)
fakeSystemSettingsProvider.givenSystemFontScaleIs(normalScale.scale)
fontScalePreferences.getResolvedFontScaleValue() shouldBeEqualTo normalScale
}
@Test
fun `given app setting is different from system setting and useSystemSetting is set to false, then returns app-level setting`() {
val scaleOptions = fontScalePreferences.getAvailableScales()
val tinyScale = scaleOptions[0]
val normalScale = scaleOptions[2]
fakeSharedPreferences.givenFontScaleIsSetTo(tinyScale)
fakeSharedPreferences.givenUseSystemFontScaleIsSetTo(false)
fakeSystemSettingsProvider.givenSystemFontScaleIs(normalScale.scale)
fontScalePreferences.getResolvedFontScaleValue() shouldBeEqualTo tinyScale
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2022 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.app.features.settings.font
import com.airbnb.mvrx.test.MvRxTestRule
import im.vector.app.features.settings.FontScaleValue
import im.vector.app.test.fakes.FakeConfiguration
import im.vector.app.test.fakes.FakeFontScalePreferences
import im.vector.app.test.test
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
private val A_SELECTION = aFontScaleValue(index = 1)
private val A_SCALE_OPTIONS_WITH_SELECTION = listOf(
aFontScaleValue(index = 0),
A_SELECTION,
)
// our tests only make use of the index
private fun aFontScaleValue(index: Int) = FontScaleValue(index, "foo", -1f, 0)
class FontScaleSettingViewModelTest {
@get:Rule
val mvrxTestRule = MvRxTestRule()
private val fakeConfiguration = FakeConfiguration()
private val fakeFontScalePreferences = FakeFontScalePreferences()
private var initialState = FontScaleSettingViewState()
private lateinit var viewModel: FontScaleSettingViewModel
@Before
fun setUp() {
viewModelWith(initialState)
}
private fun viewModelWith(state: FontScaleSettingViewState) {
FontScaleSettingViewModel(
state,
fakeConfiguration.instance,
fakeFontScalePreferences
).also {
viewModel = it
initialState = state
}
}
@Test
fun `given useSystemSetting is false when handling FontScaleChangedAction, then changes state and emits RestartActivity event`() =
runTest {
fakeFontScalePreferences.givenAvailableScaleOptions(A_SCALE_OPTIONS_WITH_SELECTION)
viewModelWith(initialState)
val test = viewModel.test()
viewModel.handle(FontScaleSettingAction.FontScaleChangedAction(A_SELECTION))
test
.assertStatesChanges(
initialState.copy(availableScaleOptions = A_SCALE_OPTIONS_WITH_SELECTION),
{ copy(persistedSettingIndex = A_SELECTION.index) }
)
.assertEvents(FontScaleSettingViewEvents.RestartActivity)
.finish()
fakeFontScalePreferences.verifyAppScaleFontValue(A_SELECTION)
}
@Test
fun `given app and system font scale are different when handling UseSystemSettingChangedAction, then changes state and emits RestartActivity event`() =
runTest {
fakeFontScalePreferences.givenAvailableScaleOptions(A_SCALE_OPTIONS_WITH_SELECTION)
viewModelWith(initialState)
val test = viewModel.test()
fakeFontScalePreferences.givenAppSettingIsDifferentFromSystemSetting()
val newValue = false
viewModel.handle(FontScaleSettingAction.UseSystemSettingChangedAction(newValue))
test
.assertStatesChanges(
initialState.copy(availableScaleOptions = A_SCALE_OPTIONS_WITH_SELECTION),
{ copy(useSystemSettings = newValue) }
)
.assertEvents(FontScaleSettingViewEvents.RestartActivity)
.finish()
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2022 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.app.test.fakes
import im.vector.app.features.configuration.VectorConfiguration
import io.mockk.every
import io.mockk.mockk
class FakeConfiguration {
val instance = mockk<VectorConfiguration> {
every { applyToApplicationContext() } returns Unit
}
}

View file

@ -33,6 +33,7 @@ class FakeContext(
init {
every { instance.contentResolver } returns contentResolver
every { instance.applicationContext } returns instance
}
fun givenFileDescriptor(uri: Uri, mode: String, factory: () -> ParcelFileDescriptor?) {

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2022 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.app.test.fakes
import im.vector.app.R
import im.vector.app.features.settings.FontScalePreferences
import im.vector.app.features.settings.FontScaleValue
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
class FakeFontScalePreferences : FontScalePreferences by mockk(relaxUnitFun = true) {
private val fontScaleValues = listOf(
FontScaleValue(0, "FONT_SCALE_TINY", 0.70f, R.string.tiny),
FontScaleValue(1, "FONT_SCALE_SMALL", 0.85f, R.string.small),
FontScaleValue(2, "FONT_SCALE_NORMAL", 1.00f, R.string.normal),
FontScaleValue(3, "FONT_SCALE_LARGE", 1.15f, R.string.large),
FontScaleValue(4, "FONT_SCALE_LARGER", 1.30f, R.string.larger),
FontScaleValue(5, "FONT_SCALE_LARGEST", 1.45f, R.string.largest),
FontScaleValue(6, "FONT_SCALE_HUGE", 1.60f, R.string.huge)
)
init {
every { getAvailableScales() } returns fontScaleValues
every { getUseSystemScale() } returns true
every { getAppFontScaleValue() } returns fontScaleValues[0]
every { getResolvedFontScaleValue() } returns fontScaleValues[0]
}
fun givenAppSettingIsDifferentFromSystemSetting() {
every { getResolvedFontScaleValue() } returns fontScaleValues[2] andThen fontScaleValues[0]
}
fun verifyAppScaleFontValue(value: FontScaleValue) {
verify {
setFontScaleValue(value)
}
}
fun givenAvailableScaleOptions(availableFontScales: List<FontScaleValue>) {
every { getAvailableScales() } returns availableFontScales
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2022 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.app.test.fakes
import android.content.SharedPreferences
import im.vector.app.features.settings.FontScaleValue
import io.mockk.every
import io.mockk.mockk
class FakeSharedPreferences : SharedPreferences by mockk() {
fun givenFontScaleIsSetTo(fontScaleValue: FontScaleValue) {
every { contains("APPLICATION_FONT_SCALE_KEY") } returns true
every { getString("APPLICATION_FONT_SCALE_KEY", any()) } returns fontScaleValue.preferenceValue
}
fun givenUseSystemFontScaleIsSetTo(useSystemScale: Boolean) {
every { contains("APPLICATION_USE_SYSTEM_FONT_SCALE_KEY") } returns true
every { getBoolean("APPLICATION_USE_SYSTEM_FONT_SCALE_KEY", any()) } returns useSystemScale
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2022 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.app.test.fakes
import im.vector.app.core.utils.SystemSettingsProvider
import io.mockk.every
import io.mockk.mockk
class FakeSystemSettingsProvider : SystemSettingsProvider by mockk() {
fun givenSystemFontScaleIs(scale: Float) {
every { getSystemFontScale() } returns scale
}
}