diff --git a/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java b/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java index cdf09002a..8837c7d8d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java +++ b/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java @@ -43,6 +43,18 @@ public class EmojiPreference extends Preference { private boolean updated, currentNeedsUpdate; + public EmojiPreference(Context context) { + super(context); + + // Find out which font is currently active + this.selected = EmojiCompatFont.byId(PreferenceManager + .getDefaultSharedPreferences(context) + .getInt(FONT_PREFERENCE, 0)); + // We'll use this later to determine if anything has changed + this.original = this.selected; + + setSummary(selected.getDisplay(context)); + } public EmojiPreference(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt index 3e54f7ce5..9fa6a6e54 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt @@ -19,21 +19,21 @@ import android.content.Intent import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle -import com.google.android.material.snackbar.Snackbar import android.util.Log -import android.view.View -import androidx.preference.* +import androidx.preference.PreferenceFragmentCompat +import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.* -import com.keylesspalace.tusky.R import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.components.instancemute.InstanceListActivity +import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.settings.* import com.keylesspalace.tusky.util.ThemeUtils import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial @@ -44,11 +44,7 @@ import retrofit2.Callback import retrofit2.Response import javax.inject.Inject - -class AccountPreferencesFragment : PreferenceFragmentCompat(), - Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener, - Injectable { - +class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject lateinit var accountManager: AccountManager @@ -58,182 +54,217 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), @Inject lateinit var eventHub: EventHub - private lateinit var notificationPreference: Preference - private lateinit var tabPreference: Preference - private lateinit var mutedUsersPreference: Preference - private lateinit var blockedUsersPreference: Preference - private lateinit var mutedDomainsPreference: Preference - - private lateinit var defaultPostPrivacyPreference: ListPreference - private lateinit var defaultMediaSensitivityPreference: SwitchPreferenceCompat - private lateinit var alwaysShowSensitiveMediaPreference: SwitchPreferenceCompat - private lateinit var alwaysOpenSpoilerPreference: SwitchPreferenceCompat - private lateinit var mediaPreviewEnabledPreference: SwitchPreferenceCompat - private lateinit var homeFiltersPreference: Preference - private lateinit var notificationFiltersPreference: Preference - private lateinit var publicFiltersPreference: Preference - private lateinit var threadFiltersPreference: Preference - private lateinit var accountFiltersPreference: Preference - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.account_preferences) - - notificationPreference = requirePreference("notificationPreference") - tabPreference = requirePreference("tabPreference") - mutedUsersPreference = requirePreference("mutedUsersPreference") - blockedUsersPreference = requirePreference("blockedUsersPreference") - mutedDomainsPreference = requirePreference("mutedDomainsPreference") - defaultPostPrivacyPreference = requirePreference("defaultPostPrivacy") as ListPreference - defaultMediaSensitivityPreference = requirePreference("defaultMediaSensitivity") as SwitchPreferenceCompat - mediaPreviewEnabledPreference = requirePreference("mediaPreviewEnabled") as SwitchPreferenceCompat - alwaysShowSensitiveMediaPreference = requirePreference("alwaysShowSensitiveMedia") as SwitchPreferenceCompat - alwaysOpenSpoilerPreference = requirePreference("alwaysOpenSpoiler") as SwitchPreferenceCompat - homeFiltersPreference = requirePreference("homeFilters") - notificationFiltersPreference = requirePreference("notificationFilters") - publicFiltersPreference = requirePreference("publicFilters") - threadFiltersPreference = requirePreference("threadFilters") - accountFiltersPreference = requirePreference("accountFilters") - - notificationPreference.icon = IconicsDrawable(notificationPreference.context, GoogleMaterial.Icon.gmd_notifications).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(notificationPreference.context, R.attr.iconColor) } - mutedUsersPreference.icon = getTintedIcon(R.drawable.ic_mute_24dp) - blockedUsersPreference.icon = IconicsDrawable(blockedUsersPreference.context, GoogleMaterial.Icon.gmd_block).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(blockedUsersPreference.context, R.attr.iconColor) } - mutedDomainsPreference.icon = getTintedIcon(R.drawable.ic_mute_24dp) - - notificationPreference.onPreferenceClickListener = this - tabPreference.onPreferenceClickListener = this - mutedUsersPreference.onPreferenceClickListener = this - blockedUsersPreference.onPreferenceClickListener = this - mutedDomainsPreference.onPreferenceClickListener = this - homeFiltersPreference.onPreferenceClickListener = this - notificationFiltersPreference.onPreferenceClickListener = this - publicFiltersPreference.onPreferenceClickListener = this - threadFiltersPreference.onPreferenceClickListener = this - accountFiltersPreference.onPreferenceClickListener = this - - defaultPostPrivacyPreference.onPreferenceChangeListener = this - defaultMediaSensitivityPreference.onPreferenceChangeListener = this - mediaPreviewEnabledPreference.onPreferenceChangeListener = this - alwaysShowSensitiveMediaPreference.onPreferenceChangeListener = this - alwaysOpenSpoilerPreference.onPreferenceChangeListener = this - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - accountManager.activeAccount?.let { - - defaultPostPrivacyPreference.value = it.defaultPostPrivacy.serverString() - defaultPostPrivacyPreference.icon = getIconForVisibility(it.defaultPostPrivacy) - - defaultMediaSensitivityPreference.isChecked = it.defaultMediaSensitivity - defaultMediaSensitivityPreference.icon = getIconForSensitivity(it.defaultMediaSensitivity) - - mediaPreviewEnabledPreference.isChecked = it.mediaPreviewEnabled - alwaysShowSensitiveMediaPreference.isChecked = it.alwaysShowSensitiveMedia - alwaysOpenSpoilerPreference.isChecked = it.alwaysOpenSpoiler - - } - } - - override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { - when (preference) { - defaultPostPrivacyPreference -> { - preference.icon = getIconForVisibility(Status.Visibility.byString(newValue as String)) - syncWithServer(visibility = newValue) - } - defaultMediaSensitivityPreference -> { - preference.icon = getIconForSensitivity(newValue as Boolean) - syncWithServer(sensitive = newValue) - } - mediaPreviewEnabledPreference -> { - accountManager.activeAccount?.let { - it.mediaPreviewEnabled = newValue as Boolean - accountManager.saveAccount(it) + val context = requireContext() + makePreferenceScreen { + preference { + setTitle(R.string.pref_title_edit_notification_settings) + icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_notifications).apply { + sizeRes = R.dimen.preference_icon_size + colorInt = ThemeUtils.getColor(context, R.attr.iconColor) + } + setOnPreferenceClickListener { + openNotificationPrefs() + true } } - alwaysShowSensitiveMediaPreference -> { - accountManager.activeAccount?.let { - it.alwaysShowSensitiveMedia = newValue as Boolean - accountManager.saveAccount(it) + + preference { + setTitle(R.string.title_tab_preferences) + setOnPreferenceClickListener { + val intent = Intent(context, TabPreferenceActivity::class.java) + activity?.startActivity(intent) + activity?.overridePendingTransition(R.anim.slide_from_right, + R.anim.slide_to_left) + true } } - alwaysOpenSpoilerPreference -> { - accountManager.activeAccount?.let { - it.alwaysOpenSpoiler = newValue as Boolean - accountManager.saveAccount(it) + + preference { + setTitle(R.string.action_view_mutes) + icon = getTintedIcon(R.drawable.ic_mute_24dp) + setOnPreferenceClickListener { + val intent = Intent(context, AccountListActivity::class.java) + intent.putExtra("type", AccountListActivity.Type.MUTES) + activity?.startActivity(intent) + activity?.overridePendingTransition(R.anim.slide_from_right, + R.anim.slide_to_left) + true } } - } - eventHub.dispatch(PreferenceChangedEvent(preference.key)) + preference { + setTitle(R.string.action_view_blocks) + icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_block).apply { + sizeRes = R.dimen.preference_icon_size + colorInt = ThemeUtils.getColor(context, R.attr.iconColor) + } + setOnPreferenceClickListener { + val intent = Intent(context, AccountListActivity::class.java) + intent.putExtra("type", AccountListActivity.Type.BLOCKS) + activity?.startActivity(intent) + activity?.overridePendingTransition(R.anim.slide_from_right, + R.anim.slide_to_left) + true + } + } - return true - } + preference { + setTitle(R.string.title_domain_mutes) + icon = getTintedIcon(R.drawable.ic_mute_24dp) + setOnPreferenceClickListener { + val intent = Intent(context, InstanceListActivity::class.java) + activity?.startActivity(intent) + activity?.overridePendingTransition(R.anim.slide_from_right, + R.anim.slide_to_left) + true + } + } - override fun onPreferenceClick(preference: Preference): Boolean { - - return when (preference) { - notificationPreference -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val intent = Intent() - intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" - intent.putExtra("android.provider.extra.APP_PACKAGE", BuildConfig.APPLICATION_ID) - startActivity(intent) - } else { - activity?.let { - val intent = PreferencesActivity.newIntent(it, PreferencesActivity.NOTIFICATION_PREFERENCES) - it.startActivity(intent) - it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + preferenceCategory(R.string.pref_publishing) { + listPreference { + setTitle(R.string.pref_default_post_privacy) + setEntries(R.array.post_privacy_names) + setEntryValues(R.array.post_privacy_values) + key = PrefKeys.DEFAULT_POST_PRIVACY + setSummaryProvider { entry } + val visibility = accountManager.activeAccount?.defaultPostPrivacy + ?: Status.Visibility.PUBLIC + value = visibility.serverString() + icon = getIconForVisibility(visibility) + setOnPreferenceChangeListener { _, newValue -> + icon = getIconForVisibility( + Status.Visibility.byString(newValue as String) + ) + syncWithServer(visibility = newValue) + eventHub.dispatch(PreferenceChangedEvent(key)) + true + } + } + + switchPreference { + setTitle(R.string.pref_default_media_sensitivity) + setIcon(R.drawable.ic_eye_24dp) + key = PrefKeys.DEFAULT_MEDIA_SENSITIVITY + isSingleLineTitle = false + val sensitivity = accountManager.activeAccount?.defaultMediaSensitivity + ?: false + setDefaultValue(sensitivity) + icon = getIconForSensitivity(sensitivity) + setOnPreferenceChangeListener { _, newValue -> + icon = getIconForSensitivity(newValue as Boolean) + syncWithServer(sensitive = newValue) + eventHub.dispatch(PreferenceChangedEvent(key)) + true } - } - true - } - tabPreference -> { - val intent = Intent(context, TabPreferenceActivity::class.java) - activity?.startActivity(intent) - activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - true - } - mutedUsersPreference -> { - val intent = Intent(context, AccountListActivity::class.java) - intent.putExtra("type", AccountListActivity.Type.MUTES) - activity?.startActivity(intent) - activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - true - } - blockedUsersPreference -> { - val intent = Intent(context, AccountListActivity::class.java) - intent.putExtra("type", AccountListActivity.Type.BLOCKS) - activity?.startActivity(intent) - activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - true - } - mutedDomainsPreference -> { - val intent = Intent(context, InstanceListActivity::class.java) - activity?.startActivity(intent) - activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - true - } - homeFiltersPreference -> { - launchFilterActivity(Filter.HOME, R.string.title_home) - } - notificationFiltersPreference -> { - launchFilterActivity(Filter.NOTIFICATIONS, R.string.title_notifications) - } - publicFiltersPreference -> { - launchFilterActivity(Filter.PUBLIC, R.string.pref_title_public_filter_keywords) - } - threadFiltersPreference -> { - launchFilterActivity(Filter.THREAD, R.string.pref_title_thread_filter_keywords) - } - accountFiltersPreference -> { - launchFilterActivity(Filter.ACCOUNT, R.string.title_accounts) } - else -> false + preferenceCategory(R.string.pref_title_timelines) { + switchPreference { + key = PrefKeys.MEDIA_PREVIEW_ENABLED + setTitle(R.string.pref_title_show_media_preview) + isSingleLineTitle = false + isChecked = accountManager.activeAccount?.mediaPreviewEnabled ?: true + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.mediaPreviewEnabled = newValue as Boolean } + eventHub.dispatch(PreferenceChangedEvent(key)) + true + } + } + + switchPreference { + key = PrefKeys.ALWAYS_SHOW_SENSITIVE_MEDIA + setTitle(R.string.pref_title_alway_show_sensitive_media) + isSingleLineTitle = false + isChecked = accountManager.activeAccount?.alwaysShowSensitiveMedia ?: false + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.alwaysShowSensitiveMedia = newValue as Boolean } + eventHub.dispatch(PreferenceChangedEvent(key)) + true + } + } + + switchPreference { + key = PrefKeys.ALWAYS_OPEN_SPOILER + setTitle(R.string.pref_title_alway_open_spoiler) + isSingleLineTitle = false + isChecked = accountManager.activeAccount?.alwaysOpenSpoiler ?: false + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.alwaysOpenSpoiler = newValue as Boolean } + eventHub.dispatch(PreferenceChangedEvent(key)) + true + } + } + } + + preferenceCategory(R.string.pref_title_timeline_filters) { + preference { + setTitle(R.string.pref_title_public_filter_keywords) + setOnPreferenceClickListener { + launchFilterActivity(Filter.THREAD, + R.string.pref_title_thread_filter_keywords) + true + } + } + + preference { + setTitle(R.string.title_notifications) + setOnPreferenceClickListener { + launchFilterActivity(Filter.NOTIFICATIONS, R.string.title_notifications) + true + } + } + + preference { + setTitle(R.string.title_home) + setOnPreferenceClickListener { + launchFilterActivity(Filter.HOME, R.string.title_home) + true + } + } + + preference { + setTitle(R.string.pref_title_thread_filter_keywords) + setOnPreferenceClickListener { + launchFilterActivity(Filter.THREAD, + R.string.pref_title_thread_filter_keywords) + true + } + } + + preference { + setTitle(R.string.title_accounts) + setOnPreferenceClickListener { + launchFilterActivity(Filter.ACCOUNT, R.string.title_accounts) + true + } + } + } } + } + private fun openNotificationPrefs() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val intent = Intent() + intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" + intent.putExtra("android.provider.extra.APP_PACKAGE", BuildConfig.APPLICATION_ID) + startActivity(intent) + } else { + activity?.let { + val intent = PreferencesActivity.newIntent(it, PreferencesActivity.NOTIFICATION_PREFERENCES) + it.startActivity(intent) + it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + } + + } + } + + private inline fun updateAccount(changer: (AccountEntity) -> Unit) { + accountManager.activeAccount?.let { account -> + changer(account) + accountManager.saveAccount(account) + } } private fun syncWithServer(visibility: String? = null, sensitive: Boolean? = null) { @@ -297,17 +328,15 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), return ThemeUtils.getTintedDrawable(requireContext(), iconId, R.attr.iconColor) } - private fun launchFilterActivity(filterContext: String, titleResource: Int): Boolean { + private fun launchFilterActivity(filterContext: String, titleResource: Int) { val intent = Intent(context, FiltersActivity::class.java) intent.putExtra(FiltersActivity.FILTERS_CONTEXT, filterContext) intent.putExtra(FiltersActivity.FILTERS_TITLE, getString(titleResource)) activity?.startActivity(intent) activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - return true } companion object { fun newInstance() = AccountPreferencesFragment() } - } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt index db01b0271..800ea7c1d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt @@ -16,80 +16,148 @@ package com.keylesspalace.tusky.fragment.preference import android.os.Bundle -import android.view.View -import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import androidx.preference.SwitchPreferenceCompat import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.components.notifications.NotificationHelper +import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.di.Injectable -import com.keylesspalace.tusky.components.notifications.NotificationHelper +import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.makePreferenceScreen +import com.keylesspalace.tusky.settings.preferenceCategory +import com.keylesspalace.tusky.settings.switchPreference import javax.inject.Inject -class NotificationPreferencesFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener, Injectable { +class NotificationPreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject lateinit var accountManager: AccountManager override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.notification_preferences) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val activeAccount = accountManager.activeAccount - - if (activeAccount != null) { - for (pair in mapOf( - "notificationsEnabled" to activeAccount.notificationsEnabled, - "notificationFilterMentions" to activeAccount.notificationsMentioned, - "notificationFilterFollows" to activeAccount.notificationsFollowed, - "notificationFilterFollowRequests" to activeAccount.notificationsFollowRequested, - "notificationFilterReblogs" to activeAccount.notificationsReblogged, - "notificationFilterFavourites" to activeAccount.notificationsFavorited, - "notificationFilterPolls" to activeAccount.notificationsPolls, - "notificationAlertSound" to activeAccount.notificationSound, - "notificationAlertVibrate" to activeAccount.notificationVibration, - "notificationAlertLight" to activeAccount.notificationLight - )) { - (requirePreference(pair.key) as SwitchPreferenceCompat).apply { - isChecked = pair.value - onPreferenceChangeListener = this@NotificationPreferencesFragment + val activeAccount = accountManager.activeAccount ?: return + val context = requireContext() + makePreferenceScreen { + switchPreference { + setTitle(R.string.pref_title_notifications_enabled) + key = PrefKeys.NOTIFICATIONS_ENABLED + isIconSpaceReserved = false + isChecked = activeAccount.notificationsEnabled + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsEnabled = newValue as Boolean } + if (NotificationHelper.areNotificationsEnabled(context, accountManager)) { + NotificationHelper.enablePullNotifications(context) + } else { + NotificationHelper.disablePullNotifications(context) + } + true } } - } - } - override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + preferenceCategory(R.string.pref_title_notification_filters) { category -> + category.dependency = PrefKeys.NOTIFICATIONS_ENABLED + category.isIconSpaceReserved = false - val activeAccount = accountManager.activeAccount - - if (activeAccount != null) { - when (preference.key) { - "notificationsEnabled" -> { - activeAccount.notificationsEnabled = newValue as Boolean - if (NotificationHelper.areNotificationsEnabled(preference.context, accountManager)) { - NotificationHelper.enablePullNotifications(preference.context) - } else { - NotificationHelper.disablePullNotifications(preference.context) + switchPreference { + setTitle(R.string.pref_title_notification_filter_follows) + key = PrefKeys.NOTIFICATIONS_FILTER_FOLLOWS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsFollowed + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsFollowed = newValue as Boolean } + true } } - "notificationFilterMentions" -> activeAccount.notificationsMentioned = newValue as Boolean - "notificationFilterFollows" -> activeAccount.notificationsFollowed = newValue as Boolean - "notificationFilterFollowRequests" -> activeAccount.notificationsFollowRequested = newValue as Boolean - "notificationFilterReblogs" -> activeAccount.notificationsReblogged = newValue as Boolean - "notificationFilterFavourites" -> activeAccount.notificationsFavorited = newValue as Boolean - "notificationFilterPolls" -> activeAccount.notificationsPolls = newValue as Boolean - "notificationAlertSound" -> activeAccount.notificationSound = newValue as Boolean - "notificationAlertVibrate" -> activeAccount.notificationVibration = newValue as Boolean - "notificationAlertLight" -> activeAccount.notificationLight = newValue as Boolean - } - accountManager.saveAccount(activeAccount) - return true - } - return false + switchPreference { + setTitle(R.string.pref_title_notification_filter_follow_requests) + key = PrefKeys.NOTIFICATION_FILTER_FOLLOW_REQUESTS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsFollowRequested + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsFollowRequested = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_filter_reblogs) + key = PrefKeys.NOTIFICATION_FILTER_REBLOGS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsReblogged + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsReblogged = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_filter_favourites) + key = PrefKeys.NOTIFICATION_FILTER_FAVS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsFavorited + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsFavorited = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_filter_poll) + key = PrefKeys.NOTIFICATION_FILTER_POLLS + isIconSpaceReserved = false + isChecked = activeAccount.notificationsPolls + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationsPolls = newValue as Boolean } + true + } + } + } + + preferenceCategory(R.string.pref_title_notification_alerts) { category -> + category.dependency = PrefKeys.NOTIFICATIONS_ENABLED + category.isIconSpaceReserved = false + + switchPreference { + setTitle(R.string.pref_title_notification_alert_sound) + key = PrefKeys.NOTIFICATION_ALERT_SOUND + isIconSpaceReserved = false + isChecked = activeAccount.notificationSound + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationSound = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_alert_vibrate) + key = PrefKeys.NOTIFICATION_ALERT_VIBRATE + isIconSpaceReserved = false + isChecked = activeAccount.notificationVibration + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationVibration = newValue as Boolean } + true + } + } + + switchPreference { + setTitle(R.string.pref_title_notification_alert_light) + key = PrefKeys.NOTIFICATION_ALERT_LIGHT + isIconSpaceReserved = false + isChecked = activeAccount.notificationLight + setOnPreferenceChangeListener { _, newValue -> + updateAccount { it.notificationLight = newValue as Boolean } + true + } + } + } + } + } + + private inline fun updateAccount(changer: (AccountEntity) -> Unit) { + accountManager.activeAccount?.let { account -> + changer(account) + accountManager.saveAccount(account) + } } companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt index 825275a1a..60e978111 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt @@ -20,58 +20,178 @@ import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.keylesspalace.tusky.PreferencesActivity import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.settings.* import com.keylesspalace.tusky.util.ThemeUtils import com.keylesspalace.tusky.util.getNonNullString import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt -import com.mikepenz.iconics.utils.sizeRes - -fun PreferenceFragmentCompat.requirePreference(key: String): Preference { - return findPreference(key)!! -} +import com.mikepenz.iconics.utils.sizePx class PreferencesFragment : PreferenceFragmentCompat() { + private val iconSize by lazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) } + private var httpProxyPref: Preference? = null + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + val context = requireContext() + makePreferenceScreen { + preferenceCategory(R.string.pref_title_appearance_settings) { + listPreference { + setDefaultValue(AppTheme.NIGHT.value) + setEntries(R.array.app_theme_names) + entryValues = AppTheme.stringValues() + key = PrefKeys.APP_THEME + setSummaryProvider { entry } + setTitle(R.string.pref_title_app_theme) + icon = makeIcon(GoogleMaterial.Icon.gmd_palette) + } - addPreferencesFromResource(R.xml.preferences) + emojiPreference { + setDefaultValue("system_default") + setIcon(R.drawable.ic_emoji_24dp) + key = PrefKeys.EMOJI + setSummary(R.string.system_default) + setTitle(R.string.emoji_style) + icon = makeIcon(GoogleMaterial.Icon.gmd_sentiment_satisfied) + } - val themePreference: Preference = requirePreference("appTheme") - themePreference.icon = IconicsDrawable(themePreference.context, GoogleMaterial.Icon.gmd_palette).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } + listPreference { + setDefaultValue("default") + setEntries(R.array.language_entries) + setEntryValues(R.array.language_values) + key = PrefKeys.LANGUAGE + setSummaryProvider { entry } + setTitle(R.string.pref_title_language) + icon = makeIcon(GoogleMaterial.Icon.gmd_translate) + } - val emojiPreference: Preference = requirePreference("emojiCompat") - emojiPreference.icon = IconicsDrawable(emojiPreference.context, GoogleMaterial.Icon.gmd_sentiment_satisfied).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } + listPreference { + setDefaultValue("medium") + setEntries(R.array.status_text_size_names) + setEntryValues(R.array.status_text_size_values) + key = PrefKeys.STATUS_TEXT_SIZE + setSummaryProvider { entry } + setTitle(R.string.pref_status_text_size) + icon = makeIcon(GoogleMaterial.Icon.gmd_format_size) + } - val textSizePreference: Preference = requirePreference("statusTextSize") - textSizePreference.icon = IconicsDrawable(textSizePreference.context, GoogleMaterial.Icon.gmd_format_size).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } + switchPreference { + setDefaultValue(false) + key = PrefKeys.FAB_HIDE + setTitle(R.string.pref_title_hide_follow_button) + isSingleLineTitle = false + } - val timelineFilterPreferences: Preference = requirePreference("timelineFilterPreferences") - timelineFilterPreferences.setOnPreferenceClickListener { - activity?.let { activity -> - val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.TAB_FILTER_PREFERENCES) - activity.startActivity(intent) - activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + switchPreference { + setDefaultValue(false) + key = PrefKeys.ABSOLUTE_TIME_VIEW + setTitle(R.string.pref_title_absolute_time) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(true) + key = PrefKeys.SHOW_BOT_OVERLAY + setTitle(R.string.pref_title_bot_overlay) + isSingleLineTitle = false + icon = ThemeUtils.getTintedDrawable( + context, + R.drawable.ic_bot_24dp, + R.attr.iconColor + ) + } + + switchPreference { + setDefaultValue(false) + key = PrefKeys.ANIMATE_GIF_AVATARS + setTitle(R.string.pref_title_animate_gif_avatars) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(true) + key = PrefKeys.USE_BLURHASH + setTitle(R.string.pref_title_gradient_for_media) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(true) + key = PrefKeys.SHOW_NOTIFICATIONS_FILTER + setTitle(R.string.pref_title_show_notifications_filter) + isSingleLineTitle = false + setOnPreferenceClickListener { + activity?.let { activity -> + val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.TAB_FILTER_PREFERENCES) + activity.startActivity(intent) + activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + } + true + } + } + + switchPreference { + setDefaultValue(false) + key = PrefKeys.SHOW_CARDS_IN_TIMELINES + setTitle(R.string.pref_title_confirm_reblogs) + isSingleLineTitle = false + } + + switchPreference { + setDefaultValue(true) + key = PrefKeys.ENABLE_SWIPE_FOR_TABS + setTitle(R.string.pref_title_enable_swipe_for_tabs) + isSingleLineTitle = false + } } - true + + preferenceCategory(R.string.pref_title_browser_settings) { + switchPreference { + setDefaultValue(false) + key = PrefKeys.CUSTOM_TABS + setTitle(R.string.pref_title_custom_tabs) + isSingleLineTitle = false + } + } + + preferenceCategory(R.string.pref_title_timeline_filters) { + preference { + setTitle(R.string.pref_title_status_tabs) + setOnPreferenceClickListener { + activity?.let { activity -> + val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.TAB_FILTER_PREFERENCES) + activity.startActivity(intent) + activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + } + true + } + } + } + + preferenceCategory(R.string.pref_title_proxy_settings) { + httpProxyPref = preference { + setTitle(R.string.pref_title_http_proxy_settings) + setOnPreferenceClickListener { + activity?.let { activity -> + val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.PROXY_PREFERENCES) + activity.startActivity(intent) + activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) + } + true + } + } + } + } + } + + private fun makeIcon(icon: GoogleMaterial.Icon): IconicsDrawable { + val context = requireContext() + return IconicsDrawable(context, icon).apply { + sizePx = iconSize + colorInt = ThemeUtils.getColor(context, R.attr.iconColor) } - val httpProxyPreferences: Preference = requirePreference("httpProxyPreferences") - httpProxyPreferences.setOnPreferenceClickListener { - activity?.let { activity -> - val intent = PreferencesActivity.newIntent(activity, PreferencesActivity.PROXY_PREFERENCES) - activity.startActivity(intent) - activity.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) - } - true - } - - val languagePreference: Preference = requirePreference("language") - languagePreference.icon = IconicsDrawable(languagePreference.context, GoogleMaterial.Icon.gmd_translate).apply { sizeRes = R.dimen.preference_icon_size; colorInt = ThemeUtils.getColor(themePreference.context, R.attr.iconColor) } - - val botIndicatorPreference = requirePreference("showBotOverlay") - - botIndicatorPreference.icon = ThemeUtils.getTintedDrawable(requireContext(), R.drawable.ic_bot_24dp, R.attr.iconColor) } override fun onResume() { @@ -80,28 +200,23 @@ class PreferencesFragment : PreferenceFragmentCompat() { } private fun updateHttpProxySummary() { - - val httpProxyPref: Preference = requirePreference("httpProxyPreferences") - val sharedPreferences = preferenceManager.sharedPreferences - - val httpProxyEnabled = sharedPreferences.getBoolean("httpProxyEnabled", false) - - val httpServer = sharedPreferences.getNonNullString("httpProxyServer", "") + val httpProxyEnabled = sharedPreferences.getBoolean(PrefKeys.HTTP_PROXY_ENABLED, false) + val httpServer = sharedPreferences.getNonNullString(PrefKeys.HTTP_PROXY_SERVER, "") try { - val httpPort = sharedPreferences.getNonNullString("httpProxyPort", "-1").toInt() + val httpPort = sharedPreferences.getNonNullString(PrefKeys.HTTP_PROXY_PORT, "-1") + .toInt() if (httpProxyEnabled && httpServer.isNotBlank() && httpPort > 0 && httpPort < 65535) { - httpProxyPref.summary = "$httpServer:$httpPort" + httpProxyPref?.summary = "$httpServer:$httpPort" return } } catch (e: NumberFormatException) { // user has entered wrong port, fall back to empty summary } - httpProxyPref.summary = "" - + httpProxyPref?.summary = "" } companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt index a4efff2d7..e7ee7ade0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt @@ -15,61 +15,53 @@ package com.keylesspalace.tusky.fragment.preference -import android.content.SharedPreferences import android.os.Bundle -import androidx.preference.EditTextPreference import androidx.preference.PreferenceFragmentCompat import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.editTextPreference +import com.keylesspalace.tusky.settings.makePreferenceScreen +import com.keylesspalace.tusky.settings.switchPreference import kotlin.system.exitProcess -class ProxyPreferencesFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { - +class ProxyPreferencesFragment : PreferenceFragmentCompat() { private var pendingRestart = false - private lateinit var sharedPreferences: SharedPreferences - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.http_proxy_preferences) + makePreferenceScreen { + switchPreference { + setTitle(R.string.pref_title_http_proxy_enable) + isIconSpaceReserved = false + key = PrefKeys.HTTP_PROXY_ENABLED + setDefaultValue(false) + } - sharedPreferences = preferenceManager.sharedPreferences + editTextPreference { + setTitle(R.string.pref_title_http_proxy_server) + key = PrefKeys.HTTP_PROXY_SERVER + isIconSpaceReserved = false + setSummaryProvider { text } + } - } + editTextPreference { + setTitle(R.string.pref_title_http_proxy_port) + key = PrefKeys.HTTP_PROXY_PORT + isIconSpaceReserved = false + setSummaryProvider { text } + } + } - override fun onResume() { - super.onResume() - - sharedPreferences.registerOnSharedPreferenceChangeListener(this) - - updateSummary("httpProxyServer") - updateSummary("httpProxyPort") } override fun onPause() { super.onPause() - - sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) - if (pendingRestart) { pendingRestart = false exitProcess(0) } } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { - updateSummary (key) - } - - private fun updateSummary(key: String) { - when (key) { - "httpProxyServer", "httpProxyPort" -> { - val editTextPreference = requirePreference(key) as EditTextPreference - editTextPreference.summary = editTextPreference.text - } - } - } - companion object { - fun newInstance(): ProxyPreferencesFragment { return ProxyPreferencesFragment() } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt index 8d92d54bd..cd76300da 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt @@ -15,22 +15,38 @@ package com.keylesspalace.tusky.fragment.preference -import android.content.SharedPreferences import android.os.Bundle import androidx.preference.PreferenceFragmentCompat import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.checkBoxPreference +import com.keylesspalace.tusky.settings.makePreferenceScreen +import com.keylesspalace.tusky.settings.preferenceCategory class TabFilterPreferencesFragment : PreferenceFragmentCompat() { - - private lateinit var sharedPreferences: SharedPreferences - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.timeline_filter_preferences) - sharedPreferences = preferenceManager.sharedPreferences + makePreferenceScreen { + preferenceCategory(R.string.title_home) { category -> + category.isIconSpaceReserved = false + + checkBoxPreference { + setTitle(R.string.pref_title_show_boosts) + key = PrefKeys.TAB_FILTER_HOME_BOOSTS + setDefaultValue(true) + isIconSpaceReserved = false + } + + checkBoxPreference { + setTitle(R.string.pref_title_show_replies) + key = PrefKeys.TAB_FILTER_HOME_REPLIES + setDefaultValue(false) + isIconSpaceReserved = false + } + } + } } companion object { - fun newInstance(): TabFilterPreferencesFragment { return TabFilterPreferencesFragment() } diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt new file mode 100644 index 000000000..ab60206fe --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt @@ -0,0 +1,56 @@ +package com.keylesspalace.tusky.settings + +enum class AppTheme(val value: String) { + NIGHT("night"), + DAY("day"), + BLACK("black"), + AUTO("auto"), + AUTO_SYSTEM("auto_system"); + + companion object { + fun stringValues() = values().map { it.value }.toTypedArray() + } +} + +object PrefKeys { + // Note: not all of these keys are actually used as SharedPreferences keys but we must give + // each preference a key for it to work. + + const val APP_THEME = "appTheme" + const val EMOJI = "emojiCompat" + const val FAB_HIDE = "fabHide" + const val LANGUAGE = "language" + const val STATUS_TEXT_SIZE = "statusTextSize" + const val ABSOLUTE_TIME_VIEW = "absoluteTimeView" + const val SHOW_BOT_OVERLAY = "showBotOverlay" + const val ANIMATE_GIF_AVATARS = "animateGifAvatars" + const val USE_BLURHASH = "useBlurhash" + const val SHOW_NOTIFICATIONS_FILTER = "showNotificationsFilter" + const val SHOW_CARDS_IN_TIMELINES = "showCardsInTimelines" + const val ENABLE_SWIPE_FOR_TABS = "enableSwipeForTabs" + + const val CUSTOM_TABS = "customTabs" + + const val HTTP_PROXY_ENABLED = "httpProxyEnabled" + const val HTTP_PROXY_SERVER = "httpProxyServer" + const val HTTP_PROXY_PORT = "httpProxyPort" + + const val DEFAULT_POST_PRIVACY = "defaultPostPrivacy" + const val DEFAULT_MEDIA_SENSITIVITY = "defaultMediaSensitivity" + const val MEDIA_PREVIEW_ENABLED = "mediaPreviewEnabled" + const val ALWAYS_SHOW_SENSITIVE_MEDIA = "alwaysShowSensitiveMedia" + const val ALWAYS_OPEN_SPOILER = "alwaysOpenSpoiler" + + const val NOTIFICATIONS_ENABLED = "notificationsEnabled" + const val NOTIFICATION_ALERT_LIGHT = "notificationAlertLight" + const val NOTIFICATION_ALERT_VIBRATE = "notificationAlertVibrate" + const val NOTIFICATION_ALERT_SOUND = "notificationAlertSound" + const val NOTIFICATION_FILTER_POLLS = "notificationFilterPolls" + const val NOTIFICATION_FILTER_FAVS = "notificationFilterFavourites" + const val NOTIFICATION_FILTER_REBLOGS = "notificationFilterReblogs" + const val NOTIFICATION_FILTER_FOLLOW_REQUESTS = "notificationFilterFollowRequests" + const val NOTIFICATIONS_FILTER_FOLLOWS = "notificationFilterFollows" + + const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeBoosts" + const val TAB_FILTER_HOME_BOOSTS = "tabFilterHomeReplies" +} diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt new file mode 100644 index 000000000..effad5370 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt @@ -0,0 +1,83 @@ +package com.keylesspalace.tusky.settings + +import android.content.Context +import androidx.annotation.StringRes +import androidx.preference.* +import com.keylesspalace.tusky.EmojiPreference + +class PreferenceParent( + val context: Context, + val addPref: (pref: Preference) -> Unit +) + +inline fun PreferenceParent.preference(builder: Preference.() -> Unit): Preference { + val pref = Preference(context) + addPref(pref) + builder(pref) + return pref +} + +inline fun PreferenceParent.listPreference(builder: ListPreference.() -> Unit): ListPreference { + val pref = ListPreference(context) + addPref(pref) + builder(pref) + return pref +} + +inline fun PreferenceParent.emojiPreference(builder: EmojiPreference.() -> Unit): EmojiPreference { + val pref = EmojiPreference(context) + addPref(pref) + builder(pref) + return pref +} + +inline fun PreferenceParent.switchPreference( + builder: SwitchPreference.() -> Unit +): SwitchPreference { + val pref = SwitchPreference(context) + addPref(pref) + builder(pref) + return pref +} + +inline fun PreferenceParent.editTextPreference( + builder: EditTextPreference.() -> Unit +): EditTextPreference { + val pref = EditTextPreference(context) + addPref(pref) + builder(pref) + return pref +} + +inline fun PreferenceParent.checkBoxPreference( + builder: CheckBoxPreference.() -> Unit +): CheckBoxPreference { + val pref = CheckBoxPreference(context) + addPref(pref) + builder(pref) + return pref +} + +inline fun PreferenceParent.preferenceCategory( + @StringRes title: Int, + builder: PreferenceParent.(PreferenceCategory) -> Unit +) { + val category = PreferenceCategory(context) + addPref(category) + category.setTitle(title) + val newParent = PreferenceParent(context) { category.addPreference(it) } + builder(newParent, category) +} + +inline fun PreferenceFragmentCompat.makePreferenceScreen( + builder: PreferenceParent.() -> Unit +): PreferenceScreen { + val context = requireContext() + val screen = preferenceManager.createPreferenceScreen(context) + val parent = PreferenceParent(context) { screen.addPreference(it) } + // For some functions (like dependencies) it's much easier for us if we attach screen first + // and change it later + preferenceScreen = screen + builder(parent) + return screen +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 91fa69c80..c48d7cb23 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -234,7 +234,7 @@ Language Show indicator for bots Animate GIF avatars - Show colorful gradients for hidden media + Show colorful gradients for hidden media Timeline filtering Tabs diff --git a/app/src/main/res/xml/account_preferences.xml b/app/src/main/res/xml/account_preferences.xml deleted file mode 100644 index ec4d2c2ae..000000000 --- a/app/src/main/res/xml/account_preferences.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/xml/http_proxy_preferences.xml b/app/src/main/res/xml/http_proxy_preferences.xml deleted file mode 100644 index fef294266..000000000 --- a/app/src/main/res/xml/http_proxy_preferences.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/notification_preferences.xml b/app/src/main/res/xml/notification_preferences.xml deleted file mode 100644 index fa03f8d1b..000000000 --- a/app/src/main/res/xml/notification_preferences.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml deleted file mode 100644 index dcb37b73c..000000000 --- a/app/src/main/res/xml/preferences.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/xml/timeline_filter_preferences.xml b/app/src/main/res/xml/timeline_filter_preferences.xml deleted file mode 100644 index bbe749d02..000000000 --- a/app/src/main/res/xml/timeline_filter_preferences.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - \ No newline at end of file