WIP: prefs via androidx.datastore
This commit is contained in:
parent
b7e0494778
commit
b1ea335e3e
|
@ -129,6 +129,8 @@ dependencies {
|
|||
implementation "androidx.room:room-rxjava3:$roomVersion"
|
||||
kapt "androidx.room:room-compiler:$roomVersion"
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0-beta01'
|
||||
implementation "androidx.datastore:datastore-core:1.0.0"
|
||||
// implementation "androidx.datastore:datastore-preferences:1.0.0"
|
||||
|
||||
implementation "com.google.android.material:material:1.5.0"
|
||||
|
||||
|
|
|
@ -11,11 +11,17 @@ import android.widget.TextView
|
|||
import androidx.annotation.StringRes
|
||||
import com.keylesspalace.tusky.databinding.ActivityAboutBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.NoUnderlineURLSpan
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import javax.inject.Inject
|
||||
|
||||
class AboutActivity : BottomSheetActivity(), Injectable {
|
||||
|
||||
@Inject
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@ -48,28 +54,28 @@ class AboutActivity : BottomSheetActivity(), Injectable {
|
|||
startActivityWithSlideInAnimation(Intent(this, LicenseActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
|
||||
private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
|
||||
|
||||
val text = SpannableString(context.getText(textId))
|
||||
val text = SpannableString(context.getText(textId))
|
||||
|
||||
Linkify.addLinks(text, Linkify.WEB_URLS)
|
||||
Linkify.addLinks(text, Linkify.WEB_URLS)
|
||||
|
||||
val builder = SpannableStringBuilder(text)
|
||||
val urlSpans = text.getSpans(0, text.length, URLSpan::class.java)
|
||||
for (span in urlSpans) {
|
||||
val start = builder.getSpanStart(span)
|
||||
val end = builder.getSpanEnd(span)
|
||||
val flags = builder.getSpanFlags(span)
|
||||
val builder = SpannableStringBuilder(text)
|
||||
val urlSpans = text.getSpans(0, text.length, URLSpan::class.java)
|
||||
for (span in urlSpans) {
|
||||
val start = builder.getSpanStart(span)
|
||||
val end = builder.getSpanEnd(span)
|
||||
val flags = builder.getSpanFlags(span)
|
||||
|
||||
val customSpan = NoUnderlineURLSpan(span.url)
|
||||
val customSpan = NoUnderlineURLSpan(span.url, prefStore.getBlocking().customTabs)
|
||||
|
||||
builder.removeSpan(span)
|
||||
builder.setSpan(customSpan, start, end, flags)
|
||||
builder.removeSpan(span)
|
||||
builder.setSpan(customSpan, start, end, flags)
|
||||
}
|
||||
|
||||
setText(builder)
|
||||
linksClickable = true
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
||||
setText(builder)
|
||||
linksClickable = true
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ 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.Prefs
|
||||
import com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.BindingHolder
|
||||
import com.keylesspalace.tusky.util.Either
|
||||
import com.keylesspalace.tusky.util.emojify
|
||||
|
@ -56,7 +57,7 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefs: PrefStore
|
||||
|
||||
private val viewModel: AccountsInListViewModel by viewModels { viewModelFactory }
|
||||
private val binding by viewBinding(FragmentAccountsInListBinding::bind)
|
||||
|
@ -67,8 +68,8 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
private val searchAdapter = SearchAdapter()
|
||||
|
||||
private val radius by lazy { resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) }
|
||||
private val animateAvatar by lazy { prefs.animateAvatars }
|
||||
private val animateEmojis by lazy { prefs.animateEmojis }
|
||||
private val animateAvatar by lazy { prefs.getBlocking().animateAvatars }
|
||||
private val animateEmojis by lazy { prefs.getBlocking().animateEmojis }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
@ -33,6 +33,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.datastore.core.DataStore;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.keylesspalace.tusky.adapter.AccountSelectionAdapter;
|
||||
|
@ -42,6 +43,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.PrefData;
|
||||
import com.keylesspalace.tusky.settings.Prefs;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
|
||||
|
@ -57,7 +59,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
public AccountManager accountManager;
|
||||
|
||||
@Inject
|
||||
public Prefs prefs;
|
||||
public DataStore<PrefData> prefStore;
|
||||
|
||||
private static final int REQUESTER_NONE = Integer.MAX_VALUE;
|
||||
private HashMap<Integer, PermissionRequester> requesters;
|
||||
|
@ -70,6 +72,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
// 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.
|
||||
PrefData prefs = Prefs.getBlocking(prefStore);
|
||||
String theme = prefs.getAppTheme();
|
||||
Log.d("activeTheme", theme);
|
||||
if (theme.equals("black")) {
|
||||
|
@ -190,7 +193,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
if (!showActiveAccount && activeAccount != null) {
|
||||
accounts.remove(activeAccount);
|
||||
}
|
||||
AccountSelectionAdapter adapter = new AccountSelectionAdapter(this, this.prefs);
|
||||
AccountSelectionAdapter adapter = new AccountSelectionAdapter(this, this.prefStore);
|
||||
adapter.addAll(accounts);
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
|
|
|
@ -28,6 +28,7 @@ import autodispose2.autoDispose
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.keylesspalace.tusky.components.account.AccountActivity
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.openLink
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import java.net.URI
|
||||
|
@ -160,7 +161,7 @@ abstract class BottomSheetActivity : BaseActivity() {
|
|||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
|
||||
open fun openLink(url: String) {
|
||||
(this as Context).openLink(url)
|
||||
(this as Context).openLink(url, prefStore.getBlocking().customTabs)
|
||||
}
|
||||
|
||||
private fun showQuerySheet() {
|
||||
|
|
|
@ -76,8 +76,8 @@ import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
|||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.deleteStaleCachedMedia
|
||||
import com.keylesspalace.tusky.util.emojify
|
||||
|
@ -137,7 +137,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
lateinit var draftHelper: DraftHelper
|
||||
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefs: PrefStore
|
||||
|
||||
private val binding by viewBinding(ActivityMainBinding::inflate)
|
||||
|
||||
|
@ -227,7 +227,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
startActivity(composeIntent)
|
||||
}
|
||||
|
||||
val hideTopToolbar = prefs.hideTopToolbar
|
||||
val hideTopToolbar = prefStore.getBlocking().hideTopToolbar
|
||||
binding.mainToolbar.visible(!hideTopToolbar)
|
||||
|
||||
loadDrawerAvatar(activeAccount.profilePictureUrl, true)
|
||||
|
@ -396,7 +396,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
R.attr.colorBackgroundAccent
|
||||
)
|
||||
)
|
||||
val animateAvatars = prefs.animateAvatars
|
||||
val animateAvatars = prefStore.getBlocking().animateAvatars
|
||||
|
||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
||||
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
|
||||
|
@ -580,8 +580,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
|
||||
private fun setupTabs(selectNotificationTab: Boolean) {
|
||||
|
||||
val activeTabLayout = if (prefs.mainNavPosition == "bottom") {
|
||||
val activeTabLayout = if (prefStore.getBlocking().mainNavPosition == "bottom") {
|
||||
val actionBarSize = ThemeUtils.getDimension(this, R.attr.actionBarSize)
|
||||
val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
|
||||
(binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin =
|
||||
|
@ -626,7 +625,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
||||
binding.viewPager.setPageTransformer(MarginPageTransformer(pageMargin))
|
||||
|
||||
binding.viewPager.isUserInputEnabled = prefs.enableSwipeForTabs
|
||||
binding.viewPager.isUserInputEnabled = prefStore.getBlocking().enableSwipeForTabs
|
||||
|
||||
onTabSelectedListener?.let {
|
||||
activeTabLayout.removeOnTabSelectedListener(it)
|
||||
|
@ -770,7 +769,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 = prefs.animateAvatars
|
||||
val animateAvatars = prefStore.getBlocking().animateAvatars
|
||||
|
||||
if (animateAvatars) {
|
||||
glide.asDrawable()
|
||||
|
@ -874,7 +873,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
|
||||
private fun updateProfiles() {
|
||||
val animateEmojis = prefs.animateEmojis
|
||||
val animateEmojis = prefStore.getBlocking().animateEmojis
|
||||
val profiles: MutableList<IProfile> =
|
||||
accountManager.getAllAccountsOrderedByActive().map { acc ->
|
||||
val emojifiedName = EmojiCompat.get()
|
||||
|
|
|
@ -20,19 +20,20 @@ import android.content.Context
|
|||
import android.content.res.Configuration
|
||||
import android.util.Log
|
||||
import androidx.emoji.text.EmojiCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.work.WorkManager
|
||||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.settings.makePrefStore
|
||||
import com.keylesspalace.tusky.util.EmojiCompatFont
|
||||
import com.keylesspalace.tusky.util.LocaleManager
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.conscrypt.Conscrypt
|
||||
import java.security.Security
|
||||
import javax.inject.Inject
|
||||
|
@ -46,7 +47,7 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
lateinit var notificationWorkerFactory: NotificationWorkerFactory
|
||||
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
override fun onCreate() {
|
||||
// Uncomment me to get StrictMode violation logs
|
||||
|
@ -69,14 +70,14 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
|
||||
|
||||
// init the custom emoji fonts
|
||||
val emojiSelection = prefs.emojiFont
|
||||
val emojiSelection = prefStore.getBlocking().emojiFont
|
||||
val emojiConfig = EmojiCompatFont.byId(emojiSelection)
|
||||
.getConfig(this)
|
||||
.setReplaceAll(true)
|
||||
EmojiCompat.init(emojiConfig)
|
||||
|
||||
// init night mode
|
||||
val theme = prefs.appTheme
|
||||
val theme = prefStore.getBlocking().appTheme
|
||||
ThemeUtils.setAppNightMode(theme)
|
||||
|
||||
RxJavaPlugins.setErrorHandler {
|
||||
|
@ -92,9 +93,13 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
}
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
// special case: injected field cannot be injected here yet so we create Prefs by hand
|
||||
localeManager = LocaleManager(Prefs(base))
|
||||
super.attachBaseContext(localeManager.setLocale(base))
|
||||
// Special case: injected field cannot be injected here yet so we create Prefs by hand
|
||||
// Give it a blocking scope so that it will be closed and pref store will be released
|
||||
runBlocking {
|
||||
val prefs = makePrefStore(base, this)
|
||||
localeManager = LocaleManager(prefs)
|
||||
super.attachBaseContext(localeManager.setLocale(base))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
|
|
|
@ -20,18 +20,17 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.preference.PreferenceManager
|
||||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.emojify
|
||||
import com.keylesspalace.tusky.util.loadAvatar
|
||||
|
||||
class AccountSelectionAdapter(
|
||||
context: Context,
|
||||
private val prefs: Prefs,
|
||||
private val prefStore: PrefStore,
|
||||
) : ArrayAdapter<AccountEntity>(context, R.layout.item_autocomplete_account) {
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
|
@ -44,6 +43,7 @@ class AccountSelectionAdapter(
|
|||
val account = getItem(position)
|
||||
if (account != null) {
|
||||
// TODO: is this even okay to do prefs things for each invocation here?
|
||||
val prefs = prefStore.getBlocking()
|
||||
val animateEmojis = prefs.animateEmojis
|
||||
|
||||
binding.username.text = account.fullName
|
||||
|
|
|
@ -91,7 +91,11 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
|||
timestampInfo.append(" • ");
|
||||
|
||||
if (app.getWebsite() != null) {
|
||||
CharSequence text = LinkHelper.createClickableText(app.getName(), app.getWebsite());
|
||||
CharSequence text = LinkHelper.createClickableText(
|
||||
app.getName(),
|
||||
app.getWebsite(),
|
||||
false // This seems like a sensible default for clicking on app name
|
||||
);
|
||||
timestampInfo.append(text);
|
||||
timestampInfo.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
} else {
|
||||
|
|
|
@ -40,7 +40,6 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.emoji.text.EmojiCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.viewpager2.widget.MarginPageTransformer
|
||||
import com.bumptech.glide.Glide
|
||||
|
@ -68,8 +67,8 @@ import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
|||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.DefaultTextWatcher
|
||||
import com.keylesspalace.tusky.util.Error
|
||||
import com.keylesspalace.tusky.util.Loading
|
||||
|
@ -97,7 +96,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
private val viewModel: AccountViewModel by viewModels { viewModelFactory }
|
||||
|
||||
|
@ -148,6 +147,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
// Obtain information to fill out the profile.
|
||||
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!)
|
||||
|
||||
val prefs = prefStore.getBlocking()
|
||||
animateAvatar = prefs.animateAvatars
|
||||
animateEmojis = prefs.animateEmojis
|
||||
hideFab = prefs.hideFab
|
||||
|
@ -189,7 +189,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
binding.accountFollowsYouTextView.hide()
|
||||
|
||||
// setup the RecyclerView for the account fields
|
||||
accountFieldAdapter = AccountFieldAdapter(this, animateEmojis)
|
||||
accountFieldAdapter = AccountFieldAdapter(
|
||||
this,
|
||||
animateEmojis, prefStore.getBlocking().customTabs,
|
||||
)
|
||||
binding.accountFieldList.isNestedScrollingEnabled = false
|
||||
binding.accountFieldList.layoutManager = LinearLayoutManager(this)
|
||||
binding.accountFieldList.adapter = accountFieldAdapter
|
||||
|
@ -215,7 +218,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
}
|
||||
|
||||
// If wellbeing mode is enabled, follow stats and posts count should be hidden
|
||||
val wellbeingEnabled = prefs.hideStatsProfile
|
||||
val wellbeingEnabled = prefStore.getBlocking().hideStatsProfile
|
||||
|
||||
if (wellbeingEnabled) {
|
||||
binding.accountStatuses.hide()
|
||||
|
@ -577,7 +580,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
showingReblogs = relation.showingReblogs
|
||||
|
||||
// If wellbeing mode is enabled, "follows you" text should not be visible
|
||||
val wellbeingEnabled = prefs.hideStatsProfile
|
||||
val wellbeingEnabled = prefStore.getBlocking().hideStatsProfile
|
||||
|
||||
binding.accountFollowsYouTextView.visible(relation.followedBy && !wellbeingEnabled)
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ import com.keylesspalace.tusky.util.setClickableText
|
|||
|
||||
class AccountFieldAdapter(
|
||||
private val linkListener: LinkListener,
|
||||
private val animateEmojis: Boolean
|
||||
private val animateEmojis: Boolean,
|
||||
private val useCustomTabs: Boolean,
|
||||
) : RecyclerView.Adapter<BindingHolder<ItemAccountFieldBinding>>() {
|
||||
|
||||
var emojis: List<Emoji> = emptyList()
|
||||
|
@ -55,7 +56,11 @@ class AccountFieldAdapter(
|
|||
val identityProof = proofOrField.asLeft()
|
||||
|
||||
nameTextView.text = identityProof.provider
|
||||
valueTextView.text = createClickableText(identityProof.username, identityProof.profileUrl)
|
||||
valueTextView.text = createClickableText(
|
||||
identityProof.username,
|
||||
identityProof.profileUrl,
|
||||
useCustomTabs,
|
||||
)
|
||||
|
||||
valueTextView.movementMethod = LinkMovementMethod.getInstance()
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ import com.keylesspalace.tusky.entity.Attachment
|
|||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.interfaces.RefreshableFragment
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.openLink
|
||||
|
@ -63,6 +65,9 @@ class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFr
|
|||
@Inject
|
||||
lateinit var api: MastodonApi
|
||||
|
||||
@Inject
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
private val binding by viewBinding(FragmentTimelineBinding::bind)
|
||||
|
||||
private lateinit var accountId: String
|
||||
|
@ -252,7 +257,10 @@ class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFr
|
|||
}
|
||||
}
|
||||
Attachment.Type.UNKNOWN -> {
|
||||
context?.openLink(items[currentIndex].attachment.url)
|
||||
context?.openLink(
|
||||
items[currentIndex].attachment.url,
|
||||
prefStore.getBlocking().customTabs,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import android.widget.PopupWindow
|
||||
import androidx.activity.viewModels
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.keylesspalace.tusky.BottomSheetActivity
|
||||
|
@ -32,8 +31,8 @@ import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
|
|||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.Error
|
||||
import com.keylesspalace.tusky.util.Loading
|
||||
import com.keylesspalace.tusky.util.Success
|
||||
|
@ -48,7 +47,7 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
|
|||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefs: PrefStore
|
||||
|
||||
private val viewModel: AnnouncementsViewModel by viewModels { viewModelFactory }
|
||||
|
||||
|
@ -88,8 +87,8 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
|
|||
val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
|
||||
binding.announcementsList.addItemDecoration(divider)
|
||||
|
||||
val wellbeingEnabled = prefs.hideStatsPosts
|
||||
val animateEmojis = prefs.animateEmojis
|
||||
val wellbeingEnabled = prefStore.getBlocking().hideStatsPosts
|
||||
val animateEmojis = prefStore.getBlocking().animateEmojis
|
||||
|
||||
adapter = AnnouncementAdapter(emptyList(), this, wellbeingEnabled, animateEmojis)
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@ import androidx.core.view.ContentInfoCompat
|
|||
import androidx.core.view.OnReceiveContentListener
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
|
@ -73,8 +72,9 @@ import com.keylesspalace.tusky.entity.Attachment
|
|||
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.settings.PrefData
|
||||
import com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.ComposeTokenizer
|
||||
import com.keylesspalace.tusky.util.PickMediaFiles
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
|
@ -113,7 +113,7 @@ class ComposeActivity :
|
|||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefs: PrefStore
|
||||
|
||||
private lateinit var composeOptionsBehavior: BottomSheetBehavior<*>
|
||||
private lateinit var addMediaBehavior: BottomSheetBehavior<*>
|
||||
|
@ -165,6 +165,7 @@ class ComposeActivity :
|
|||
accountManager.setActiveAccount(accountId)
|
||||
}
|
||||
|
||||
val prefs = prefStore.getBlocking()
|
||||
val theme = prefs.appTheme
|
||||
if (theme == "black") {
|
||||
setTheme(R.style.TuskyDialogActivityBlackTheme)
|
||||
|
@ -175,7 +176,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(activeAccount)
|
||||
setupAvatar(prefs, activeAccount)
|
||||
val mediaAdapter = MediaPreviewAdapter(
|
||||
this,
|
||||
onAddCaption = { item ->
|
||||
|
@ -211,7 +212,7 @@ class ComposeActivity :
|
|||
binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt)
|
||||
}
|
||||
|
||||
setupComposeField(viewModel.startingText)
|
||||
setupComposeField(prefs, viewModel.startingText)
|
||||
setupContentWarningField(composeOptions?.contentWarning)
|
||||
setupPollView()
|
||||
applyShareIntent(intent, savedInstanceState)
|
||||
|
@ -296,7 +297,7 @@ class ComposeActivity :
|
|||
binding.composeContentWarningField.onTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() }
|
||||
}
|
||||
|
||||
private fun setupComposeField(startingText: String?) {
|
||||
private fun setupComposeField(prefs: PrefData, startingText: String?) {
|
||||
binding.composeEditField.setOnReceiveContentListener(this)
|
||||
|
||||
binding.composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) }
|
||||
|
@ -314,9 +315,9 @@ class ComposeActivity :
|
|||
binding.composeEditField.setSelection(binding.composeEditField.length())
|
||||
|
||||
val mentionColour = binding.composeEditField.linkTextColors.defaultColor
|
||||
highlightSpans(binding.composeEditField.text, mentionColour)
|
||||
highlightSpans(binding.composeEditField.text, mentionColour, prefs.customTabs)
|
||||
binding.composeEditField.afterTextChanged { editable ->
|
||||
highlightSpans(editable, mentionColour)
|
||||
highlightSpans(editable, mentionColour, prefs.customTabs)
|
||||
updateVisibleCharactersLeft()
|
||||
}
|
||||
|
||||
|
@ -430,7 +431,7 @@ class ComposeActivity :
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupAvatar(activeAccount: AccountEntity) {
|
||||
private fun setupAvatar(prefs: PrefData, activeAccount: AccountEntity) {
|
||||
val actionBarSizeAttr = intArrayOf(R.attr.actionBarSize)
|
||||
val a = obtainStyledAttributes(null, actionBarSizeAttr)
|
||||
val avatarSize = a.getDimensionPixelSize(0, 1)
|
||||
|
|
|
@ -25,7 +25,6 @@ import androidx.fragment.app.viewModels
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.LoadState
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
|
@ -38,8 +37,8 @@ import com.keylesspalace.tusky.di.ViewModelFactory
|
|||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.CardViewMode
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
|
@ -58,7 +57,7 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefsStore: PrefStore
|
||||
|
||||
private val viewModel: ConversationsViewModel by viewModels { viewModelFactory }
|
||||
|
||||
|
@ -76,6 +75,7 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val prefs = prefsStore.getBlocking()
|
||||
val statusDisplayOptions = StatusDisplayOptions(
|
||||
animateAvatars = prefs.animateAvatars,
|
||||
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
||||
|
|
|
@ -23,7 +23,6 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.LoadState
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
|
@ -42,8 +41,8 @@ import com.keylesspalace.tusky.di.Injectable
|
|||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.CardViewMode
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
|
@ -62,7 +61,7 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
lateinit var accountManager: AccountManager
|
||||
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
private val viewModel: ReportViewModel by activityViewModels { viewModelFactory }
|
||||
|
||||
|
@ -109,6 +108,7 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
}
|
||||
|
||||
private fun initStatusesView() {
|
||||
val prefs = prefStore.getBlocking()
|
||||
val statusDisplayOptions = StatusDisplayOptions(
|
||||
animateAvatars = false,
|
||||
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
||||
|
|
|
@ -17,23 +17,23 @@ package com.keylesspalace.tusky.components.search.fragments
|
|||
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.PagingDataAdapter
|
||||
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 com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.get
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class SearchAccountsFragment : SearchFragment<TimelineAccount>() {
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
override fun createAdapter(): PagingDataAdapter<TimelineAccount, *> {
|
||||
return SearchAccountsAdapter(
|
||||
this,
|
||||
animateAvatars = prefs.animateAvatars,
|
||||
animateEmojis = prefs.animateEmojis,
|
||||
animateAvatars = prefStore.getBlocking().animateAvatars,
|
||||
animateEmojis = prefStore.getBlocking().animateEmojis,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ import androidx.core.view.ViewCompat
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.PagingDataAdapter
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from
|
||||
|
@ -52,8 +51,8 @@ import com.keylesspalace.tusky.entity.Status
|
|||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.CardViewMode
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.util.openLink
|
||||
|
@ -66,7 +65,7 @@ import javax.inject.Inject
|
|||
|
||||
class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), StatusActionListener {
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
override val data: Flow<PagingData<StatusViewData.Concrete>>
|
||||
get() = viewModel.statusesFlow
|
||||
|
@ -75,6 +74,7 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
get() = super.adapter as SearchStatusesAdapter
|
||||
|
||||
override fun createAdapter(): PagingDataAdapter<StatusViewData.Concrete, *> {
|
||||
val prefs = prefStore.getBlocking()
|
||||
val statusDisplayOptions = StatusDisplayOptions(
|
||||
animateAvatars = prefs.animateAvatars,
|
||||
mediaPreviewEnabled = viewModel.mediaPreviewEnabled,
|
||||
|
@ -88,8 +88,14 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
animateEmojis = prefs.animateEmojis,
|
||||
)
|
||||
|
||||
binding.searchRecyclerView.addItemDecoration(DividerItemDecoration(binding.searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||
binding.searchRecyclerView.layoutManager = LinearLayoutManager(binding.searchRecyclerView.context)
|
||||
binding.searchRecyclerView.addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
binding.searchRecyclerView.context,
|
||||
DividerItemDecoration.VERTICAL
|
||||
)
|
||||
)
|
||||
binding.searchRecyclerView.layoutManager =
|
||||
LinearLayoutManager(binding.searchRecyclerView.context)
|
||||
return SearchStatusesAdapter(statusDisplayOptions, this)
|
||||
}
|
||||
|
||||
|
@ -145,7 +151,10 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
}
|
||||
}
|
||||
Attachment.Type.UNKNOWN -> {
|
||||
context?.openLink(actionable.attachments[attachmentIndex].url)
|
||||
context?.openLink(
|
||||
actionable.attachments[attachmentIndex].url,
|
||||
prefStore.getBlocking().customTabs,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -241,7 +250,8 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
menu.findItem(R.id.status_open_as).isVisible = !statusUrl.isNullOrBlank()
|
||||
when (status.visibility) {
|
||||
Status.Visibility.PUBLIC, Status.Visibility.UNLISTED -> {
|
||||
val textId = getString(if (status.isPinned()) R.string.unpin_action else R.string.pin_action)
|
||||
val textId =
|
||||
getString(if (status.isPinned()) R.string.unpin_action else R.string.pin_action)
|
||||
menu.add(0, R.id.pin, 1, textId)
|
||||
}
|
||||
Status.Visibility.PRIVATE -> {
|
||||
|
@ -267,7 +277,8 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
openAsItem.title = openAsText
|
||||
}
|
||||
|
||||
val mutable = statusIsByCurrentUser || accountIsInMentions(viewModel.activeAccount, status.mentions)
|
||||
val mutable =
|
||||
statusIsByCurrentUser || accountIsInMentions(viewModel.activeAccount, status.mentions)
|
||||
val muteConversationItem = popup.menu.findItem(R.id.status_mute_conversation).apply {
|
||||
isVisible = mutable
|
||||
}
|
||||
|
@ -290,11 +301,16 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
sendIntent.action = Intent.ACTION_SEND
|
||||
|
||||
val stringToShare = statusToShare.account.username +
|
||||
" - " +
|
||||
statusToShare.content.toString()
|
||||
" - " +
|
||||
statusToShare.content.toString()
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, stringToShare)
|
||||
sendIntent.type = "text/plain"
|
||||
startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_status_content_to)))
|
||||
startActivity(
|
||||
Intent.createChooser(
|
||||
sendIntent,
|
||||
resources.getText(R.string.send_status_content_to)
|
||||
)
|
||||
)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
R.id.status_share_link -> {
|
||||
|
@ -302,11 +318,17 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
sendIntent.action = Intent.ACTION_SEND
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, statusUrl)
|
||||
sendIntent.type = "text/plain"
|
||||
startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_status_link_to)))
|
||||
startActivity(
|
||||
Intent.createChooser(
|
||||
sendIntent,
|
||||
resources.getText(R.string.send_status_link_to)
|
||||
)
|
||||
)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
R.id.status_copy_link -> {
|
||||
val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipboard =
|
||||
requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText(null, statusUrl))
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
@ -402,7 +424,8 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
val uri = Uri.parse(url)
|
||||
val filename = uri.lastPathSegment
|
||||
|
||||
val downloadManager = requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val downloadManager =
|
||||
requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val request = DownloadManager.Request(uri)
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename)
|
||||
downloadManager.enqueue(request)
|
||||
|
@ -415,13 +438,24 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
downloadAllMedia(status)
|
||||
} else {
|
||||
Toast.makeText(context, R.string.error_media_download_permission, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
context,
|
||||
R.string.error_media_download_permission,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openReportPage(accountId: String, accountUsername: String, statusId: String) {
|
||||
startActivity(ReportActivity.getIntent(requireContext(), accountId, accountUsername, statusId))
|
||||
startActivity(
|
||||
ReportActivity.getIntent(
|
||||
requireContext(),
|
||||
accountId,
|
||||
accountUsername,
|
||||
statusId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun showConfirmDeleteDialog(id: String, position: Int) {
|
||||
|
@ -471,7 +505,8 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
},
|
||||
{ error ->
|
||||
Log.w("SearchStatusesFragment", "error deleting status", error)
|
||||
Toast.makeText(context, R.string.error_generic, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, R.string.error_generic, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import androidx.lifecycle.Lifecycle
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.LoadState
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -56,7 +55,9 @@ 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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.get
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.CardViewMode
|
||||
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
|
@ -67,10 +68,13 @@ import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
|||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.rx3.asFlow
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlin.concurrent.timer
|
||||
|
||||
class TimelineFragment :
|
||||
SFragment(),
|
||||
|
@ -90,7 +94,7 @@ class TimelineFragment :
|
|||
lateinit var accountManager: AccountManager
|
||||
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
private val viewModel: TimelineViewModel by lazy {
|
||||
if (kind == TimelineViewModel.Kind.HOME) {
|
||||
|
@ -140,6 +144,7 @@ class TimelineFragment :
|
|||
|
||||
isSwipeToRefreshEnabled = arguments.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, true)
|
||||
|
||||
val prefs = prefStore.getBlocking()
|
||||
val statusDisplayOptions = StatusDisplayOptions(
|
||||
animateAvatars = prefs.animateAvatars,
|
||||
mediaPreviewEnabled = accountManager.activeAccount!!.mediaPreviewEnabled,
|
||||
|
@ -238,7 +243,7 @@ class TimelineFragment :
|
|||
}
|
||||
|
||||
if (actionButtonPresent()) {
|
||||
hideFab = prefs.hideFab
|
||||
hideFab = prefStore.getBlocking().hideFab
|
||||
scrollListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
|
||||
val composeButton = (activity as ActionButtonActivity).actionButton
|
||||
|
@ -259,20 +264,21 @@ class TimelineFragment :
|
|||
}
|
||||
}
|
||||
|
||||
eventHub.events
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDispose(this, Lifecycle.Event.ON_DESTROY)
|
||||
.subscribe { event ->
|
||||
when (event) {
|
||||
is PreferenceChangedEvent -> {
|
||||
onPreferenceChanged(event.preferenceKey)
|
||||
}
|
||||
is StatusComposedEvent -> {
|
||||
val status = event.status
|
||||
handleStatusComposeEvent(status)
|
||||
lifecycleScope.launch {
|
||||
eventHub.events
|
||||
.asFlow()
|
||||
.collect { event ->
|
||||
when (event) {
|
||||
is PreferenceChangedEvent -> {
|
||||
onPreferenceChanged(event.preferenceKey)
|
||||
}
|
||||
is StatusComposedEvent -> {
|
||||
val status = event.status
|
||||
handleStatusComposeEvent(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSwipeRefreshLayout() {
|
||||
|
@ -283,7 +289,7 @@ class TimelineFragment :
|
|||
|
||||
private fun setupRecyclerView() {
|
||||
binding.recyclerView.setAccessibilityDelegateCompat(
|
||||
ListStatusAccessibilityDelegate(binding.recyclerView, this) { pos ->
|
||||
ListStatusAccessibilityDelegate(binding.recyclerView, this, prefStore) { pos ->
|
||||
if (pos in 0 until adapter.itemCount) {
|
||||
adapter.peek(pos)
|
||||
} else {
|
||||
|
@ -413,10 +419,10 @@ class TimelineFragment :
|
|||
super.viewAccount(id)
|
||||
}
|
||||
|
||||
private fun onPreferenceChanged(key: String) {
|
||||
private suspend fun onPreferenceChanged(key: String) {
|
||||
when (key) {
|
||||
PrefKeys.FAB_HIDE -> {
|
||||
hideFab = prefs.hideFab
|
||||
hideFab = prefStore.get().hideFab
|
||||
}
|
||||
PrefKeys.MEDIA_PREVIEW_ENABLED -> {
|
||||
val enabled = accountManager.activeAccount!!.mediaPreviewEnabled
|
||||
|
@ -471,7 +477,9 @@ class TimelineFragment :
|
|||
if (talkBackWasEnabled && !wasEnabled) {
|
||||
adapter.notifyItemRangeChanged(0, adapter.itemCount)
|
||||
}
|
||||
startUpdateTimestamp()
|
||||
lifecycleScope.launchWhenResumed {
|
||||
startUpdateTimestamp()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -479,8 +487,8 @@ class TimelineFragment :
|
|||
* If setting absoluteTimeView is false
|
||||
* Auto dispose observable on pause
|
||||
*/
|
||||
private fun startUpdateTimestamp() {
|
||||
val useAbsoluteTime = prefs.useAbsoluteTime
|
||||
private suspend fun startUpdateTimestamp() {
|
||||
val useAbsoluteTime = prefStore.get().useAbsoluteTime
|
||||
if (!useAbsoluteTime) {
|
||||
Observable.interval(1, TimeUnit.MINUTES)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
|
@ -40,7 +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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.util.dec
|
||||
import com.keylesspalace.tusky.util.inc
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
|
@ -61,7 +61,7 @@ class CachedTimelineViewModel @Inject constructor(
|
|||
private val api: MastodonApi,
|
||||
eventHub: EventHub,
|
||||
accountManager: AccountManager,
|
||||
prefs: Prefs,
|
||||
prefs: PrefStore,
|
||||
filterModel: FilterModel,
|
||||
private val db: AppDatabase,
|
||||
private val gson: Gson
|
||||
|
|
|
@ -34,7 +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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.util.dec
|
||||
import com.keylesspalace.tusky.util.getDomain
|
||||
import com.keylesspalace.tusky.util.inc
|
||||
|
@ -58,7 +58,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
|||
private val api: MastodonApi,
|
||||
eventHub: EventHub,
|
||||
accountManager: AccountManager,
|
||||
prefs: Prefs,
|
||||
prefs: PrefStore,
|
||||
filterModel: FilterModel
|
||||
) : TimelineViewModel(timelineCases, api, eventHub, accountManager, prefs, filterModel) {
|
||||
|
||||
|
|
|
@ -40,7 +40,9 @@ 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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.get
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -53,7 +55,7 @@ abstract class TimelineViewModel(
|
|||
private val api: MastodonApi,
|
||||
private val eventHub: EventHub,
|
||||
protected val accountManager: AccountManager,
|
||||
private val prefs: Prefs,
|
||||
private val prefStore: PrefStore,
|
||||
private val filterModel: FilterModel
|
||||
) : ViewModel() {
|
||||
|
||||
|
@ -81,6 +83,7 @@ abstract class TimelineViewModel(
|
|||
this.tags = tags
|
||||
|
||||
if (kind == Kind.HOME) {
|
||||
val prefs = prefStore.getBlocking()
|
||||
filterRemoveReplies = !prefs.tabFilterHomeReplies
|
||||
filterRemoveReblogs = !prefs.tabFilterHomeBoosts
|
||||
}
|
||||
|
@ -177,10 +180,10 @@ abstract class TimelineViewModel(
|
|||
filterModel.shouldFilterStatus(status.actionableStatus)
|
||||
}
|
||||
|
||||
private fun onPreferenceChanged(key: String) {
|
||||
private suspend fun onPreferenceChanged(key: String) {
|
||||
when (key) {
|
||||
PrefKeys.TAB_FILTER_HOME_REPLIES -> {
|
||||
val filter = prefs.tabFilterHomeReplies
|
||||
val filter = prefStore.get().tabFilterHomeReplies
|
||||
val oldRemoveReplies = filterRemoveReplies
|
||||
filterRemoveReplies = kind == Kind.HOME && !filter
|
||||
if (oldRemoveReplies != filterRemoveReplies) {
|
||||
|
@ -188,7 +191,7 @@ abstract class TimelineViewModel(
|
|||
}
|
||||
}
|
||||
PrefKeys.TAB_FILTER_HOME_BOOSTS -> {
|
||||
val filter = prefs.tabFilterHomeBoosts
|
||||
val filter = prefStore.get().tabFilterHomeBoosts
|
||||
val oldRemoveReblogs = filterRemoveReblogs
|
||||
filterRemoveReblogs = kind == Kind.HOME && !filter
|
||||
if (oldRemoveReblogs != filterRemoveReblogs) {
|
||||
|
@ -230,7 +233,7 @@ abstract class TimelineViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleEvent(event: Event) {
|
||||
private suspend fun handleEvent(event: Event) {
|
||||
when (event) {
|
||||
is FavoriteEvent -> handleFavEvent(event)
|
||||
is ReblogEvent -> handleReblogEvent(event)
|
||||
|
|
|
@ -18,13 +18,19 @@ package com.keylesspalace.tusky.di
|
|||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.room.Room
|
||||
import com.keylesspalace.tusky.TuskyApplication
|
||||
import com.keylesspalace.tusky.db.AppDatabase
|
||||
import com.keylesspalace.tusky.db.Converters
|
||||
import com.keylesspalace.tusky.settings.PrefData
|
||||
import com.keylesspalace.tusky.settings.makePrefStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
|
@ -45,6 +51,13 @@ class AppModule {
|
|||
return PreferenceManager.getDefaultSharedPreferences(app)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesDataStore(app: Application): DataStore<PrefData> {
|
||||
// Scope is the copy of default one
|
||||
return makePrefStore(app, CoroutineScope(Dispatchers.IO + SupervisorJob()))
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesDatabase(appContext: Context, converters: Converters): AppDatabase {
|
||||
|
@ -52,16 +65,34 @@ class AppModule {
|
|||
.addTypeConverter(converters)
|
||||
.allowMainThreadQueries()
|
||||
.addMigrations(
|
||||
AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5,
|
||||
AppDatabase.MIGRATION_5_6, AppDatabase.MIGRATION_6_7, AppDatabase.MIGRATION_7_8,
|
||||
AppDatabase.MIGRATION_8_9, AppDatabase.MIGRATION_9_10, AppDatabase.MIGRATION_10_11,
|
||||
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
|
||||
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
|
||||
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
|
||||
AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22,
|
||||
AppDatabase.MIGRATION_22_23, AppDatabase.MIGRATION_23_24, AppDatabase.MIGRATION_24_25,
|
||||
AppDatabase.MIGRATION_2_3,
|
||||
AppDatabase.MIGRATION_3_4,
|
||||
AppDatabase.MIGRATION_4_5,
|
||||
AppDatabase.MIGRATION_5_6,
|
||||
AppDatabase.MIGRATION_6_7,
|
||||
AppDatabase.MIGRATION_7_8,
|
||||
AppDatabase.MIGRATION_8_9,
|
||||
AppDatabase.MIGRATION_9_10,
|
||||
AppDatabase.MIGRATION_10_11,
|
||||
AppDatabase.MIGRATION_11_12,
|
||||
AppDatabase.MIGRATION_12_13,
|
||||
AppDatabase.MIGRATION_10_13,
|
||||
AppDatabase.MIGRATION_13_14,
|
||||
AppDatabase.MIGRATION_14_15,
|
||||
AppDatabase.MIGRATION_15_16,
|
||||
AppDatabase.MIGRATION_16_17,
|
||||
AppDatabase.MIGRATION_17_18,
|
||||
AppDatabase.MIGRATION_18_19,
|
||||
AppDatabase.MIGRATION_19_20,
|
||||
AppDatabase.MIGRATION_20_21,
|
||||
AppDatabase.MIGRATION_21_22,
|
||||
AppDatabase.MIGRATION_22_23,
|
||||
AppDatabase.MIGRATION_23_24,
|
||||
AppDatabase.MIGRATION_24_25,
|
||||
AppDatabase.Migration25_26(appContext.getExternalFilesDir("Tusky")),
|
||||
AppDatabase.MIGRATION_26_27, AppDatabase.MIGRATION_27_28, AppDatabase.MIGRATION_28_29,
|
||||
AppDatabase.MIGRATION_26_27,
|
||||
AppDatabase.MIGRATION_27_28,
|
||||
AppDatabase.MIGRATION_28_29,
|
||||
AppDatabase.MIGRATION_29_30
|
||||
)
|
||||
.build()
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package com.keylesspalace.tusky.di
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.text.Spanned
|
||||
import com.google.gson.Gson
|
||||
|
@ -26,8 +25,8 @@ 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 com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import okhttp3.Cache
|
||||
|
@ -63,8 +62,9 @@ class NetworkModule {
|
|||
fun providesHttpClient(
|
||||
accountManager: AccountManager,
|
||||
context: Context,
|
||||
prefs: Prefs,
|
||||
prefStore: PrefStore,
|
||||
): OkHttpClient {
|
||||
val prefs = prefStore.getBlocking()
|
||||
val httpProxyEnabled = prefs.httpProxyEnabled
|
||||
val httpServer = prefs.httpProxyServer
|
||||
val httpPort = prefs.httpProxyPort.toIntOrNull() ?: -1
|
||||
|
|
|
@ -20,7 +20,6 @@ import android.util.Log
|
|||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.ConcatAdapter
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
@ -46,8 +45,8 @@ import com.keylesspalace.tusky.entity.Relationship
|
|||
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.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
|
@ -67,7 +66,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
@Inject
|
||||
lateinit var accountManager: AccountManager
|
||||
@Inject
|
||||
lateinit var prefs: Prefs
|
||||
lateinit var prefStore: PrefStore
|
||||
|
||||
private val binding by viewBinding(FragmentAccountListBinding::bind)
|
||||
|
||||
|
@ -95,6 +94,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
|||
|
||||
binding.recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||
|
||||
val prefs = prefStore.getBlocking()
|
||||
val animateAvatar = prefs.animateAvatars
|
||||
val animateEmojis = prefs.animateEmojis
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
|
||||
package com.keylesspalace.tusky.fragment;
|
||||
|
||||
import static com.keylesspalace.tusky.util.StringUtils.isLessThan;
|
||||
import static autodispose2.AutoDispose.autoDisposable;
|
||||
import static autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
@ -36,8 +40,8 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.arch.core.util.Function;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.datastore.core.DataStore;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.AsyncDifferConfig;
|
||||
import androidx.recyclerview.widget.AsyncListDiffer;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
|
@ -71,6 +75,7 @@ import com.keylesspalace.tusky.interfaces.AccountActionListener;
|
|||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.settings.PrefData;
|
||||
import com.keylesspalace.tusky.settings.PrefKeys;
|
||||
import com.keylesspalace.tusky.settings.Prefs;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
|
@ -111,10 +116,6 @@ import kotlin.Unit;
|
|||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
import static autodispose2.AutoDispose.autoDisposable;
|
||||
import static autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from;
|
||||
import static com.keylesspalace.tusky.util.StringUtils.isLessThan;
|
||||
|
||||
public class NotificationsFragment extends SFragment implements
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
StatusActionListener,
|
||||
|
@ -157,7 +158,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
@Inject
|
||||
EventHub eventHub;
|
||||
@Inject
|
||||
Prefs prefs;
|
||||
DataStore<PrefData> prefStore;
|
||||
|
||||
private SwipeRefreshLayout swipeRefreshLayout;
|
||||
private RecyclerView recyclerView;
|
||||
|
@ -216,6 +217,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
@NonNull Context context = inflater.getContext(); // from inflater to silence warning
|
||||
|
||||
PrefData prefs = Prefs.getBlocking(prefStore);
|
||||
boolean showNotificationsFilterSetting = prefs.getShowNotificationsFilter();
|
||||
//Clear notifications on filter visibility change to force refresh
|
||||
if (showNotificationsFilterSetting != showNotificationsFilter)
|
||||
|
@ -239,7 +241,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
layoutManager = new LinearLayoutManager(context);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
recyclerView.setAccessibilityDelegateCompat(
|
||||
new ListStatusAccessibilityDelegate(recyclerView, this, (pos) -> {
|
||||
new ListStatusAccessibilityDelegate(recyclerView, this, prefStore, (pos) -> {
|
||||
NotificationViewData notification = notifications.getPairedItemOrNull(pos);
|
||||
// We support replies only for now
|
||||
if (notification instanceof NotificationViewData.Concrete) {
|
||||
|
@ -329,7 +331,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
// 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();
|
||||
|
||||
hideFab = Prefs.getBlocking(prefStore).getHideFab();
|
||||
scrollListener = new EndlessOnScrollListener(layoutManager) {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView view, int dx, int dy) {
|
||||
|
@ -799,7 +802,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
private void onPreferenceChanged(String key) {
|
||||
switch (key) {
|
||||
case PrefKeys.FAB_HIDE: {
|
||||
hideFab = prefs.getHideFab();
|
||||
hideFab = Prefs.getBlocking(prefStore).getHideFab();
|
||||
break;
|
||||
}
|
||||
case PrefKeys.MEDIA_PREVIEW_ENABLED: {
|
||||
|
@ -812,7 +815,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
case PrefKeys.SHOW_NOTIFICATIONS_FILTER: {
|
||||
if (isAdded()) {
|
||||
showNotificationsFilter = prefs.getShowNotificationsFilter();
|
||||
showNotificationsFilter = Prefs.getBlocking(prefStore).getShowNotificationsFilter();
|
||||
updateFilterVisibility();
|
||||
fullyRefreshWithProgressBar(true);
|
||||
}
|
||||
|
@ -1224,7 +1227,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
* Auto dispose observable on pause
|
||||
*/
|
||||
private void startUpdateTimestamp() {
|
||||
boolean useAbsoluteTime = prefs.getUseAbsoluteTime();
|
||||
boolean useAbsoluteTime = Prefs.getBlocking(prefStore).getUseAbsoluteTime();
|
||||
if (!useAbsoluteTime) {
|
||||
Observable.interval(1, TimeUnit.MINUTES)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
|
@ -36,6 +36,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.core.app.ActivityOptionsCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.datastore.core.DataStore;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
|
||||
|
@ -55,6 +56,8 @@ import com.keylesspalace.tusky.entity.Attachment;
|
|||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.network.TimelineCases;
|
||||
import com.keylesspalace.tusky.settings.PrefData;
|
||||
import com.keylesspalace.tusky.settings.Prefs;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.view.MuteAccountDialog;
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData;
|
||||
|
@ -91,6 +94,8 @@ public abstract class SFragment extends Fragment implements Injectable {
|
|||
public AccountManager accountManager;
|
||||
@Inject
|
||||
public TimelineCases timelineCases;
|
||||
@Inject
|
||||
public DataStore<PrefData> prefStore;
|
||||
|
||||
private static final String TAG = "SFragment";
|
||||
|
||||
|
@ -363,7 +368,11 @@ public abstract class SFragment extends Fragment implements Injectable {
|
|||
}
|
||||
default:
|
||||
case UNKNOWN: {
|
||||
LinkHelper.openLink(requireContext(), active.getAttachment().getUrl());
|
||||
LinkHelper.openLink(
|
||||
requireContext(),
|
||||
active.getAttachment().getUrl(),
|
||||
Prefs.getBlocking(prefStore).getCustomTabs()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.view.ViewGroup;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.datastore.core.DataStore;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
|
@ -57,6 +58,7 @@ import com.keylesspalace.tusky.entity.Status;
|
|||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.network.FilterModel;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.settings.PrefData;
|
||||
import com.keylesspalace.tusky.settings.PrefKeys;
|
||||
import com.keylesspalace.tusky.settings.Prefs;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
|
@ -94,7 +96,7 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
@Inject
|
||||
public FilterModel filterModel;
|
||||
@Inject
|
||||
public Prefs prefs;
|
||||
public DataStore<PrefData> prefStore;
|
||||
|
||||
private SwipeRefreshLayout swipeRefreshLayout;
|
||||
private RecyclerView recyclerView;
|
||||
|
@ -132,6 +134,7 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
|
||||
thisThreadsStatusId = getArguments().getString("id");
|
||||
|
||||
PrefData prefs = Prefs.getBlocking(prefStore);
|
||||
StatusDisplayOptions statusDisplayOptions = new StatusDisplayOptions(
|
||||
prefs.getAnimateAvatars(),
|
||||
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
||||
|
@ -164,7 +167,7 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
recyclerView.setAccessibilityDelegateCompat(
|
||||
new ListStatusAccessibilityDelegate(recyclerView, this, statuses::getPairedItemOrNull));
|
||||
new ListStatusAccessibilityDelegate(recyclerView, this, prefStore, statuses::getPairedItemOrNull));
|
||||
DividerItemDecoration divider = new DividerItemDecoration(
|
||||
context, layoutManager.getOrientation());
|
||||
recyclerView.addItemDecoration(divider);
|
||||
|
@ -330,7 +333,7 @@ public final class ViewThreadFragment extends SFragment implements
|
|||
// already viewing the status with this url
|
||||
// probably just a preview federated and the user is clicking again to view more -> open the browser
|
||||
// this can happen with some friendica statuses
|
||||
LinkHelper.openLink(requireContext(), url);
|
||||
LinkHelper.openLink(requireContext(), url, Prefs.getBlocking(prefStore).getCustomTabs());
|
||||
return;
|
||||
}
|
||||
super.onViewUrl(url);
|
||||
|
|
|
@ -1,129 +1,81 @@
|
|||
@file:JvmName("Prefs")
|
||||
|
||||
package com.keylesspalace.tusky.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.annotation.Keep
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.core.DataStoreFactory
|
||||
import androidx.datastore.core.Serializer
|
||||
import com.google.gson.Gson
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
// 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)
|
||||
data class PrefData(
|
||||
var appTheme: String = ThemeUtils.APP_THEME_DEFAULT,
|
||||
var emojiFont: Int = 0,
|
||||
val hideFab: Boolean = false,
|
||||
var language: String = "default",
|
||||
val statusTextSize: String = "medium",
|
||||
val mainNavPosition: String? = null,
|
||||
val hideTopToolbar: Boolean = false,
|
||||
val animateAvatars: Boolean = true,
|
||||
val useAbsoluteTime: Boolean = false,
|
||||
val showBotOverlay: Boolean = true,
|
||||
val useBlurhash: Boolean = true,
|
||||
val showNotificationsFilter: Boolean = true,
|
||||
val showCardsInTimelines: Boolean = false,
|
||||
val confirmReblogs: Boolean = true,
|
||||
val confirmFavourites: Boolean = false,
|
||||
val enableSwipeForTabs: Boolean = true,
|
||||
val customTabs: Boolean = false,
|
||||
val hideStatsPosts: Boolean = false,
|
||||
val hideStatsProfile: Boolean = false,
|
||||
val animateEmojis: Boolean = false,
|
||||
val tabFilterHomeReplies: Boolean = true,
|
||||
val tabFilterHomeBoosts: Boolean = true,
|
||||
|
||||
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 httpProxyEnabled: Boolean = false,
|
||||
val httpProxyServer: String = "",
|
||||
val httpProxyPort: String = ""
|
||||
)
|
||||
|
||||
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)
|
||||
abstract class GsonSerializer<T>(
|
||||
private val classOfData: Class<T>,
|
||||
) : Serializer<T> {
|
||||
private val gson = Gson()
|
||||
|
||||
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 suspend fun readFrom(input: InputStream): T {
|
||||
return gson.fromJson(input.reader(), classOfData)
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: T, property: KProperty<*>, value: P) {
|
||||
this@withDefault.setValue(thisRef, property, value)
|
||||
override suspend fun writeTo(t: T, output: OutputStream) {
|
||||
gson.toJson(t, output.writer())
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
object PrefDataSerializer : GsonSerializer<PrefData>(PrefData::class.java) {
|
||||
override val defaultValue: PrefData
|
||||
get() = PrefData()
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
fun <T> DataStore<T>.getBlocking() = runBlocking { this@getBlocking.data.first() }
|
||||
suspend fun <T> DataStore<T>.get() = this.data.first()
|
||||
|
||||
override fun setValue(thisRef: Prefs, property: KProperty<*>, value: Boolean) {
|
||||
sharedPreferences.edit().putBoolean(overrideName ?: property.name, value)
|
||||
.apply()
|
||||
}
|
||||
}
|
||||
typealias PrefStore = DataStore<PrefData>
|
||||
|
||||
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,
|
||||
)
|
||||
/** Exposed for special cases, please inject singleton instead! */
|
||||
fun makePrefStore(context: Context, scope: CoroutineScope): PrefStore {
|
||||
return DataStoreFactory.create(
|
||||
PrefDataSerializer,
|
||||
scope = scope,
|
||||
) {
|
||||
// Would love to use dataStoreFile() here but it needs app context which we might not have
|
||||
// yet.
|
||||
File(context.filesDir, "datastore/prefs.json")
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Prefs, property: KProperty<*>, value: Int) {
|
||||
sharedPreferences.edit().putInt(overrideName ?: property.name, value)
|
||||
.apply()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,12 +32,13 @@ import androidx.annotation.VisibleForTesting
|
|||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.net.toUri
|
||||
import androidx.preference.PreferenceManager
|
||||
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
|
||||
import com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.settings.makePrefStore
|
||||
|
||||
fun getDomain(urlString: String?): String {
|
||||
val host = urlString?.toUri()?.host
|
||||
|
@ -57,7 +58,13 @@ fun getDomain(urlString: String?): String {
|
|||
* @param mentions any '@' mentions which are known to be in the content
|
||||
* @param listener to notify about particular spans that are clicked
|
||||
*/
|
||||
fun setClickableText(view: TextView, content: CharSequence, mentions: List<Mention>, tags: List<HashTag>?, listener: LinkListener) {
|
||||
fun setClickableText(
|
||||
view: TextView,
|
||||
content: CharSequence,
|
||||
mentions: List<Mention>,
|
||||
tags: List<HashTag>?,
|
||||
listener: LinkListener
|
||||
) {
|
||||
view.text = SpannableStringBuilder.valueOf(content).apply {
|
||||
getSpans(0, content.length, URLSpan::class.java).forEach {
|
||||
setClickableText(it, this, mentions, tags, listener)
|
||||
|
@ -83,7 +90,8 @@ fun setClickableText(
|
|||
'#' -> getCustomSpanForTag(text, tags, span, listener)
|
||||
'@' -> getCustomSpanForMention(mentions, span, listener)
|
||||
else -> null
|
||||
} ?: object : NoUnderlineURLSpan(span.url) {
|
||||
} ?: object : NoUnderlineURLSpan(span.url, false) {
|
||||
// It doesn't matter what we pass for customTabs because we override onCLick() anyway
|
||||
override fun onClick(view: View) = listener.onViewUrl(url)
|
||||
}
|
||||
|
||||
|
@ -107,23 +115,36 @@ fun getTagName(text: CharSequence, tags: List<HashTag>?): String? {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getCustomSpanForTag(text: CharSequence, tags: List<HashTag>?, span: URLSpan, listener: LinkListener): ClickableSpan? {
|
||||
private fun getCustomSpanForTag(
|
||||
text: CharSequence,
|
||||
tags: List<HashTag>?,
|
||||
span: URLSpan,
|
||||
listener: LinkListener
|
||||
): ClickableSpan? {
|
||||
return getTagName(text, tags)?.let {
|
||||
object : NoUnderlineURLSpan(span.url) {
|
||||
object : NoUnderlineURLSpan(span.url, false) {
|
||||
override fun onClick(view: View) = listener.onViewTag(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCustomSpanForMention(mentions: List<Mention>, span: URLSpan, listener: LinkListener): ClickableSpan? {
|
||||
private fun getCustomSpanForMention(
|
||||
mentions: List<Mention>,
|
||||
span: URLSpan,
|
||||
listener: LinkListener
|
||||
): ClickableSpan? {
|
||||
// https://github.com/tuskyapp/Tusky/pull/2339
|
||||
return mentions.firstOrNull { it.url == span.url }?.let {
|
||||
getCustomSpanForMentionUrl(span.url, it.id, listener)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCustomSpanForMentionUrl(url: String, mentionId: String, listener: LinkListener): ClickableSpan {
|
||||
return object : NoUnderlineURLSpan(url) {
|
||||
private fun getCustomSpanForMentionUrl(
|
||||
url: String,
|
||||
mentionId: String,
|
||||
listener: LinkListener
|
||||
): ClickableSpan {
|
||||
return object : NoUnderlineURLSpan(url, false) {
|
||||
override fun onClick(view: View) = listener.onViewAccount(mentionId)
|
||||
}
|
||||
}
|
||||
|
@ -171,9 +192,14 @@ fun setClickableMentions(view: TextView, mentions: List<Mention>?, listener: Lin
|
|||
view.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
||||
fun createClickableText(text: String, link: String): CharSequence {
|
||||
fun createClickableText(text: String, link: String, useCustomTabs: Boolean): CharSequence {
|
||||
return SpannableStringBuilder(text).apply {
|
||||
setSpan(NoUnderlineURLSpan(link), 0, text.length, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
setSpan(
|
||||
NoUnderlineURLSpan(link, useCustomTabs),
|
||||
0,
|
||||
text.length,
|
||||
Spanned.SPAN_INCLUSIVE_EXCLUSIVE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,12 +208,11 @@ fun createClickableText(text: String, link: String): CharSequence {
|
|||
*
|
||||
* @receiver the Context to open the link from
|
||||
* @param url a string containing the url to open
|
||||
* @param customTabs whether to use customs tabs or open link in system browser
|
||||
*/
|
||||
fun Context.openLink(url: String) {
|
||||
fun Context.openLink(url: String, customTabs: Boolean) {
|
||||
val uri = url.toUri().normalizeScheme()
|
||||
val useCustomTabs = Prefs(this).customTabs
|
||||
|
||||
if (useCustomTabs) {
|
||||
if (customTabs) {
|
||||
openLinkInCustomTab(uri, this)
|
||||
} else {
|
||||
openLinkInBrowser(uri, this)
|
||||
|
|
|
@ -18,6 +18,8 @@ import com.keylesspalace.tusky.R
|
|||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder
|
||||
import com.keylesspalace.tusky.entity.Status.Companion.MAX_MEDIA_ATTACHMENTS
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import kotlin.math.min
|
||||
|
||||
|
@ -29,10 +31,11 @@ fun interface StatusProvider {
|
|||
class ListStatusAccessibilityDelegate(
|
||||
private val recyclerView: RecyclerView,
|
||||
private val statusActionListener: StatusActionListener,
|
||||
private val statusProvider: StatusProvider
|
||||
private val prefStore: PrefStore,
|
||||
private val statusProvider: StatusProvider,
|
||||
) : RecyclerViewAccessibilityDelegate(recyclerView) {
|
||||
private val a11yManager = context.getSystemService(Context.ACCESSIBILITY_SERVICE)
|
||||
as AccessibilityManager
|
||||
as AccessibilityManager
|
||||
|
||||
override fun getItemDelegate(): AccessibilityDelegateCompat = itemDelegate
|
||||
|
||||
|
@ -182,7 +185,12 @@ class ListStatusAccessibilityDelegate(
|
|||
android.R.layout.simple_list_item_1,
|
||||
textLinks
|
||||
)
|
||||
) { _, which -> host.context.openLink(links[which].link) }
|
||||
) { _, which ->
|
||||
host.context.openLink(
|
||||
links[which].link,
|
||||
prefStore.getBlocking().customTabs,
|
||||
)
|
||||
}
|
||||
.show()
|
||||
.let { forceFocus(it.listView) }
|
||||
}
|
||||
|
|
|
@ -17,15 +17,16 @@ package com.keylesspalace.tusky.util
|
|||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import com.keylesspalace.tusky.settings.Prefs
|
||||
import com.keylesspalace.tusky.settings.PrefStore
|
||||
import com.keylesspalace.tusky.settings.getBlocking
|
||||
import java.util.*
|
||||
|
||||
class LocaleManager(
|
||||
private val prefs: Prefs,
|
||||
private val prefs: PrefStore,
|
||||
) {
|
||||
|
||||
fun setLocale(context: Context): Context {
|
||||
val language = prefs.language
|
||||
val language = prefs.getBlocking().language
|
||||
if (language == "default") {
|
||||
return context
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@ import android.text.style.URLSpan
|
|||
import android.view.View
|
||||
|
||||
open class NoUnderlineURLSpan(
|
||||
url: String
|
||||
url: String,
|
||||
private val useCustomTabs: Boolean,
|
||||
) : URLSpan(url) {
|
||||
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
|
@ -29,6 +30,6 @@ open class NoUnderlineURLSpan(
|
|||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
view.context.openLink(url)
|
||||
view.context.openLink(url, useCustomTabs)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,16 +124,23 @@ private fun findEndOfPattern(string: String, result: FindCharsResult, pattern: P
|
|||
}
|
||||
}
|
||||
|
||||
private fun getSpan(matchType: FoundMatchType, string: String, colour: Int, start: Int, end: Int): CharacterStyle {
|
||||
private fun getSpan(
|
||||
matchType: FoundMatchType,
|
||||
string: String,
|
||||
colour: Int,
|
||||
start: Int,
|
||||
end: Int,
|
||||
useCustomTabs: Boolean,
|
||||
): CharacterStyle {
|
||||
return when (matchType) {
|
||||
FoundMatchType.HTTP_URL -> NoUnderlineURLSpan(string.substring(start, end))
|
||||
FoundMatchType.HTTPS_URL -> NoUnderlineURLSpan(string.substring(start, end))
|
||||
FoundMatchType.HTTP_URL -> NoUnderlineURLSpan(string.substring(start, end), useCustomTabs)
|
||||
FoundMatchType.HTTPS_URL -> NoUnderlineURLSpan(string.substring(start, end), useCustomTabs)
|
||||
else -> ForegroundColorSpan(colour)
|
||||
}
|
||||
}
|
||||
|
||||
/** Takes text containing mentions and hashtags and urls and makes them the given colour. */
|
||||
fun highlightSpans(text: Spannable, colour: Int) {
|
||||
fun highlightSpans(text: Spannable, colour: Int, useCustomTabs: Boolean) {
|
||||
// Strip all existing colour spans.
|
||||
for (spanClass in spanClasses) {
|
||||
clearSpans(text, spanClass)
|
||||
|
@ -150,7 +157,7 @@ fun highlightSpans(text: Spannable, colour: Int) {
|
|||
start = found.start
|
||||
end = found.end
|
||||
if (start >= 0 && end > start) {
|
||||
text.setSpan(getSpan(found.matchType, string, colour, start, end), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
text.setSpan(getSpan(found.matchType, string, colour, start, end, useCustomTabs), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
start += finders[found.matchType]!!.searchPrefixWidth
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class LicenseCard
|
|||
binding.licenseCardLink.hide()
|
||||
} else {
|
||||
binding.licenseCardLink.text = link
|
||||
setOnClickListener { context.openLink(link) }
|
||||
setOnClickListener { context.openLink(link, false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue