/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.fenix.settings import android.annotation.SuppressLint import android.os.Build import android.os.Build.VERSION.SDK_INT import android.os.Bundle import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.edit import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.utils.view.addToRadioGroup /** * Lets the user customize the UI. */ @Suppress("LargeClass", "TooManyFunctions") class CustomizationFragment : PreferenceFragmentCompat() { private lateinit var radioLightTheme: RadioButtonPreference private lateinit var radioDarkTheme: RadioButtonPreference private lateinit var radioAutoBatteryTheme: RadioButtonPreference private lateinit var radioFollowDeviceTheme: RadioButtonPreference override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.customization_preferences, rootKey) requirePreference(R.string.pref_key_strip_url).apply { isChecked = context.settings().shouldStripUrl onPreferenceChangeListener = SharedPreferenceUpdater() } } override fun onResume() { super.onResume() showToolbar(getString(R.string.preferences_customize)) setupPreferences() } private fun setupPreferences() { bindFollowDeviceTheme() bindDarkTheme() bindLightTheme() bindAutoBatteryTheme() setupRadioGroups() setupToolbarCategory() setupTabsTrayCategory() setupFabCategory() setupHomeCategory() setupGesturesCategory() setupAddonsCustomizationCategory() } private fun setupRadioGroups() { addToRadioGroup( radioLightTheme, radioDarkTheme, if (SDK_INT >= Build.VERSION_CODES.P) { radioFollowDeviceTheme } else { radioAutoBatteryTheme } ) } private fun bindLightTheme() { radioLightTheme = requirePreference(R.string.pref_key_light_theme) radioLightTheme.onClickListener { setNewTheme(AppCompatDelegate.MODE_NIGHT_NO) } } @SuppressLint("WrongConstant") // Suppressing erroneous lint warning about using MODE_NIGHT_AUTO_BATTERY, a likely library bug private fun bindAutoBatteryTheme() { radioAutoBatteryTheme = requirePreference(R.string.pref_key_auto_battery_theme) radioAutoBatteryTheme.onClickListener { setNewTheme(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY) } } private fun bindDarkTheme() { radioDarkTheme = requirePreference(R.string.pref_key_dark_theme) radioDarkTheme.onClickListener { requireContext().components.analytics.metrics.track( Event.DarkThemeSelected( Event.DarkThemeSelected.Source.SETTINGS ) ) setNewTheme(AppCompatDelegate.MODE_NIGHT_YES) } } private fun bindFollowDeviceTheme() { radioFollowDeviceTheme = requirePreference(R.string.pref_key_follow_device_theme) if (SDK_INT >= Build.VERSION_CODES.P) { radioFollowDeviceTheme.onClickListener { setNewTheme(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) } } } private fun setNewTheme(mode: Int) { if (AppCompatDelegate.getDefaultNightMode() == mode) return AppCompatDelegate.setDefaultNightMode(mode) activity?.recreate() with(requireComponents.core) { engine.settings.preferredColorScheme = getPreferredColorScheme() } requireComponents.useCases.sessionUseCases.reload.invoke() } private fun setupToolbarCategory() { val topPreference = requirePreference(R.string.pref_key_toolbar_top) topPreference.onClickListener { requireContext().components.analytics.metrics.track(Event.ToolbarPositionChanged( Event.ToolbarPositionChanged.Position.TOP )) } val bottomPreference = requirePreference(R.string.pref_key_toolbar_bottom) bottomPreference.onClickListener { requireContext().components.analytics.metrics.track(Event.ToolbarPositionChanged( Event.ToolbarPositionChanged.Position.BOTTOM )) } val toolbarPosition = requireContext().settings().toolbarPosition topPreference.setCheckedWithoutClickListener(toolbarPosition == ToolbarPosition.TOP) bottomPreference.setCheckedWithoutClickListener(toolbarPosition == ToolbarPosition.BOTTOM) addToRadioGroup(topPreference, bottomPreference) } private fun setupTabsTrayCategory() { requirePreference(R.string.pref_key_tabs_tray_top_tray).apply { isChecked = context.settings().useTopTabsTray onPreferenceChangeListener = SharedPreferenceUpdater() } requirePreference(R.string.pref_key_use_fullscreen_tabs_screen).apply { isChecked = context.settings().useFullScreenTabScreen onPreferenceChangeListener = SharedPreferenceUpdater() } val reverseOrderPref = requirePreference( R.string.pref_key_tabs_tray_reverse_tab_order).apply { if (context.settings().enableCompactTabs) { isChecked = false isEnabled = false } else { isChecked = context.settings().reverseTabOrderInTabsTray isEnabled = true } onPreferenceChangeListener = SharedPreferenceUpdater() } requirePreference(R.string.pref_key_tabs_tray_compact_tab).apply { isChecked = context.settings().enableCompactTabs onPreferenceChangeListener = Preference.OnPreferenceChangeListener { preference, newValue -> val newValueBoolean = newValue as Boolean preference.context.settings().preferences.edit { putBoolean(preference.key, newValueBoolean) if (newValueBoolean) { reverseOrderPref.isChecked = false putBoolean(getString(R.string.pref_key_tabs_tray_reverse_tab_order), false) } reverseOrderPref.isEnabled = !newValueBoolean } true } } } private fun setupFabCategory() { val fabPositionTop = requirePreference(R.string.pref_key_tabs_tray_fab_top_position).apply { if (context.settings().useNewTabFloatingActionButton) { isChecked = context.settings().placeNewTabFloatingActionButtonAtTop isEnabled = true } else { isChecked = false isEnabled = false } onPreferenceChangeListener = SharedPreferenceUpdater() } requirePreference(R.string.pref_key_tabs_tray_use_fab).apply { isChecked = context.settings().useNewTabFloatingActionButton onPreferenceChangeListener = Preference.OnPreferenceChangeListener { preference, newValue -> val newValueBoolean = newValue as Boolean preference.context.settings().preferences.edit { putBoolean(preference.key, newValueBoolean) if (!newValueBoolean) { fabPositionTop.isChecked = false putBoolean(getString(R.string.pref_key_tabs_tray_fab_top_position), false) } fabPositionTop.isEnabled = newValueBoolean } true } } } private fun setupHomeCategory() { requirePreference(R.string.pref_home_category).apply { isVisible = FeatureFlags.topFrecentSite } requirePreference(R.string.pref_key_enable_top_frecent_sites).apply { isVisible = FeatureFlags.topFrecentSite isChecked = context.settings().showTopFrecentSites onPreferenceChangeListener = SharedPreferenceUpdater() } } private fun setupGesturesCategory() { requirePreference(R.string.pref_key_website_pull_to_refresh).apply { isVisible = FeatureFlags.pullToRefreshEnabled isChecked = context.settings().isPullToRefreshEnabledInBrowser onPreferenceChangeListener = SharedPreferenceUpdater() } requirePreference(R.string.pref_key_dynamic_toolbar).apply { isChecked = context.settings().isDynamicToolbarEnabled onPreferenceChangeListener = SharedPreferenceUpdater() } requirePreference(R.string.pref_key_swipe_toolbar_switch_tabs).apply { isChecked = context.settings().isSwipeToolbarToSwitchTabsEnabled onPreferenceChangeListener = SharedPreferenceUpdater() } } private fun setupAddonsCustomizationCategory() { requirePreference(R.string.pref_key_addons_custom_account).apply { text = context.settings().customAddonsAccount onPreferenceChangeListener = SharedPreferenceUpdater() } requirePreference(R.string.pref_key_addons_custom_collection).apply { text = context.settings().customAddonsCollection onPreferenceChangeListener = SharedPreferenceUpdater() } } }