diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt index 03a74458d..d5b4f8842 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt @@ -24,7 +24,6 @@ import android.widget.LinearLayout import androidx.appcompat.widget.SearchView import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels -import androidx.preference.PreferenceManager import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter @@ -35,7 +34,7 @@ import com.keylesspalace.tusky.databinding.ItemFollowRequestBinding import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.entity.TimelineAccount -import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.BindingHolder import com.keylesspalace.tusky.util.Either import com.keylesspalace.tusky.util.emojify @@ -56,6 +55,9 @@ class AccountsInListFragment : DialogFragment(), Injectable { @Inject lateinit var viewModelFactory: ViewModelFactory + @Inject + lateinit var prefs: Prefs + private val viewModel: AccountsInListViewModel by viewModels { viewModelFactory } private val binding by viewBinding(FragmentAccountsInListBinding::bind) @@ -65,9 +67,8 @@ class AccountsInListFragment : DialogFragment(), Injectable { private val searchAdapter = SearchAdapter() private val radius by lazy { resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) } - private val pm by lazy { PreferenceManager.getDefaultSharedPreferences(requireContext()) } - private val animateAvatar by lazy { pm.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false) } - private val animateEmojis by lazy { pm.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) } + private val animateAvatar by lazy { prefs.animateAvatars } + private val animateEmojis by lazy { prefs.animateEmojis } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index d34dd6df8..e5a2fea3c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -18,7 +18,6 @@ package com.keylesspalace.tusky; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -34,7 +33,6 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; -import androidx.preference.PreferenceManager; import com.google.android.material.snackbar.Snackbar; import com.keylesspalace.tusky.adapter.AccountSelectionAdapter; @@ -44,6 +42,7 @@ import com.keylesspalace.tusky.db.AccountManager; import com.keylesspalace.tusky.di.Injectable; import com.keylesspalace.tusky.interfaces.AccountSelectionListener; import com.keylesspalace.tusky.interfaces.PermissionRequester; +import com.keylesspalace.tusky.settings.Prefs; import com.keylesspalace.tusky.util.ThemeUtils; import java.util.ArrayList; @@ -57,6 +56,9 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab @Inject public AccountManager accountManager; + @Inject + public Prefs prefs; + private static final int REQUESTER_NONE = Integer.MAX_VALUE; private HashMap requesters; @@ -64,25 +66,24 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - /* There isn't presently a way to globally change the theme of a whole application at - * runtime, just individual activities. So, each activity has to set its theme before any - * views are created. */ - String theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT); + // There isn't presently a way to globally change the theme of a whole application at + // runtime, just individual activities. So, each activity has to set its theme before any + // views are created. + String theme = prefs.getAppTheme(); Log.d("activeTheme", theme); if (theme.equals("black")) { setTheme(R.style.TuskyBlackTheme); } - /* set the taskdescription programmatically, the theme would turn it blue */ + // set the task description programmatically, the theme would turn it blue String appName = getString(R.string.app_name); Bitmap appIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); int recentsBackgroundColor = ThemeUtils.getColor(this, R.attr.colorSurface); setTaskDescription(new ActivityManager.TaskDescription(appName, appIcon, recentsBackgroundColor)); - int style = textStyle(preferences.getString("statusTextSize", "medium")); + int style = textStyle(prefs.getStatusTextSize()); getTheme().applyStyle(style, false); if(requiresLogin()) { @@ -189,7 +190,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab if (!showActiveAccount && activeAccount != null) { accounts.remove(activeAccount); } - AccountSelectionAdapter adapter = new AccountSelectionAdapter(this); + AccountSelectionAdapter adapter = new AccountSelectionAdapter(this, this.prefs); adapter.addAll(accounts); new AlertDialog.Builder(this) diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt index 2e9f6f3de..06d001ffd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt @@ -40,7 +40,6 @@ import androidx.emoji.text.EmojiCompat import androidx.emoji.text.EmojiCompat.InitCallback import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope -import androidx.preference.PreferenceManager import androidx.viewpager2.widget.MarginPageTransformer import autodispose2.androidx.lifecycle.autoDispose import com.bumptech.glide.Glide @@ -78,6 +77,7 @@ import com.keylesspalace.tusky.interfaces.ActionButtonActivity import com.keylesspalace.tusky.interfaces.ReselectableFragment import com.keylesspalace.tusky.pager.MainPagerAdapter import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.ThemeUtils import com.keylesspalace.tusky.util.deleteStaleCachedMedia import com.keylesspalace.tusky.util.emojify @@ -136,6 +136,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje @Inject lateinit var draftHelper: DraftHelper + @Inject + lateinit var prefs: Prefs + private val binding by viewBinding(ActivityMainBinding::inflate) private lateinit var header: AccountHeaderView @@ -145,8 +148,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje private var unreadAnnouncementsCount = 0 - private val preferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) } - private lateinit var glide: RequestManager private var accountLocked: Boolean = false @@ -215,7 +216,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje showNotificationTab = true } } - window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own + window.statusBarColor = + Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own setContentView(binding.root) glide = Glide.with(this) @@ -225,7 +227,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje startActivity(composeIntent) } - val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false) + val hideTopToolbar = prefs.hideTopToolbar binding.mainToolbar.visible(!hideTopToolbar) loadDrawerAvatar(activeAccount.profilePictureUrl, true) @@ -362,7 +364,12 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje header = AccountHeaderView(this).apply { headerBackgroundScaleType = ImageView.ScaleType.CENTER_CROP currentHiddenInList = true - onAccountHeaderListener = { _: View?, profile: IProfile, current: Boolean -> handleProfileClick(profile, current) } + onAccountHeaderListener = { _: View?, profile: IProfile, current: Boolean -> + handleProfileClick( + profile, + current + ) + } addProfile( ProfileSettingDrawerItem().apply { identifier = DRAWER_ITEM_ADD_ACCOUNT @@ -377,9 +384,19 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje closeDrawerOnProfileListClick = true } - header.accountHeaderBackground.setColorFilter(ContextCompat.getColor(this, R.color.headerBackgroundFilter)) - header.accountHeaderBackground.setBackgroundColor(ThemeUtils.getColor(this, R.attr.colorBackgroundAccent)) - val animateAvatars = preferences.getBoolean("animateGifAvatars", false) + header.accountHeaderBackground.setColorFilter( + ContextCompat.getColor( + this, + R.color.headerBackgroundFilter + ) + ) + header.accountHeaderBackground.setBackgroundColor( + ThemeUtils.getColor( + this, + R.attr.colorBackgroundAccent + ) + ) + val animateAvatars = prefs.animateAvatars DrawerImageLoader.init(object : AbstractDrawerImageLoader() { override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) { @@ -440,7 +457,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje nameRes = R.string.action_view_follow_requests iconicsIcon = GoogleMaterial.Icon.gmd_person_add onClick = { - val intent = AccountListActivity.newIntent(context, AccountListActivity.Type.FOLLOW_REQUESTS, accountLocked = accountLocked) + val intent = AccountListActivity.newIntent( + context, + AccountListActivity.Type.FOLLOW_REQUESTS, + accountLocked = accountLocked + ) startActivityWithSlideInAnimation(intent) } }, @@ -474,8 +495,18 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje startActivityWithSlideInAnimation(AnnouncementsActivity.newIntent(context)) } badgeStyle = BadgeStyle().apply { - textColor = ColorHolder.fromColor(ThemeUtils.getColor(this@MainActivity, R.attr.colorOnPrimary)) - color = ColorHolder.fromColor(ThemeUtils.getColor(this@MainActivity, R.attr.colorPrimary)) + textColor = ColorHolder.fromColor( + ThemeUtils.getColor( + this@MainActivity, + R.attr.colorOnPrimary + ) + ) + color = ColorHolder.fromColor( + ThemeUtils.getColor( + this@MainActivity, + R.attr.colorPrimary + ) + ) } }, DividerDrawerItem(), @@ -483,7 +514,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje nameRes = R.string.action_view_account_preferences iconRes = R.drawable.ic_account_settings onClick = { - val intent = PreferencesActivity.newIntent(context, PreferencesActivity.ACCOUNT_PREFERENCES) + val intent = PreferencesActivity.newIntent( + context, + PreferencesActivity.ACCOUNT_PREFERENCES + ) startActivityWithSlideInAnimation(intent) } }, @@ -491,7 +525,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje nameRes = R.string.action_view_preferences iconicsIcon = GoogleMaterial.Icon.gmd_settings onClick = { - val intent = PreferencesActivity.newIntent(context, PreferencesActivity.GENERAL_PREFERENCES) + val intent = PreferencesActivity.newIntent( + context, + PreferencesActivity.GENERAL_PREFERENCES + ) startActivityWithSlideInAnimation(intent) } }, @@ -544,16 +581,18 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje private fun setupTabs(selectNotificationTab: Boolean) { - val activeTabLayout = if (preferences.getString("mainNavPosition", "top") == "bottom") { + val activeTabLayout = if (prefs.mainNavPosition == "bottom") { val actionBarSize = ThemeUtils.getDimension(this, R.attr.actionBarSize) val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin) - (binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin + (binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = + actionBarSize + fabMargin binding.tabLayout.hide() binding.bottomTabLayout } else { binding.bottomNav.hide() (binding.viewPager.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0 - (binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = R.id.viewPager + (binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = + R.id.viewPager binding.tabLayout } @@ -561,7 +600,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje val adapter = MainPagerAdapter(tabs, this) binding.viewPager.adapter = adapter - TabLayoutMediator(activeTabLayout, binding.viewPager) { _: TabLayout.Tab?, _: Int -> }.attach() + TabLayoutMediator( + activeTabLayout, + binding.viewPager + ) { _: TabLayout.Tab?, _: Int -> }.attach() activeTabLayout.removeAllTabs() for (i in tabs.indices) { val tab = activeTabLayout.newTab() @@ -584,8 +626,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin) binding.viewPager.setPageTransformer(MarginPageTransformer(pageMargin)) - val enableSwipeForTabs = preferences.getBoolean("enableSwipeForTabs", true) - binding.viewPager.isUserInputEnabled = enableSwipeForTabs + binding.viewPager.isUserInputEnabled = prefs.enableSwipeForTabs onTabSelectedListener?.let { activeTabLayout.removeOnTabSelectedListener(it) @@ -594,7 +635,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje onTabSelectedListener = object : OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { if (tab.position == notificationTabPosition) { - NotificationHelper.clearNotificationsForActiveAccount(this@MainActivity, accountManager) + NotificationHelper.clearNotificationsForActiveAccount( + this@MainActivity, + accountManager + ) } binding.mainToolbar.title = tabs[tab.position].title(this@MainActivity) @@ -660,7 +704,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje .setMessage(getString(R.string.action_logout_confirm, activeAccount.fullName)) .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> lifecycleScope.launch { - NotificationHelper.deleteNotificationChannelsForAccount(activeAccount, this@MainActivity) + NotificationHelper.deleteNotificationChannelsForAccount( + activeAccount, + this@MainActivity + ) cacheUpdater.clearForUser(activeAccount.id) conversationRepository.deleteCacheForAccount(activeAccount.id) draftHelper.deleteAllDraftsAndAttachmentsForAccount(activeAccount.id) @@ -709,7 +756,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje loadDrawerAvatar(me.avatar, false) accountManager.updateActiveAccount(me) - NotificationHelper.createNotificationChannelsForAccount(accountManager.activeAccount!!, this) + NotificationHelper.createNotificationChannelsForAccount( + accountManager.activeAccount!!, + this + ) accountLocked = me.locked @@ -720,7 +770,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje private fun loadDrawerAvatar(avatarUrl: String, showPlaceholder: Boolean) { val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size) - val animateAvatars = preferences.getBoolean("animateGifAvatars", false) + val animateAvatars = prefs.animateAvatars if (animateAvatars) { glide.asDrawable() @@ -737,20 +787,26 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje override fun onLoadStarted(placeholder: Drawable?) { if (placeholder != null) { - binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize) + binding.mainToolbar.navigationIcon = + FixedSizeDrawable(placeholder, navIconSize, navIconSize) } } - override fun onResourceReady(resource: Drawable, transition: Transition?) { + override fun onResourceReady( + resource: Drawable, + transition: Transition? + ) { if (resource is Animatable) { resource.start() } - binding.mainToolbar.navigationIcon = FixedSizeDrawable(resource, navIconSize, navIconSize) + binding.mainToolbar.navigationIcon = + FixedSizeDrawable(resource, navIconSize, navIconSize) } override fun onLoadCleared(placeholder: Drawable?) { if (placeholder != null) { - binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize) + binding.mainToolbar.navigationIcon = + FixedSizeDrawable(placeholder, navIconSize, navIconSize) } } }) @@ -769,17 +825,26 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje override fun onLoadStarted(placeholder: Drawable?) { if (placeholder != null) { - binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize) + binding.mainToolbar.navigationIcon = + FixedSizeDrawable(placeholder, navIconSize, navIconSize) } } - override fun onResourceReady(resource: Bitmap, transition: Transition?) { - binding.mainToolbar.navigationIcon = FixedSizeDrawable(BitmapDrawable(resources, resource), navIconSize, navIconSize) + override fun onResourceReady( + resource: Bitmap, + transition: Transition? + ) { + binding.mainToolbar.navigationIcon = FixedSizeDrawable( + BitmapDrawable(resources, resource), + navIconSize, + navIconSize + ) } override fun onLoadCleared(placeholder: Drawable?) { if (placeholder != null) { - binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize) + binding.mainToolbar.navigationIcon = + FixedSizeDrawable(placeholder, navIconSize, navIconSize) } } }) @@ -802,23 +867,28 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje } private fun updateAnnouncementsBadge() { - binding.mainDrawer.updateBadge(DRAWER_ITEM_ANNOUNCEMENTS, StringHolder(if (unreadAnnouncementsCount <= 0) null else unreadAnnouncementsCount.toString())) + binding.mainDrawer.updateBadge( + DRAWER_ITEM_ANNOUNCEMENTS, + StringHolder(if (unreadAnnouncementsCount <= 0) null else unreadAnnouncementsCount.toString()) + ) } private fun updateProfiles() { - val animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) - val profiles: MutableList = accountManager.getAllAccountsOrderedByActive().map { acc -> - val emojifiedName = EmojiCompat.get().process(acc.displayName.emojify(acc.emojis, header, animateEmojis)) + val animateEmojis = prefs.animateEmojis + val profiles: MutableList = + accountManager.getAllAccountsOrderedByActive().map { acc -> + val emojifiedName = EmojiCompat.get() + .process(acc.displayName.emojify(acc.emojis, header, animateEmojis)) - ProfileDrawerItem().apply { - isSelected = acc.isActive - nameText = emojifiedName - iconUrl = acc.profilePictureUrl - isNameShown = true - identifier = acc.id - descriptionText = acc.fullName - } - }.toMutableList() + ProfileDrawerItem().apply { + isSelected = acc.isActive + nameText = emojifiedName + iconUrl = acc.profilePictureUrl + isNameShown = true + identifier = acc.id + descriptionText = acc.fullName + } + }.toMutableList() // reuse the already existing "add account" item for (profile in header.profiles.orEmpty()) { diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt index 0339a7bcc..e16215352 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt @@ -26,6 +26,7 @@ import autodispose2.AutoDisposePlugins import com.keylesspalace.tusky.components.notifications.NotificationWorkerFactory import com.keylesspalace.tusky.di.AppInjector import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.EmojiCompatFont import com.keylesspalace.tusky.util.LocaleManager import com.keylesspalace.tusky.util.ThemeUtils @@ -44,6 +45,9 @@ class TuskyApplication : Application(), HasAndroidInjector { @Inject lateinit var notificationWorkerFactory: NotificationWorkerFactory + @Inject + lateinit var prefs: Prefs + override fun onCreate() { // Uncomment me to get StrictMode violation logs // if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { @@ -63,17 +67,16 @@ class TuskyApplication : Application(), HasAndroidInjector { AppInjector.init(this) - val preferences = PreferenceManager.getDefaultSharedPreferences(this) // init the custom emoji fonts - val emojiSelection = preferences.getInt(PrefKeys.EMOJI, 0) + val emojiSelection = prefs.emojiFont val emojiConfig = EmojiCompatFont.byId(emojiSelection) .getConfig(this) .setReplaceAll(true) EmojiCompat.init(emojiConfig) // init night mode - val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT) + val theme = prefs.appTheme ThemeUtils.setAppNightMode(theme) RxJavaPlugins.setErrorHandler { @@ -89,7 +92,8 @@ class TuskyApplication : Application(), HasAndroidInjector { } override fun attachBaseContext(base: Context) { - localeManager = LocaleManager(base) + // special case: injected field cannot be injected here yet so we create Prefs by hand + localeManager = LocaleManager(Prefs(base)) super.attachBaseContext(localeManager.setLocale(base)) } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt index 4f58b1ffd..09c38b829 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt @@ -25,10 +25,14 @@ import com.keylesspalace.tusky.R import com.keylesspalace.tusky.databinding.ItemAutocompleteAccountBinding import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.emojify import com.keylesspalace.tusky.util.loadAvatar -class AccountSelectionAdapter(context: Context) : ArrayAdapter(context, R.layout.item_autocomplete_account) { +class AccountSelectionAdapter( + context: Context, + private val prefs: Prefs, +) : ArrayAdapter(context, R.layout.item_autocomplete_account) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val binding = if (convertView == null) { @@ -39,14 +43,15 @@ class AccountSelectionAdapter(context: Context) : ArrayAdapter(co val account = getItem(position) if (account != null) { - val pm = PreferenceManager.getDefaultSharedPreferences(binding.avatar.context) - val animateEmojis = pm.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + // TODO: is this even okay to do prefs things for each invocation here? + val animateEmojis = prefs.animateEmojis binding.username.text = account.fullName - binding.displayName.text = account.displayName.emojify(account.emojis, binding.displayName, animateEmojis) + binding.displayName.text = + account.displayName.emojify(account.emojis, binding.displayName, animateEmojis) val avatarRadius = context.resources.getDimensionPixelSize(R.dimen.avatar_radius_42dp) - val animateAvatar = pm.getBoolean("animateGifAvatars", false) + val animateAvatar = prefs.animateAvatars loadAvatar(account.profilePictureUrl, binding.avatar, avatarRadius, animateAvatar) } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java index 75a6a577e..22a94babb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java @@ -29,6 +29,7 @@ public class AccountViewHolder extends RecyclerView.ViewHolder { displayName = itemView.findViewById(R.id.account_display_name); avatar = itemView.findViewById(R.id.account_avatar); avatarInset = itemView.findViewById(R.id.account_avatar_inset); + // TODO: wtf SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(itemView.getContext()); showBotOverlay = sharedPrefs.getBoolean("showBotOverlay", true); } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt index 65cc4e84c..5fc328988 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt @@ -69,6 +69,7 @@ import com.keylesspalace.tusky.interfaces.ActionButtonActivity import com.keylesspalace.tusky.interfaces.LinkListener import com.keylesspalace.tusky.interfaces.ReselectableFragment import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.DefaultTextWatcher import com.keylesspalace.tusky.util.Error import com.keylesspalace.tusky.util.Loading @@ -78,7 +79,6 @@ import com.keylesspalace.tusky.util.emojify import com.keylesspalace.tusky.util.getDomain import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.loadAvatar -import com.keylesspalace.tusky.util.openLink import com.keylesspalace.tusky.util.setClickableText import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding @@ -96,6 +96,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector @Inject lateinit var viewModelFactory: ViewModelFactory + @Inject + lateinit var prefs: Prefs private val viewModel: AccountViewModel by viewModels { viewModelFactory } @@ -146,10 +148,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI // Obtain information to fill out the profile. viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!) - val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this) - animateAvatar = sharedPrefs.getBoolean("animateGifAvatars", false) - animateEmojis = sharedPrefs.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) - hideFab = sharedPrefs.getBoolean("fabHide", false) + animateAvatar = prefs.animateAvatars + animateEmojis = prefs.animateEmojis + hideFab = prefs.hideFab handleWindowInsets() setupToolbar() @@ -214,8 +215,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI } // If wellbeing mode is enabled, follow stats and posts count should be hidden - val preferences = PreferenceManager.getDefaultSharedPreferences(this) - val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_PROFILE, false) + val wellbeingEnabled = prefs.hideStatsProfile if (wellbeingEnabled) { binding.accountStatuses.hide() @@ -577,8 +577,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI showingReblogs = relation.showingReblogs // If wellbeing mode is enabled, "follows you" text should not be visible - val preferences = PreferenceManager.getDefaultSharedPreferences(this) - val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_PROFILE, false) + val wellbeingEnabled = prefs.hideStatsProfile binding.accountFollowsYouTextView.visible(relation.followedBy && !wellbeingEnabled) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt index b0c6653b7..f8aecfba0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt @@ -17,7 +17,6 @@ package com.keylesspalace.tusky.components.announcements import android.content.Context import android.content.Intent -import android.content.SharedPreferences import android.os.Bundle import android.view.View import android.widget.PopupWindow @@ -34,6 +33,7 @@ import com.keylesspalace.tusky.databinding.ActivityAnnouncementsBinding import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.Error import com.keylesspalace.tusky.util.Loading import com.keylesspalace.tusky.util.Success @@ -47,6 +47,8 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener, @Inject lateinit var viewModelFactory: ViewModelFactory + @Inject + lateinit var prefs: Prefs private val viewModel: AnnouncementsViewModel by viewModels { viewModelFactory } @@ -86,9 +88,8 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener, val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) binding.announcementsList.addItemDecoration(divider) - val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) - val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false) - val animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + val wellbeingEnabled = prefs.hideStatsPosts + val animateEmojis = prefs.animateEmojis adapter = AnnouncementAdapter(emptyList(), this, wellbeingEnabled, animateEmojis) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt index a9a9c2d4a..854f29181 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt @@ -21,7 +21,6 @@ import android.app.ProgressDialog import android.content.ClipData import android.content.Context import android.content.Intent -import android.content.SharedPreferences import android.content.pm.PackageManager import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter @@ -75,6 +74,7 @@ import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.NewPoll import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.ComposeTokenizer import com.keylesspalace.tusky.util.PickMediaFiles import com.keylesspalace.tusky.util.ThemeUtils @@ -112,6 +112,8 @@ class ComposeActivity : @Inject lateinit var viewModelFactory: ViewModelFactory + @Inject + lateinit var prefs: Prefs private lateinit var composeOptionsBehavior: BottomSheetBehavior<*> private lateinit var addMediaBehavior: BottomSheetBehavior<*> @@ -163,8 +165,7 @@ class ComposeActivity : accountManager.setActiveAccount(accountId) } - val preferences = PreferenceManager.getDefaultSharedPreferences(this) - val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT) + val theme = prefs.appTheme if (theme == "black") { setTheme(R.style.TuskyDialogActivityBlackTheme) } @@ -174,7 +175,7 @@ class ComposeActivity : // do not do anything when not logged in, activity will be finished in super.onCreate() anyway val activeAccount = accountManager.activeAccount ?: return - setupAvatar(preferences, activeAccount) + setupAvatar(activeAccount) val mediaAdapter = MediaPreviewAdapter( this, onAddCaption = { item -> @@ -210,7 +211,7 @@ class ComposeActivity : binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt) } - setupComposeField(preferences, viewModel.startingText) + setupComposeField(viewModel.startingText) setupContentWarningField(composeOptions?.contentWarning) setupPollView() applyShareIntent(intent, savedInstanceState) @@ -295,7 +296,7 @@ class ComposeActivity : binding.composeContentWarningField.onTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() } } - private fun setupComposeField(preferences: SharedPreferences, startingText: String?) { + private fun setupComposeField(startingText: String?) { binding.composeEditField.setOnReceiveContentListener(this) binding.composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) } @@ -303,8 +304,8 @@ class ComposeActivity : binding.composeEditField.setAdapter( ComposeAutoCompleteAdapter( this, - preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false), - preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + prefs.animateAvatars, + prefs.animateEmojis, ) ) binding.composeEditField.setTokenizer(ComposeTokenizer()) @@ -429,13 +430,13 @@ class ComposeActivity : } } - private fun setupAvatar(preferences: SharedPreferences, activeAccount: AccountEntity) { + private fun setupAvatar(activeAccount: AccountEntity) { val actionBarSizeAttr = intArrayOf(R.attr.actionBarSize) val a = obtainStyledAttributes(null, actionBarSizeAttr) val avatarSize = a.getDimensionPixelSize(0, 1) a.recycle() - val animateAvatars = preferences.getBoolean("animateGifAvatars", false) + val animateAvatars = prefs.animateAvatars loadAvatar( activeAccount.profilePictureUrl, binding.composeAvatar, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index 684144556..ab4f2723b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -39,6 +39,7 @@ import com.keylesspalace.tusky.fragment.SFragment import com.keylesspalace.tusky.interfaces.ReselectableFragment import com.keylesspalace.tusky.interfaces.StatusActionListener import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.CardViewMode import com.keylesspalace.tusky.util.StatusDisplayOptions import com.keylesspalace.tusky.util.hide @@ -56,6 +57,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res @Inject lateinit var viewModelFactory: ViewModelFactory + @Inject + lateinit var prefs: Prefs private val viewModel: ConversationsViewModel by viewModels { viewModelFactory } @@ -73,19 +76,17 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - val preferences = PreferenceManager.getDefaultSharedPreferences(view.context) - val statusDisplayOptions = StatusDisplayOptions( - animateAvatars = preferences.getBoolean("animateGifAvatars", false), - mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true, - useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false), - showBotOverlay = preferences.getBoolean("showBotOverlay", true), - useBlurhash = preferences.getBoolean("useBlurhash", true), + animateAvatars = prefs.animateAvatars, + mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true, + useAbsoluteTime = prefs.useAbsoluteTime, + showBotOverlay = prefs.showBotOverlay, + useBlurhash = prefs.useBlurhash, cardViewMode = CardViewMode.NONE, - confirmReblogs = preferences.getBoolean("confirmReblogs", true), - confirmFavourites = preferences.getBoolean("confirmFavourites", false), - hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false), - animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + confirmReblogs = prefs.confirmReblogs, + confirmFavourites = prefs.confirmFavourites, + hideStats = prefs.hideStatsPosts, + animateEmojis = prefs.animateEmojis, ) adapter = ConversationAdapter(statusDisplayOptions, this) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt index cc2bd776b..2daf9bfc4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt @@ -51,6 +51,7 @@ class LoginActivity : BaseActivity(), Injectable { private val binding by viewBinding(ActivityLoginBinding::inflate) + /** Special SharedPreferences for persisting login state in some cases. */ private lateinit var preferences: SharedPreferences private val oauthRedirectUri: String diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt index 20f4f86a0..339c2cb86 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt @@ -43,6 +43,7 @@ import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.CardViewMode import com.keylesspalace.tusky.util.StatusDisplayOptions import com.keylesspalace.tusky.util.viewBinding @@ -60,6 +61,9 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje @Inject lateinit var accountManager: AccountManager + @Inject + lateinit var prefs: Prefs + private val viewModel: ReportViewModel by activityViewModels { viewModelFactory } private val binding by viewBinding(FragmentReportStatusesBinding::bind) @@ -105,18 +109,17 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje } private fun initStatusesView() { - val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val statusDisplayOptions = StatusDisplayOptions( animateAvatars = false, mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true, - useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false), + useAbsoluteTime = prefs.useAbsoluteTime, showBotOverlay = false, - useBlurhash = preferences.getBoolean("useBlurhash", true), + useBlurhash = prefs.useBlurhash, cardViewMode = CardViewMode.NONE, - confirmReblogs = preferences.getBoolean("confirmReblogs", true), - confirmFavourites = preferences.getBoolean("confirmFavourites", false), - hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false), - animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + confirmReblogs = prefs.confirmReblogs, + confirmFavourites = prefs.confirmFavourites, + hideStats = prefs.hideStatsPosts, + animateEmojis = prefs.animateEmojis, ) adapter = StatusesAdapter(statusDisplayOptions, viewModel.statusViewState, this) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt index f59f84ff6..5128c93b8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt @@ -21,16 +21,19 @@ import androidx.preference.PreferenceManager import com.keylesspalace.tusky.components.search.adapter.SearchAccountsAdapter import com.keylesspalace.tusky.entity.TimelineAccount import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import kotlinx.coroutines.flow.Flow +import javax.inject.Inject class SearchAccountsFragment : SearchFragment() { - override fun createAdapter(): PagingDataAdapter { - val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context) + @Inject + lateinit var prefs: Prefs + override fun createAdapter(): PagingDataAdapter { return SearchAccountsAdapter( this, - preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false), - preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + animateAvatars = prefs.animateAvatars, + animateEmojis = prefs.animateEmojis, ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt index 8469b57d5..d81719388 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt @@ -53,6 +53,7 @@ import com.keylesspalace.tusky.entity.Status.Mention import com.keylesspalace.tusky.interfaces.AccountSelectionListener import com.keylesspalace.tusky.interfaces.StatusActionListener import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.CardViewMode import com.keylesspalace.tusky.util.StatusDisplayOptions import com.keylesspalace.tusky.util.openLink @@ -61,8 +62,11 @@ import com.keylesspalace.tusky.viewdata.AttachmentViewData import com.keylesspalace.tusky.viewdata.StatusViewData import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import kotlinx.coroutines.flow.Flow +import javax.inject.Inject class SearchStatusesFragment : SearchFragment(), StatusActionListener { + @Inject + lateinit var prefs: Prefs override val data: Flow> get() = viewModel.statusesFlow @@ -71,18 +75,17 @@ class SearchStatusesFragment : SearchFragment(), Status get() = super.adapter as SearchStatusesAdapter override fun createAdapter(): PagingDataAdapter { - val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context) val statusDisplayOptions = StatusDisplayOptions( - animateAvatars = preferences.getBoolean("animateGifAvatars", false), + animateAvatars = prefs.animateAvatars, mediaPreviewEnabled = viewModel.mediaPreviewEnabled, - useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false), - showBotOverlay = preferences.getBoolean("showBotOverlay", true), - useBlurhash = preferences.getBoolean("useBlurhash", true), + useAbsoluteTime = prefs.useAbsoluteTime, + showBotOverlay = prefs.showBotOverlay, + useBlurhash = prefs.useBlurhash, cardViewMode = CardViewMode.NONE, - confirmReblogs = preferences.getBoolean("confirmReblogs", true), - confirmFavourites = preferences.getBoolean("confirmFavourites", false), - hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false), - animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + confirmReblogs = prefs.confirmReblogs, + confirmFavourites = prefs.confirmFavourites, + hideStats = prefs.hideStatsPosts, + animateEmojis = prefs.animateEmojis, ) binding.searchRecyclerView.addItemDecoration(DividerItemDecoration(binding.searchRecyclerView.context, DividerItemDecoration.VERTICAL)) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt index f91750529..4e3d8de98 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt @@ -56,6 +56,7 @@ import com.keylesspalace.tusky.interfaces.RefreshableFragment import com.keylesspalace.tusky.interfaces.ReselectableFragment import com.keylesspalace.tusky.interfaces.StatusActionListener import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.CardViewMode import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate import com.keylesspalace.tusky.util.StatusDisplayOptions @@ -88,6 +89,9 @@ class TimelineFragment : @Inject lateinit var accountManager: AccountManager + @Inject + lateinit var prefs: Prefs + private val viewModel: TimelineViewModel by lazy { if (kind == TimelineViewModel.Kind.HOME) { ViewModelProvider(this, viewModelFactory)[CachedTimelineViewModel::class.java] @@ -136,22 +140,17 @@ class TimelineFragment : isSwipeToRefreshEnabled = arguments.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, true) - val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val statusDisplayOptions = StatusDisplayOptions( - animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false), + animateAvatars = prefs.animateAvatars, mediaPreviewEnabled = accountManager.activeAccount!!.mediaPreviewEnabled, - useAbsoluteTime = preferences.getBoolean(PrefKeys.ABSOLUTE_TIME_VIEW, false), - showBotOverlay = preferences.getBoolean(PrefKeys.SHOW_BOT_OVERLAY, true), - useBlurhash = preferences.getBoolean(PrefKeys.USE_BLURHASH, true), - cardViewMode = if (preferences.getBoolean( - PrefKeys.SHOW_CARDS_IN_TIMELINES, - false - ) - ) CardViewMode.INDENTED else CardViewMode.NONE, - confirmReblogs = preferences.getBoolean(PrefKeys.CONFIRM_REBLOGS, true), - confirmFavourites = preferences.getBoolean(PrefKeys.CONFIRM_FAVOURITES, false), - hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false), - animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + useAbsoluteTime = prefs.useAbsoluteTime, + showBotOverlay = prefs.showBotOverlay, + useBlurhash = prefs.useBlurhash, + cardViewMode = if (prefs.showCardsInTimelines) CardViewMode.INDENTED else CardViewMode.NONE, + confirmReblogs = prefs.confirmReblogs, + confirmFavourites = prefs.confirmFavourites, + hideStats = prefs.hideStatsPosts, + animateEmojis = prefs.animateEmojis, ) adapter = TimelinePagingAdapter( statusDisplayOptions, @@ -184,16 +183,28 @@ class TimelineFragment : is LoadState.NotLoading -> { if (loadState.append is LoadState.NotLoading && loadState.source.refresh is LoadState.NotLoading) { binding.statusView.show() - binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null) + binding.statusView.setup( + R.drawable.elephant_friend_empty, + R.string.message_empty, + null + ) } } is LoadState.Error -> { binding.statusView.show() if ((loadState.refresh as LoadState.Error).error is IOException) { - binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network, null) + binding.statusView.setup( + R.drawable.elephant_offline, + R.string.error_network, + null + ) } else { - binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic, null) + binding.statusView.setup( + R.drawable.elephant_error, + R.string.error_generic, + null + ) } } is LoadState.Loading -> { @@ -209,7 +220,10 @@ class TimelineFragment : binding.recyclerView.post { if (getView() != null) { if (isSwipeToRefreshEnabled) { - binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30)) + binding.recyclerView.scrollBy( + 0, + Utils.dpToPx(requireContext(), -30) + ) } else binding.recyclerView.scrollToPosition(0) } } @@ -224,8 +238,7 @@ class TimelineFragment : } if (actionButtonPresent()) { - val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) - hideFab = preferences.getBoolean("fabHide", false) + hideFab = prefs.hideFab scrollListener = object : RecyclerView.OnScrollListener() { override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) { val composeButton = (activity as ActionButtonActivity).actionButton @@ -388,9 +401,9 @@ class TimelineFragment : override fun onViewAccount(id: String) { if (( - viewModel.kind == TimelineViewModel.Kind.USER || - viewModel.kind == TimelineViewModel.Kind.USER_WITH_REPLIES - ) && + viewModel.kind == TimelineViewModel.Kind.USER || + viewModel.kind == TimelineViewModel.Kind.USER_WITH_REPLIES + ) && viewModel.id == id ) { /* If already viewing an account page, then any requests to view that account page @@ -401,10 +414,9 @@ class TimelineFragment : } private fun onPreferenceChanged(key: String) { - val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) when (key) { PrefKeys.FAB_HIDE -> { - hideFab = sharedPreferences.getBoolean(PrefKeys.FAB_HIDE, false) + hideFab = prefs.hideFab } PrefKeys.MEDIA_PREVIEW_ENABLED -> { val enabled = accountManager.activeAccount!!.mediaPreviewEnabled @@ -441,9 +453,9 @@ class TimelineFragment : private fun actionButtonPresent(): Boolean { return viewModel.kind != TimelineViewModel.Kind.TAG && - viewModel.kind != TimelineViewModel.Kind.FAVOURITES && - viewModel.kind != TimelineViewModel.Kind.BOOKMARKS && - activity is ActionButtonActivity + viewModel.kind != TimelineViewModel.Kind.FAVOURITES && + viewModel.kind != TimelineViewModel.Kind.BOOKMARKS && + activity is ActionButtonActivity } private var talkBackWasEnabled = false @@ -468,14 +480,17 @@ class TimelineFragment : * Auto dispose observable on pause */ private fun startUpdateTimestamp() { - val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) - val useAbsoluteTime = preferences.getBoolean(PrefKeys.ABSOLUTE_TIME_VIEW, false) + val useAbsoluteTime = prefs.useAbsoluteTime if (!useAbsoluteTime) { Observable.interval(1, TimeUnit.MINUTES) .observeOn(AndroidSchedulers.mainThread()) .autoDispose(this, Lifecycle.Event.ON_PAUSE) .subscribe { - adapter.notifyItemRangeChanged(0, adapter.itemCount, listOf(StatusBaseViewHolder.Key.KEY_CREATED)) + adapter.notifyItemRangeChanged( + 0, + adapter.itemCount, + listOf(StatusBaseViewHolder.Key.KEY_CREATED) + ) } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt index d71da9bb7..46c66c824 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt @@ -15,7 +15,6 @@ package com.keylesspalace.tusky.components.timeline.viewmodel -import android.content.SharedPreferences import android.util.Log import androidx.lifecycle.viewModelScope import androidx.paging.ExperimentalPagingApi @@ -41,6 +40,7 @@ import com.keylesspalace.tusky.entity.Poll import com.keylesspalace.tusky.network.FilterModel import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.TimelineCases +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.dec import com.keylesspalace.tusky.util.inc import com.keylesspalace.tusky.viewdata.StatusViewData @@ -61,11 +61,11 @@ class CachedTimelineViewModel @Inject constructor( private val api: MastodonApi, eventHub: EventHub, accountManager: AccountManager, - sharedPreferences: SharedPreferences, + prefs: Prefs, filterModel: FilterModel, private val db: AppDatabase, private val gson: Gson -) : TimelineViewModel(timelineCases, api, eventHub, accountManager, sharedPreferences, filterModel) { +) : TimelineViewModel(timelineCases, api, eventHub, accountManager, prefs, filterModel) { @OptIn(ExperimentalPagingApi::class) override val statuses = Pager( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt index 7a6df9d26..db0cb1683 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt @@ -15,7 +15,6 @@ package com.keylesspalace.tusky.components.timeline.viewmodel -import android.content.SharedPreferences import android.util.Log import androidx.lifecycle.viewModelScope import androidx.paging.ExperimentalPagingApi @@ -35,6 +34,7 @@ import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.FilterModel import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.TimelineCases +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.dec import com.keylesspalace.tusky.util.getDomain import com.keylesspalace.tusky.util.inc @@ -58,9 +58,9 @@ class NetworkTimelineViewModel @Inject constructor( private val api: MastodonApi, eventHub: EventHub, accountManager: AccountManager, - sharedPreferences: SharedPreferences, + prefs: Prefs, filterModel: FilterModel -) : TimelineViewModel(timelineCases, api, eventHub, accountManager, sharedPreferences, filterModel) { +) : TimelineViewModel(timelineCases, api, eventHub, accountManager, prefs, filterModel) { var currentSource: NetworkTimelinePagingSource? = null diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt index 544d08181..7327fa18d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt @@ -15,7 +15,6 @@ package com.keylesspalace.tusky.components.timeline.viewmodel -import android.content.SharedPreferences import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -41,6 +40,7 @@ import com.keylesspalace.tusky.network.FilterModel import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.TimelineCases import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.viewdata.StatusViewData import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow @@ -53,7 +53,7 @@ abstract class TimelineViewModel( private val api: MastodonApi, private val eventHub: EventHub, protected val accountManager: AccountManager, - private val sharedPreferences: SharedPreferences, + private val prefs: Prefs, private val filterModel: FilterModel ) : ViewModel() { @@ -81,10 +81,8 @@ abstract class TimelineViewModel( this.tags = tags if (kind == Kind.HOME) { - filterRemoveReplies = - !sharedPreferences.getBoolean(PrefKeys.TAB_FILTER_HOME_REPLIES, true) - filterRemoveReblogs = - !sharedPreferences.getBoolean(PrefKeys.TAB_FILTER_HOME_BOOSTS, true) + filterRemoveReplies = !prefs.tabFilterHomeReplies + filterRemoveReblogs = !prefs.tabFilterHomeBoosts } this.alwaysShowSensitiveMedia = accountManager.activeAccount!!.alwaysShowSensitiveMedia this.alwaysOpenSpoilers = accountManager.activeAccount!!.alwaysOpenSpoiler @@ -182,7 +180,7 @@ abstract class TimelineViewModel( private fun onPreferenceChanged(key: String) { when (key) { PrefKeys.TAB_FILTER_HOME_REPLIES -> { - val filter = sharedPreferences.getBoolean(PrefKeys.TAB_FILTER_HOME_REPLIES, true) + val filter = prefs.tabFilterHomeReplies val oldRemoveReplies = filterRemoveReplies filterRemoveReplies = kind == Kind.HOME && !filter if (oldRemoveReplies != filterRemoveReplies) { @@ -190,7 +188,7 @@ abstract class TimelineViewModel( } } PrefKeys.TAB_FILTER_HOME_BOOSTS -> { - val filter = sharedPreferences.getBoolean(PrefKeys.TAB_FILTER_HOME_BOOSTS, true) + val filter = prefs.tabFilterHomeBoosts val oldRemoveReblogs = filterRemoveReblogs filterRemoveReblogs = kind == Kind.HOME && !filter if (oldRemoveReblogs != filterRemoveReblogs) { diff --git a/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt index 7bda6ef74..fd6072d36 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt @@ -26,6 +26,7 @@ import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.json.SpannedTypeAdapter import com.keylesspalace.tusky.network.InstanceSwitchAuthInterceptor import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.getNonNullString import dagger.Module import dagger.Provides @@ -62,11 +63,11 @@ class NetworkModule { fun providesHttpClient( accountManager: AccountManager, context: Context, - preferences: SharedPreferences + prefs: Prefs, ): OkHttpClient { - val httpProxyEnabled = preferences.getBoolean("httpProxyEnabled", false) - val httpServer = preferences.getNonNullString("httpProxyServer", "") - val httpPort = preferences.getNonNullString("httpProxyPort", "-1").toIntOrNull() ?: -1 + val httpProxyEnabled = prefs.httpProxyEnabled + val httpServer = prefs.httpProxyServer + val httpPort = prefs.httpProxyPort.toIntOrNull() ?: -1 val cacheSize = 25 * 1024 * 1024L // 25 MiB val builder = OkHttpClient.Builder() .addInterceptor { chain -> diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt index 465b9f216..ad8bb28b9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt @@ -47,6 +47,7 @@ import com.keylesspalace.tusky.entity.TimelineAccount import com.keylesspalace.tusky.interfaces.AccountActionListener import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.settings.PrefKeys +import com.keylesspalace.tusky.settings.Prefs import com.keylesspalace.tusky.util.HttpHeaderLink import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show @@ -65,6 +66,8 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct lateinit var api: MastodonApi @Inject lateinit var accountManager: AccountManager + @Inject + lateinit var prefs: Prefs private val binding by viewBinding(FragmentAccountListBinding::bind) @@ -92,9 +95,8 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct binding.recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) - val pm = PreferenceManager.getDefaultSharedPreferences(view.context) - val animateAvatar = pm.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false) - val animateEmojis = pm.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + val animateAvatar = prefs.animateAvatars + val animateEmojis = prefs.animateEmojis adapter = when (type) { Type.BLOCKS -> BlocksAdapter(this, animateAvatar, animateEmojis) diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 467ebc8c6..4249c1203 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -18,7 +18,6 @@ package com.keylesspalace.tusky.fragment; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; -import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import android.util.SparseBooleanArray; @@ -73,6 +72,7 @@ import com.keylesspalace.tusky.interfaces.ActionButtonActivity; import com.keylesspalace.tusky.interfaces.ReselectableFragment; import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.settings.PrefKeys; +import com.keylesspalace.tusky.settings.Prefs; import com.keylesspalace.tusky.util.CardViewMode; import com.keylesspalace.tusky.util.Either; import com.keylesspalace.tusky.util.HttpHeaderLink; @@ -156,6 +156,8 @@ public class NotificationsFragment extends SFragment implements AccountManager accountManager; @Inject EventHub eventHub; + @Inject + Prefs prefs; private SwipeRefreshLayout swipeRefreshLayout; private RecyclerView recyclerView; @@ -213,9 +215,8 @@ public class NotificationsFragment extends SFragment implements View rootView = inflater.inflate(R.layout.fragment_timeline_notifications, container, false); @NonNull Context context = inflater.getContext(); // from inflater to silence warning - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); - boolean showNotificationsFilterSetting = preferences.getBoolean("showNotificationsFilter", true); + boolean showNotificationsFilterSetting = prefs.getShowNotificationsFilter(); //Clear notifications on filter visibility change to force refresh if (showNotificationsFilterSetting != showNotificationsFilter) notifications.clear(); @@ -251,16 +252,16 @@ public class NotificationsFragment extends SFragment implements recyclerView.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL)); StatusDisplayOptions statusDisplayOptions = new StatusDisplayOptions( - preferences.getBoolean("animateGifAvatars", false), + prefs.getAnimateAvatars(), accountManager.getActiveAccount().getMediaPreviewEnabled(), - preferences.getBoolean("absoluteTimeView", false), - preferences.getBoolean("showBotOverlay", true), - preferences.getBoolean("useBlurhash", true), + prefs.getUseAbsoluteTime(), + prefs.getShowBotOverlay(), + prefs.getUseBlurhash(), CardViewMode.NONE, - preferences.getBoolean("confirmReblogs", true), - preferences.getBoolean("confirmFavourites", false), - preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false), - preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + prefs.getConfirmReblogs(), + prefs.getConfirmFavourites(), + prefs.getHideStatsPosts(), + prefs.getAnimateEmojis() ); adapter = new NotificationsAdapter(accountManager.getActiveAccount().getAccountId(), @@ -324,12 +325,11 @@ public class NotificationsFragment extends SFragment implements Activity activity = getActivity(); if (activity == null) throw new AssertionError("Activity is null"); - /* This is delayed until onActivityCreated solely because MainActivity.composeButton isn't - * guaranteed to be set until then. - * Use a modified scroll listener that both loads more notificationsEnabled as it goes, and hides - * the compose button on down-scroll. */ - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); - hideFab = preferences.getBoolean("fabHide", false); + // This is delayed until onActivityCreated solely because MainActivity.composeButton isn't + // guaranteed to be set until then. + // Use a modified scroll listener that both loads more notificationsEnabled as it goes, and hides + // the compose button on down-scroll. + hideFab = prefs.getHideFab(); scrollListener = new EndlessOnScrollListener(layoutManager) { @Override public void onScrolled(@NonNull RecyclerView view, int dx, int dy) { @@ -798,11 +798,11 @@ public class NotificationsFragment extends SFragment implements private void onPreferenceChanged(String key) { switch (key) { - case "fabHide": { - hideFab = PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("fabHide", false); + case PrefKeys.FAB_HIDE: { + hideFab = prefs.getHideFab(); break; } - case "mediaPreviewEnabled": { + case PrefKeys.MEDIA_PREVIEW_ENABLED: { boolean enabled = accountManager.getActiveAccount().getMediaPreviewEnabled(); if (enabled != adapter.isMediaPreviewEnabled()) { adapter.setMediaPreviewEnabled(enabled); @@ -810,9 +810,9 @@ public class NotificationsFragment extends SFragment implements } break; } - case "showNotificationsFilter": { + case PrefKeys.SHOW_NOTIFICATIONS_FILTER: { if (isAdded()) { - showNotificationsFilter = PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("showNotificationsFilter", true); + showNotificationsFilter = prefs.getShowNotificationsFilter(); updateFilterVisibility(); fullyRefreshWithProgressBar(true); } @@ -1224,8 +1224,7 @@ public class NotificationsFragment extends SFragment implements * Auto dispose observable on pause */ private void startUpdateTimestamp() { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); - boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false); + boolean useAbsoluteTime = prefs.getUseAbsoluteTime(); if (!useAbsoluteTime) { Observable.interval(1, TimeUnit.MINUTES) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java index 98466cc99..54125212c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java @@ -17,7 +17,6 @@ package com.keylesspalace.tusky.fragment; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; @@ -59,6 +58,7 @@ import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.network.FilterModel; import com.keylesspalace.tusky.network.MastodonApi; import com.keylesspalace.tusky.settings.PrefKeys; +import com.keylesspalace.tusky.settings.Prefs; import com.keylesspalace.tusky.util.CardViewMode; import com.keylesspalace.tusky.util.LinkHelper; import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate; @@ -93,6 +93,8 @@ public final class ViewThreadFragment extends SFragment implements public EventHub eventHub; @Inject public FilterModel filterModel; + @Inject + public Prefs prefs; private SwipeRefreshLayout swipeRefreshLayout; private RecyclerView recyclerView; @@ -129,22 +131,20 @@ public final class ViewThreadFragment extends SFragment implements super.onCreate(savedInstanceState); thisThreadsStatusId = getArguments().getString("id"); - SharedPreferences preferences = - PreferenceManager.getDefaultSharedPreferences(getActivity()); StatusDisplayOptions statusDisplayOptions = new StatusDisplayOptions( - preferences.getBoolean("animateGifAvatars", false), + prefs.getAnimateAvatars(), accountManager.getActiveAccount().getMediaPreviewEnabled(), - preferences.getBoolean("absoluteTimeView", false), - preferences.getBoolean("showBotOverlay", true), - preferences.getBoolean("useBlurhash", true), - preferences.getBoolean("showCardsInTimelines", false) ? + prefs.getUseAbsoluteTime(), + prefs.getShowBotOverlay(), + prefs.getUseBlurhash(), + prefs.getShowCardsInTimelines() ? CardViewMode.INDENTED : CardViewMode.NONE, - preferences.getBoolean("confirmReblogs", true), - preferences.getBoolean("confirmFavourites", false), - preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false), - preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) + prefs.getConfirmReblogs(), + prefs.getConfirmFavourites(), + prefs.getHideStatsPosts(), + prefs.getAnimateEmojis() ); adapter = new ThreadAdapter(statusDisplayOptions, this); } diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/Prefs.kt b/app/src/main/java/com/keylesspalace/tusky/settings/Prefs.kt new file mode 100644 index 000000000..2a1acbf5d --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/settings/Prefs.kt @@ -0,0 +1,129 @@ +package com.keylesspalace.tusky.settings + +import android.content.Context +import android.content.SharedPreferences +import androidx.annotation.Keep +import androidx.preference.PreferenceManager +import com.keylesspalace.tusky.util.ThemeUtils +import javax.inject.Inject +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +// TODO: do we every plan to use them as writable properties? +// TODO: is this enough to preserve fields/names? +// TODO: what about observing changes? Do we keep PrefKeys and reference them? +@Keep +class Prefs @Inject constructor(context: Context) { + // TODO: not sure if should be lazy or non-cached at all + private val sharedPreferences: SharedPreferences = + PreferenceManager.getDefaultSharedPreferences(context) + + var appTheme by stringProperty(ThemeUtils.APP_THEME_DEFAULT) + var emojiFont by intProperty(0, "selected_emoji_font") + val hideFab by booleanProperty(false, "fabHide") + var language by stringProperty(defaultValue = "default") + val statusTextSize by stringProperty("medium") + val mainNavPosition by stringProperty() + val hideTopToolbar by booleanProperty(false) + + val animateAvatars by booleanProperty(false, "animateGifAvatars") + val useAbsoluteTime by booleanProperty(false, "absoluteTimeView") + val showBotOverlay by booleanProperty(true) + val useBlurhash by booleanProperty(true) + val showNotificationsFilter by booleanProperty(true) + val showCardsInTimelines by booleanProperty(false) + val confirmReblogs by booleanProperty(true) + val confirmFavourites by booleanProperty(false) + val enableSwipeForTabs by booleanProperty(true) + val customTabs by booleanProperty(false) + val hideStatsPosts by booleanProperty(false, "wellbeingHideStatsPosts") + val hideStatsProfile by booleanProperty(false, "wellbeingHideStatsProfile") + val animateEmojis by booleanProperty(false, "animateCustomEmojis") + val tabFilterHomeReplies by booleanProperty(true) + val tabFilterHomeBoosts by booleanProperty(true) + + val httpProxyEnabled by booleanProperty(false) + val httpProxyServer by stringProperty(defaultValue = "") + val httpProxyPort by stringProperty(defaultValue = "") + + private fun stringProperty(overrideName: String? = null) = + StringProperty(sharedPreferences, overrideName) + + private fun stringProperty( + defaultValue: String, + overrideName: String? = null, + ): ReadWriteProperty = + this.stringProperty(overrideName).withDefault(defaultValue) + + private fun booleanProperty( + defaultValue: Boolean, + overrideName: String? = null, + ) = BooleanProperty(sharedPreferences, overrideName, defaultValue) + + private fun intProperty( + defaultValue: Int, + overrideName: String? = null, + ) = IntProperty(sharedPreferences, overrideName, defaultValue) +} + +private fun ReadWriteProperty.withDefault( + default: P +): ReadWriteProperty = object : ReadWriteProperty { + override fun getValue(thisRef: T, property: KProperty<*>): P { + return this@withDefault.getValue(thisRef, property) ?: default + } + + override fun setValue(thisRef: T, property: KProperty<*>, value: P) { + this@withDefault.setValue(thisRef, property, value) + } +} + +private class StringProperty( + private val sharedPreferences: SharedPreferences, + private val overrideName: String?, +) : ReadWriteProperty { + override fun getValue(thisRef: Prefs, property: KProperty<*>): String? { + return sharedPreferences.getString(overrideName ?: property.name, null) + } + + override fun setValue(thisRef: Prefs, property: KProperty<*>, value: String?) { + sharedPreferences.edit().putString(overrideName ?: property.name, value) + .apply() + } +} + +private class BooleanProperty( + private val sharedPreferences: SharedPreferences, + private val overrideName: String?, + private val defaultValue: Boolean, +) : ReadWriteProperty { + override fun getValue(thisRef: Prefs, property: KProperty<*>): Boolean { + return sharedPreferences.getBoolean( + overrideName ?: property.name, + defaultValue, + ) + } + + override fun setValue(thisRef: Prefs, property: KProperty<*>, value: Boolean) { + sharedPreferences.edit().putBoolean(overrideName ?: property.name, value) + .apply() + } +} + +private class IntProperty( + private val sharedPreferences: SharedPreferences, + private val overrideName: String?, + private val defaultValue: Int, +) : ReadWriteProperty { + override fun getValue(thisRef: Prefs, property: KProperty<*>): Int { + return sharedPreferences.getInt( + overrideName ?: property.name, + defaultValue, + ) + } + + override fun setValue(thisRef: Prefs, property: KProperty<*>, value: Int) { + sharedPreferences.edit().putInt(overrideName ?: property.name, value) + .apply() + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt index c59ba58b0..0b2142c9b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsConstants.kt @@ -1,5 +1,14 @@ package com.keylesspalace.tusky.settings +import android.content.Context +import android.content.SharedPreferences +import androidx.annotation.Keep +import androidx.preference.PreferenceManager +import com.keylesspalace.tusky.util.ThemeUtils +import javax.inject.Inject +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + enum class AppTheme(val value: String) { NIGHT("night"), DAY("day"), @@ -62,4 +71,4 @@ object PrefKeys { const val TAB_FILTER_HOME_REPLIES = "tabFilterHomeReplies" const val TAB_FILTER_HOME_BOOSTS = "tabFilterHomeBoosts" -} +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt index 02ecf7735..9b4551943 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt @@ -37,6 +37,7 @@ import com.keylesspalace.tusky.R import com.keylesspalace.tusky.entity.HashTag import com.keylesspalace.tusky.entity.Status.Mention import com.keylesspalace.tusky.interfaces.LinkListener +import com.keylesspalace.tusky.settings.Prefs fun getDomain(urlString: String?): String { val host = urlString?.toUri()?.host @@ -184,7 +185,7 @@ fun createClickableText(text: String, link: String): CharSequence { */ fun Context.openLink(url: String) { val uri = url.toUri().normalizeScheme() - val useCustomTabs = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("customTabs", false) + val useCustomTabs = Prefs(this).customTabs if (useCustomTabs) { openLinkInCustomTab(uri, this) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LocaleManager.kt b/app/src/main/java/com/keylesspalace/tusky/util/LocaleManager.kt index 45f3ab371..5a6e065f6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/LocaleManager.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/LocaleManager.kt @@ -16,17 +16,16 @@ package com.keylesspalace.tusky.util import android.content.Context -import android.content.SharedPreferences import android.content.res.Configuration -import androidx.preference.PreferenceManager -import java.util.Locale +import com.keylesspalace.tusky.settings.Prefs +import java.util.* -class LocaleManager(context: Context) { - - private var prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) +class LocaleManager( + private val prefs: Prefs, +) { fun setLocale(context: Context): Context { - val language = prefs.getNonNullString("language", "default") + val language = prefs.language if (language == "default") { return context }