WIP: prefs

This commit is contained in:
charlag 2022-03-19 01:34:17 +01:00
parent 0dc32774ec
commit b7e0494778
No known key found for this signature in database
GPG Key ID: 5B96E7C76F0CA558
26 changed files with 467 additions and 220 deletions

View File

@ -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)

View File

@ -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<Integer, PermissionRequester> 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)

View File

@ -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<in Drawable>?) {
override fun onResourceReady(
resource: Drawable,
transition: Transition<in Drawable>?
) {
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<in Bitmap>?) {
binding.mainToolbar.navigationIcon = FixedSizeDrawable(BitmapDrawable(resources, resource), navIconSize, navIconSize)
override fun onResourceReady(
resource: Bitmap,
transition: Transition<in Bitmap>?
) {
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<IProfile> = accountManager.getAllAccountsOrderedByActive().map { acc ->
val emojifiedName = EmojiCompat.get().process(acc.displayName.emojify(acc.emojis, header, animateEmojis))
val animateEmojis = prefs.animateEmojis
val profiles: MutableList<IProfile> =
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()) {

View File

@ -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))
}

View File

@ -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<AccountEntity>(context, R.layout.item_autocomplete_account) {
class AccountSelectionAdapter(
context: Context,
private val prefs: Prefs,
) : ArrayAdapter<AccountEntity>(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<AccountEntity>(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)
}

View File

@ -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);
}

View File

@ -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<Any>
@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)

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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<TimelineAccount>() {
override fun createAdapter(): PagingDataAdapter<TimelineAccount, *> {
val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context)
@Inject
lateinit var prefs: Prefs
override fun createAdapter(): PagingDataAdapter<TimelineAccount, *> {
return SearchAccountsAdapter(
this,
preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
animateAvatars = prefs.animateAvatars,
animateEmojis = prefs.animateEmojis,
)
}

View File

@ -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<StatusViewData.Concrete>(), StatusActionListener {
@Inject
lateinit var prefs: Prefs
override val data: Flow<PagingData<StatusViewData.Concrete>>
get() = viewModel.statusesFlow
@ -71,18 +75,17 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
get() = super.adapter as SearchStatusesAdapter
override fun createAdapter(): PagingDataAdapter<StatusViewData.Concrete, *> {
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))

View File

@ -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)
)
}
}
}

View File

@ -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(

View File

@ -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

View File

@ -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) {

View File

@ -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 ->

View File

@ -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)

View File

@ -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())

View File

@ -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);
}

View File

@ -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<Prefs, String> =
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 <T, P> ReadWriteProperty<T, P?>.withDefault(
default: P
): ReadWriteProperty<T, P> = object : ReadWriteProperty<T, P> {
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<Prefs, String?> {
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<Prefs, Boolean> {
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<Prefs, Int> {
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()
}
}

View File

@ -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"
}
}

View File

@ -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)

View File

@ -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
}