/* Copyright 2018 Conny Duck * * This file is a part of Tusky. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ package com.keylesspalace.tusky.components.preference import android.content.Intent import android.os.Build import android.os.Bundle import android.util.Log import androidx.annotation.DrawableRes import androidx.preference.PreferenceFragmentCompat import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.AccountListActivity import com.keylesspalace.tusky.BuildConfig import com.keylesspalace.tusky.FiltersActivity import com.keylesspalace.tusky.R import com.keylesspalace.tusky.TabPreferenceActivity 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.PrefKeys import com.keylesspalace.tusky.settings.listPreference import com.keylesspalace.tusky.settings.makePreferenceScreen import com.keylesspalace.tusky.settings.preference import com.keylesspalace.tusky.settings.preferenceCategory import com.keylesspalace.tusky.settings.switchPreference import com.keylesspalace.tusky.util.ThemeUtils 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 import retrofit2.Call import retrofit2.Callback import retrofit2.Response import javax.inject.Inject class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject lateinit var accountManager: AccountManager @Inject lateinit var mastodonApi: MastodonApi @Inject lateinit var eventHub: EventHub override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 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 } } preference { setTitle(R.string.title_tab_preferences) setIcon(R.drawable.ic_tabs) setOnPreferenceClickListener { val intent = Intent(context, TabPreferenceActivity::class.java) activity?.startActivity(intent) activity?.overridePendingTransition( R.anim.slide_from_right, R.anim.slide_to_left ) true } } preference { setTitle(R.string.action_view_mutes) setIcon(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 } } 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 } } preference { setTitle(R.string.title_domain_mutes) setIcon(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 } } 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() setIcon(getIconForVisibility(visibility)) setOnPreferenceChangeListener { _, newValue -> setIcon(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) setIcon(getIconForSensitivity(sensitivity)) setOnPreferenceChangeListener { _, newValue -> setIcon(getIconForSensitivity(newValue as Boolean)) syncWithServer(sensitive = newValue) eventHub.dispatch(PreferenceChangedEvent(key)) true } } } 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.PUBLIC, R.string.pref_title_public_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) { mastodonApi.accountUpdateSource(visibility, sensitive) .enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { val account = response.body() if (response.isSuccessful && account != null) { accountManager.activeAccount?.let { it.defaultPostPrivacy = account.source?.privacy ?: Status.Visibility.PUBLIC it.defaultMediaSensitivity = account.source?.sensitive ?: false accountManager.saveAccount(it) } } else { Log.e("AccountPreferences", "failed updating settings on server") showErrorSnackbar(visibility, sensitive) } } override fun onFailure(call: Call, t: Throwable) { Log.e("AccountPreferences", "failed updating settings on server", t) showErrorSnackbar(visibility, sensitive) } }) } private fun showErrorSnackbar(visibility: String?, sensitive: Boolean?) { view?.let { view -> Snackbar.make(view, R.string.pref_failed_to_sync, Snackbar.LENGTH_LONG) .setAction(R.string.action_retry) { syncWithServer(visibility, sensitive) } .show() } } @DrawableRes private fun getIconForVisibility(visibility: Status.Visibility): Int { return when (visibility) { Status.Visibility.PRIVATE -> R.drawable.ic_lock_outline_24dp Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp else -> R.drawable.ic_public_24dp } } @DrawableRes private fun getIconForSensitivity(sensitive: Boolean): Int { return if (sensitive) { R.drawable.ic_hide_media_24dp } else { R.drawable.ic_eye_24dp } } 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) } companion object { fun newInstance() = AccountPreferencesFragment() } }