Merge branch 'develop' into prompt_to_save_before_leaving_changed_profile
This commit is contained in:
commit
84915e6af5
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -256,9 +256,8 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
|||
|
||||
public void openAsAccount(@NonNull String url, @NonNull AccountEntity account) {
|
||||
accountManager.setActiveAccount(account.getId());
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
intent.putExtra(MainActivity.REDIRECT_URL, url);
|
||||
Intent intent = MainActivity.redirectIntent(this, account.getId(), url);
|
||||
|
||||
startActivity(intent);
|
||||
finishWithoutSlideOutAnimation();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
package com.keylesspalace.tusky
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
|
@ -33,6 +35,7 @@ import android.view.KeyEvent
|
|||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.MenuItem.SHOW_AS_ACTION_NEVER
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
|
@ -41,9 +44,12 @@ import androidx.appcompat.content.res.AppCompatResources
|
|||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.IntentCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.viewpager2.widget.MarginPageTransformer
|
||||
|
@ -100,7 +106,6 @@ import com.keylesspalace.tusky.util.show
|
|||
import com.keylesspalace.tusky.util.unsafeLazy
|
||||
import com.keylesspalace.tusky.util.updateShortcut
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
|
@ -175,6 +180,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
/** Adapter for the different timeline tabs */
|
||||
private lateinit var tabAdapter: MainPagerAdapter
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@ -182,30 +188,39 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
?: return // will be redirected to LoginActivity by BaseActivity
|
||||
|
||||
var showNotificationTab = false
|
||||
if (intent != null) {
|
||||
|
||||
// check for savedInstanceState in order to not handle intent events more than once
|
||||
if (intent != null && savedInstanceState == null) {
|
||||
val notificationId = intent.getIntExtra(NOTIFICATION_ID, -1)
|
||||
if (notificationId != -1) {
|
||||
// opened from a notification action, cancel the notification
|
||||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.cancel(intent.getStringExtra(NOTIFICATION_TAG), notificationId)
|
||||
}
|
||||
|
||||
/** there are two possibilities the accountId can be passed to MainActivity:
|
||||
* - from our code as long 'account_id'
|
||||
* - from our code as Long Intent Extra TUSKY_ACCOUNT_ID
|
||||
* - from share shortcuts as String 'android.intent.extra.shortcut.ID'
|
||||
*/
|
||||
var accountId = intent.getLongExtra(NotificationHelper.ACCOUNT_ID, -1)
|
||||
if (accountId == -1L) {
|
||||
var tuskyAccountId = intent.getLongExtra(TUSKY_ACCOUNT_ID, -1)
|
||||
if (tuskyAccountId == -1L) {
|
||||
val accountIdString = intent.getStringExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID)
|
||||
if (accountIdString != null) {
|
||||
accountId = accountIdString.toLong()
|
||||
tuskyAccountId = accountIdString.toLong()
|
||||
}
|
||||
}
|
||||
val accountRequested = accountId != -1L
|
||||
if (accountRequested && accountId != activeAccount.id) {
|
||||
accountManager.setActiveAccount(accountId)
|
||||
val accountRequested = tuskyAccountId != -1L
|
||||
if (accountRequested && tuskyAccountId != activeAccount.id) {
|
||||
accountManager.setActiveAccount(tuskyAccountId)
|
||||
}
|
||||
|
||||
val openDrafts = intent.getBooleanExtra(OPEN_DRAFTS, false)
|
||||
|
||||
if (canHandleMimeType(intent.type)) {
|
||||
if (canHandleMimeType(intent.type) || intent.hasExtra(COMPOSE_OPTIONS)) {
|
||||
// Sharing to Tusky from an external app
|
||||
if (accountRequested) {
|
||||
// The correct account is already active
|
||||
forwardShare(intent)
|
||||
forwardToComposeActivity(intent)
|
||||
} else {
|
||||
// No account was provided, show the chooser
|
||||
showAccountChooserDialog(
|
||||
|
@ -216,10 +231,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
val requestedId = account.id
|
||||
if (requestedId == activeAccount.id) {
|
||||
// The correct account is already active
|
||||
forwardShare(intent)
|
||||
forwardToComposeActivity(intent)
|
||||
} else {
|
||||
// A different account was requested, restart the activity
|
||||
intent.putExtra(NotificationHelper.ACCOUNT_ID, requestedId)
|
||||
intent.putExtra(TUSKY_ACCOUNT_ID, requestedId)
|
||||
changeAccount(requestedId, intent)
|
||||
}
|
||||
}
|
||||
|
@ -229,10 +244,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
} else if (openDrafts) {
|
||||
val intent = DraftsActivity.newIntent(this)
|
||||
startActivity(intent)
|
||||
} else if (accountRequested && savedInstanceState == null) {
|
||||
} else if (accountRequested && intent.hasExtra(NOTIFICATION_TYPE)) {
|
||||
// user clicked a notification, show follow requests for type FOLLOW_REQUEST,
|
||||
// otherwise show notification tab
|
||||
if (intent.getStringExtra(NotificationHelper.TYPE) == Notification.Type.FOLLOW_REQUEST.name) {
|
||||
if (intent.getSerializableExtra(NOTIFICATION_TYPE) == Notification.Type.FOLLOW_REQUEST) {
|
||||
val intent = AccountListActivity.newIntent(this, AccountListActivity.Type.FOLLOW_REQUESTS)
|
||||
startActivityWithSlideInAnimation(intent)
|
||||
} else {
|
||||
|
@ -242,7 +257,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.mainToolbar)
|
||||
|
||||
glide = Glide.with(this)
|
||||
|
||||
|
@ -251,8 +265,21 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
startActivity(composeIntent)
|
||||
}
|
||||
|
||||
// Determine which of the three toolbars should be the supportActionBar (which hosts
|
||||
// the options menu).
|
||||
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
|
||||
binding.mainToolbar.visible(!hideTopToolbar)
|
||||
if (hideTopToolbar) {
|
||||
when (preferences.getString(PrefKeys.MAIN_NAV_POSITION, "top")) {
|
||||
"top" -> setSupportActionBar(binding.topNav)
|
||||
"bottom" -> setSupportActionBar(binding.bottomNav)
|
||||
}
|
||||
binding.mainToolbar.hide()
|
||||
// There's not enough space in the top/bottom bars to show the title as well.
|
||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
} else {
|
||||
setSupportActionBar(binding.mainToolbar)
|
||||
binding.mainToolbar.show()
|
||||
}
|
||||
|
||||
loadDrawerAvatar(activeAccount.profilePictureUrl, true)
|
||||
|
||||
|
@ -263,7 +290,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
setupDrawer(
|
||||
savedInstanceState,
|
||||
addSearchButton = hideTopToolbar,
|
||||
addTrendingButton = !accountManager.activeAccount!!.tabPreferences.hasTab(TRENDING)
|
||||
addTrendingTagsButton = !accountManager.activeAccount!!.tabPreferences.hasTab(TRENDING_TAGS)
|
||||
)
|
||||
|
||||
/* Fetch user info while we're doing other things. This has to be done after setting up the
|
||||
|
@ -288,7 +315,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
is MainTabsChangedEvent -> {
|
||||
refreshMainDrawerItems(
|
||||
addSearchButton = hideTopToolbar,
|
||||
addTrendingButton = !event.newTabs.hasTab(TRENDING)
|
||||
addTrendingTagsButton = !event.newTabs.hasTab(TRENDING_TAGS)
|
||||
)
|
||||
|
||||
setupTabs(false)
|
||||
|
@ -350,6 +377,14 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
super.onPrepareMenu(menu)
|
||||
|
||||
// If the main toolbar is hidden then there's no space in the top/bottomNav to show
|
||||
// the menu items as icons, so forceably disable them
|
||||
if (!binding.mainToolbar.isVisible) menu.forEach { it.setShowAsAction(SHOW_AS_ACTION_NEVER) }
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.action_search -> {
|
||||
|
@ -422,12 +457,19 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
}
|
||||
|
||||
private fun forwardShare(intent: Intent) {
|
||||
val composeIntent = Intent(this, ComposeActivity::class.java)
|
||||
composeIntent.action = intent.action
|
||||
composeIntent.type = intent.type
|
||||
composeIntent.putExtras(intent)
|
||||
composeIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
private fun forwardToComposeActivity(intent: Intent) {
|
||||
val composeOptions = IntentCompat.getParcelableExtra(intent, COMPOSE_OPTIONS, ComposeActivity.ComposeOptions::class.java)
|
||||
|
||||
val composeIntent = if (composeOptions != null) {
|
||||
ComposeActivity.startIntent(this, composeOptions)
|
||||
} else {
|
||||
Intent(this, ComposeActivity::class.java).apply {
|
||||
action = intent.action
|
||||
type = intent.type
|
||||
putExtras(intent)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
}
|
||||
startActivity(composeIntent)
|
||||
finish()
|
||||
}
|
||||
|
@ -435,13 +477,13 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
private fun setupDrawer(
|
||||
savedInstanceState: Bundle?,
|
||||
addSearchButton: Boolean,
|
||||
addTrendingButton: Boolean
|
||||
addTrendingTagsButton: Boolean
|
||||
) {
|
||||
val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() }
|
||||
|
||||
binding.mainToolbar.setNavigationOnClickListener(drawerOpenClickListener)
|
||||
binding.topNavAvatar.setOnClickListener(drawerOpenClickListener)
|
||||
binding.bottomNavAvatar.setOnClickListener(drawerOpenClickListener)
|
||||
binding.topNav.setNavigationOnClickListener(drawerOpenClickListener)
|
||||
binding.bottomNav.setNavigationOnClickListener(drawerOpenClickListener)
|
||||
|
||||
header = AccountHeaderView(this).apply {
|
||||
headerBackgroundScaleType = ImageView.ScaleType.CENTER_CROP
|
||||
|
@ -496,12 +538,12 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
})
|
||||
|
||||
binding.mainDrawer.apply {
|
||||
refreshMainDrawerItems(addSearchButton, addTrendingButton)
|
||||
refreshMainDrawerItems(addSearchButton, addTrendingTagsButton)
|
||||
setSavedInstance(savedInstanceState)
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshMainDrawerItems(addSearchButton: Boolean, addTrendingButton: Boolean) {
|
||||
private fun refreshMainDrawerItems(addSearchButton: Boolean, addTrendingTagsButton: Boolean) {
|
||||
binding.mainDrawer.apply {
|
||||
itemAdapter.clear()
|
||||
tintStatusBar = true
|
||||
|
@ -618,7 +660,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
)
|
||||
}
|
||||
|
||||
if (addTrendingButton) {
|
||||
if (addTrendingTagsButton) {
|
||||
binding.mainDrawer.addItemsAtPosition(
|
||||
5,
|
||||
primaryDrawerItem {
|
||||
|
@ -876,112 +918,75 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
|
||||
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
|
||||
|
||||
if (hideTopToolbar) {
|
||||
val activeToolbar = if (hideTopToolbar) {
|
||||
val navOnBottom = preferences.getString("mainNavPosition", "top") == "bottom"
|
||||
|
||||
val avatarView = if (navOnBottom) {
|
||||
binding.bottomNavAvatar.show()
|
||||
binding.bottomNavAvatar
|
||||
if (navOnBottom) {
|
||||
binding.bottomNav
|
||||
} else {
|
||||
binding.topNavAvatar.show()
|
||||
binding.topNavAvatar
|
||||
}
|
||||
|
||||
if (animateAvatars) {
|
||||
Glide.with(this)
|
||||
.load(avatarUrl)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatarView)
|
||||
} else {
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(avatarUrl)
|
||||
.placeholder(R.drawable.avatar_default)
|
||||
.into(avatarView)
|
||||
binding.topNav
|
||||
}
|
||||
} else {
|
||||
binding.bottomNavAvatar.hide()
|
||||
binding.topNavAvatar.hide()
|
||||
binding.mainToolbar
|
||||
}
|
||||
|
||||
val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)
|
||||
val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)
|
||||
|
||||
if (animateAvatars) {
|
||||
glide.asDrawable()
|
||||
.load(avatarUrl)
|
||||
.transform(
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
|
||||
)
|
||||
.apply {
|
||||
if (showPlaceholder) {
|
||||
placeholder(R.drawable.avatar_default)
|
||||
if (animateAvatars) {
|
||||
glide.asDrawable().load(avatarUrl).transform(RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp)))
|
||||
.apply {
|
||||
if (showPlaceholder) placeholder(R.drawable.avatar_default)
|
||||
}
|
||||
.into(object : CustomTarget<Drawable>(navIconSize, navIconSize) {
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
.into(object : CustomTarget<Drawable>(navIconSize, navIconSize) {
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable>?
|
||||
) {
|
||||
if (resource is Animatable) resource.start()
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(resource, navIconSize, navIconSize)
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable>?
|
||||
) {
|
||||
if (resource is Animatable) {
|
||||
resource.start()
|
||||
}
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(resource, navIconSize, navIconSize)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
glide.asBitmap()
|
||||
.load(avatarUrl)
|
||||
.transform(
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
|
||||
)
|
||||
.apply {
|
||||
if (showPlaceholder) {
|
||||
placeholder(R.drawable.avatar_default)
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
.into(object : CustomTarget<Bitmap>(navIconSize, navIconSize) {
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
glide.asBitmap().load(avatarUrl).transform(RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp)))
|
||||
.apply {
|
||||
if (showPlaceholder) placeholder(R.drawable.avatar_default)
|
||||
}
|
||||
.into(object : CustomTarget<Bitmap>(navIconSize, navIconSize) {
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, 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>?
|
||||
) {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(
|
||||
BitmapDrawable(resources, resource),
|
||||
navIconSize,
|
||||
navIconSize
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
if (placeholder != null) {
|
||||
binding.mainToolbar.navigationIcon =
|
||||
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
|
||||
}
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1043,8 +1048,75 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
private const val TAG = "MainActivity" // logging tag
|
||||
private const val DRAWER_ITEM_ADD_ACCOUNT: Long = -13
|
||||
private const val DRAWER_ITEM_ANNOUNCEMENTS: Long = 14
|
||||
const val REDIRECT_URL = "redirectUrl"
|
||||
const val OPEN_DRAFTS = "draft"
|
||||
private const val REDIRECT_URL = "redirectUrl"
|
||||
private const val OPEN_DRAFTS = "draft"
|
||||
private const val TUSKY_ACCOUNT_ID = "tuskyAccountId"
|
||||
private const val COMPOSE_OPTIONS = "composeOptions"
|
||||
private const val NOTIFICATION_TYPE = "notificationType"
|
||||
private const val NOTIFICATION_TAG = "notificationTag"
|
||||
private const val NOTIFICATION_ID = "notificationId"
|
||||
|
||||
/**
|
||||
* Switches the active account to the provided accountId and then stays on MainActivity
|
||||
*/
|
||||
@JvmStatic
|
||||
fun accountSwitchIntent(context: Context, tuskyAccountId: Long): Intent {
|
||||
return Intent(context, MainActivity::class.java).apply {
|
||||
putExtra(TUSKY_ACCOUNT_ID, tuskyAccountId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the active account to the accountId and takes the user to the correct place according to the notification they clicked
|
||||
*/
|
||||
@JvmStatic
|
||||
fun openNotificationIntent(context: Context, tuskyAccountId: Long, type: Notification.Type): Intent {
|
||||
return accountSwitchIntent(context, tuskyAccountId).apply {
|
||||
putExtra(NOTIFICATION_TYPE, type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the active account to the accountId and then opens ComposeActivity with the provided options
|
||||
* @param tuskyAccountId the id of the Tusky account to open the screen with. Set to -1 for current account.
|
||||
* @param notificationId optional id of the notification that should be cancelled when this intent is opened
|
||||
* @param notificationTag optional tag of the notification that should be cancelled when this intent is opened
|
||||
*/
|
||||
@JvmStatic
|
||||
fun composeIntent(
|
||||
context: Context,
|
||||
options: ComposeActivity.ComposeOptions,
|
||||
tuskyAccountId: Long = -1,
|
||||
notificationTag: String? = null,
|
||||
notificationId: Int = -1
|
||||
): Intent {
|
||||
return accountSwitchIntent(context, tuskyAccountId).apply {
|
||||
action = Intent.ACTION_SEND // so it can be opened via shortcuts
|
||||
putExtra(COMPOSE_OPTIONS, options)
|
||||
putExtra(NOTIFICATION_TAG, notificationTag)
|
||||
putExtra(NOTIFICATION_ID, notificationId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* switches the active account to the accountId and then tries to resolve and show the provided url
|
||||
*/
|
||||
@JvmStatic
|
||||
fun redirectIntent(context: Context, tuskyAccountId: Long, url: String): Intent {
|
||||
return accountSwitchIntent(context, tuskyAccountId).apply {
|
||||
putExtra(REDIRECT_URL, url)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* switches the active account to the provided accountId and then opens drafts
|
||||
*/
|
||||
fun draftIntent(context: Context, tuskyAccountId: Long): Intent {
|
||||
return accountSwitchIntent(context, tuskyAccountId).apply {
|
||||
putExtra(OPEN_DRAFTS, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import com.keylesspalace.tusky.components.conversation.ConversationsFragment
|
|||
import com.keylesspalace.tusky.components.notifications.NotificationsFragment
|
||||
import com.keylesspalace.tusky.components.timeline.TimelineFragment
|
||||
import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel
|
||||
import com.keylesspalace.tusky.components.trending.TrendingFragment
|
||||
import com.keylesspalace.tusky.components.trending.TrendingTagsFragment
|
||||
import java.util.Objects
|
||||
|
||||
/** this would be a good case for a sealed class, but that does not work nice with Room */
|
||||
|
@ -33,7 +33,7 @@ const val NOTIFICATIONS = "Notifications"
|
|||
const val LOCAL = "Local"
|
||||
const val FEDERATED = "Federated"
|
||||
const val DIRECT = "Direct"
|
||||
const val TRENDING = "Trending"
|
||||
const val TRENDING_TAGS = "TrendingTags"
|
||||
const val HASHTAG = "Hashtag"
|
||||
const val LIST = "List"
|
||||
|
||||
|
@ -92,11 +92,11 @@ fun createTabDataFromId(id: String, arguments: List<String> = emptyList()): TabD
|
|||
icon = R.drawable.ic_reblog_direct_24dp,
|
||||
fragment = { ConversationsFragment.newInstance() }
|
||||
)
|
||||
TRENDING -> TabData(
|
||||
id = TRENDING,
|
||||
TRENDING_TAGS -> TabData(
|
||||
id = TRENDING_TAGS,
|
||||
text = R.string.title_public_trending_hashtags,
|
||||
icon = R.drawable.ic_trending_up_24px,
|
||||
fragment = { TrendingFragment.newInstance() }
|
||||
fragment = { TrendingTagsFragment.newInstance() }
|
||||
)
|
||||
HASHTAG -> TabData(
|
||||
id = HASHTAG,
|
||||
|
|
|
@ -378,9 +378,9 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
|||
if (!currentTabs.contains(directMessagesTab)) {
|
||||
addableTabs.add(directMessagesTab)
|
||||
}
|
||||
val trendingTab = createTabDataFromId(TRENDING)
|
||||
if (!currentTabs.contains(trendingTab)) {
|
||||
addableTabs.add(trendingTab)
|
||||
val trendingTagsTab = createTabDataFromId(TRENDING_TAGS)
|
||||
if (!currentTabs.contains(trendingTagsTab)) {
|
||||
addableTabs.add(trendingTagsTab)
|
||||
}
|
||||
|
||||
addableTabs.add(createTabDataFromId(HASHTAG))
|
||||
|
|
|
@ -130,6 +130,12 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
editor.remove(PrefKeys.MEDIA_PREVIEW_ENABLED)
|
||||
}
|
||||
|
||||
if (oldVersion < 2023072401) {
|
||||
// The notifications filter / clear options are shown on a menu, not a separate bar,
|
||||
// the preference to display them is not needed.
|
||||
editor.remove(PrefKeys.Deprecated.SHOW_NOTIFICATIONS_FILTER)
|
||||
}
|
||||
|
||||
editor.putInt(PrefKeys.SCHEMA_VERSION, newVersion)
|
||||
editor.apply()
|
||||
}
|
||||
|
|
|
@ -772,13 +772,16 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
loadedAccount?.let { loadedAccount ->
|
||||
val muteDomain = menu.findItem(R.id.action_mute_domain)
|
||||
domain = getDomain(loadedAccount.url)
|
||||
if (domain.isEmpty()) {
|
||||
when {
|
||||
// If we can't get the domain, there's no way we can mute it anyway...
|
||||
menu.removeItem(R.id.action_mute_domain)
|
||||
} else {
|
||||
if (blockingDomain) {
|
||||
// If the account is from our own domain, muting it is no-op
|
||||
domain.isEmpty() || viewModel.isFromOwnDomain -> {
|
||||
menu.removeItem(R.id.action_mute_domain)
|
||||
}
|
||||
blockingDomain -> {
|
||||
muteDomain.title = getString(R.string.action_unmute_domain, domain)
|
||||
} else {
|
||||
}
|
||||
else -> {
|
||||
muteDomain.title = getString(R.string.action_mute_domain, domain)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.keylesspalace.tusky.util.Error
|
|||
import com.keylesspalace.tusky.util.Loading
|
||||
import com.keylesspalace.tusky.util.Resource
|
||||
import com.keylesspalace.tusky.util.Success
|
||||
import com.keylesspalace.tusky.util.getDomain
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -27,7 +28,7 @@ import javax.inject.Inject
|
|||
class AccountViewModel @Inject constructor(
|
||||
private val mastodonApi: MastodonApi,
|
||||
private val eventHub: EventHub,
|
||||
private val accountManager: AccountManager
|
||||
accountManager: AccountManager
|
||||
) : ViewModel() {
|
||||
|
||||
val accountData = MutableLiveData<Resource<Account>>()
|
||||
|
@ -41,8 +42,13 @@ class AccountViewModel @Inject constructor(
|
|||
lateinit var accountId: String
|
||||
var isSelf = false
|
||||
|
||||
/** True if the viewed account has the same domain as the active account */
|
||||
var isFromOwnDomain = false
|
||||
|
||||
private var noteUpdateJob: Job? = null
|
||||
|
||||
private val activeAccount = accountManager.activeAccount!!
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
eventHub.events.collect { event ->
|
||||
|
@ -65,6 +71,8 @@ class AccountViewModel @Inject constructor(
|
|||
accountData.postValue(Success(account))
|
||||
isDataLoading = false
|
||||
isRefreshing.postValue(false)
|
||||
|
||||
isFromOwnDomain = getDomain(account.url) == activeAccount.domain
|
||||
},
|
||||
{ t ->
|
||||
Log.w(TAG, "failed obtaining account", t)
|
||||
|
@ -298,7 +306,7 @@ class AccountViewModel @Inject constructor(
|
|||
|
||||
fun setAccountInfo(accountId: String) {
|
||||
this.accountId = accountId
|
||||
this.isSelf = accountManager.activeAccount?.accountId == accountId
|
||||
this.isSelf = activeAccount.accountId == accountId
|
||||
reload(false)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package com.keylesspalace.tusky.components.compose
|
||||
|
||||
import android.Manifest
|
||||
import android.app.NotificationManager
|
||||
import android.app.ProgressDialog
|
||||
import android.content.ClipData
|
||||
import android.content.Context
|
||||
|
@ -207,22 +206,7 @@ class ComposeActivity :
|
|||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val notificationId = intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1)
|
||||
if (notificationId != -1) {
|
||||
// ComposeActivity was opened from a notification, delete the notification
|
||||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.cancel(notificationId)
|
||||
}
|
||||
|
||||
// If started from an intent then compose as the account ID from the intent.
|
||||
// Otherwise use the active account. If null then the user is not logged in,
|
||||
// and return from the activity.
|
||||
val intentAccountId = intent.getLongExtra(ACCOUNT_ID_EXTRA, -1)
|
||||
activeAccount = if (intentAccountId != -1L) {
|
||||
accountManager.getAccountById(intentAccountId)
|
||||
} else {
|
||||
accountManager.activeAccount
|
||||
} ?: return
|
||||
activeAccount = accountManager.activeAccount ?: return
|
||||
|
||||
val theme = preferences.getString("appTheme", APP_THEME_DEFAULT)
|
||||
if (theme == "black") {
|
||||
|
@ -280,7 +264,7 @@ class ComposeActivity :
|
|||
binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt)
|
||||
}
|
||||
|
||||
setupLanguageSpinner(getInitialLanguages(composeOptions?.language, accountManager.activeAccount))
|
||||
setupLanguageSpinner(getInitialLanguages(composeOptions?.language, activeAccount))
|
||||
setupComposeField(preferences, viewModel.startingText)
|
||||
setupContentWarningField(composeOptions?.contentWarning)
|
||||
setupPollView()
|
||||
|
@ -1355,8 +1339,6 @@ class ComposeActivity :
|
|||
private const val PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1
|
||||
|
||||
internal const val COMPOSE_OPTIONS_EXTRA = "COMPOSE_OPTIONS"
|
||||
private const val NOTIFICATION_ID_EXTRA = "NOTIFICATION_ID"
|
||||
private const val ACCOUNT_ID_EXTRA = "ACCOUNT_ID"
|
||||
private const val PHOTO_UPLOAD_URI_KEY = "PHOTO_UPLOAD_URI"
|
||||
private const val VISIBILITY_KEY = "VISIBILITY"
|
||||
private const val SCHEDULED_TIME_KEY = "SCHEDULE"
|
||||
|
@ -1364,26 +1346,15 @@ class ComposeActivity :
|
|||
|
||||
/**
|
||||
* @param options ComposeOptions to configure the ComposeActivity
|
||||
* @param notificationId the id of the notification that starts the Activity
|
||||
* @param accountId the id of the account to compose with, null for the current account
|
||||
* @return an Intent to start the ComposeActivity
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun startIntent(
|
||||
context: Context,
|
||||
options: ComposeOptions,
|
||||
notificationId: Int? = null,
|
||||
accountId: Long? = null
|
||||
options: ComposeOptions
|
||||
): Intent {
|
||||
return Intent(context, ComposeActivity::class.java).apply {
|
||||
putExtra(COMPOSE_OPTIONS_EXTRA, options)
|
||||
if (notificationId != null) {
|
||||
putExtra(NOTIFICATION_ID_EXTRA, notificationId)
|
||||
}
|
||||
if (accountId != null) {
|
||||
putExtra(ACCOUNT_ID_EXTRA, accountId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,13 +85,6 @@ public class NotificationHelper {
|
|||
/** Dynamic notification IDs start here */
|
||||
private static int notificationId = NOTIFICATION_ID_PRUNE_CACHE + 1;
|
||||
|
||||
/**
|
||||
* constants used in Intents
|
||||
*/
|
||||
public static final String ACCOUNT_ID = "account_id";
|
||||
|
||||
public static final String TYPE = APPLICATION_ID + ".notification.type";
|
||||
|
||||
private static final String TAG = "NotificationHelper";
|
||||
|
||||
public static final String REPLY_ACTION = "REPLY_ACTION";
|
||||
|
@ -325,11 +318,10 @@ public class NotificationHelper {
|
|||
// Create a notification that summarises the other notifications in this group
|
||||
|
||||
// All notifications in this group have the same type, so get it from the first.
|
||||
String notificationType = members.get(0).getNotification().extras.getString(EXTRA_NOTIFICATION_TYPE);
|
||||
Notification.Type notificationType = (Notification.Type)members.get(0).getNotification().extras.getSerializable(EXTRA_NOTIFICATION_TYPE);
|
||||
|
||||
Intent summaryResultIntent = MainActivity.openNotificationIntent(context, accountId, notificationType);
|
||||
|
||||
Intent summaryResultIntent = new Intent(context, MainActivity.class);
|
||||
summaryResultIntent.putExtra(ACCOUNT_ID, (long) accountId);
|
||||
summaryResultIntent.putExtra(TYPE, notificationType);
|
||||
TaskStackBuilder summaryStackBuilder = TaskStackBuilder.create(context);
|
||||
summaryStackBuilder.addParentStack(MainActivity.class);
|
||||
summaryStackBuilder.addNextIntent(summaryResultIntent);
|
||||
|
@ -373,10 +365,8 @@ public class NotificationHelper {
|
|||
|
||||
private static NotificationCompat.Builder newAndroidNotification(Context context, Notification body, AccountEntity account) {
|
||||
|
||||
// we have to switch account here
|
||||
Intent eventResultIntent = new Intent(context, MainActivity.class);
|
||||
eventResultIntent.putExtra(ACCOUNT_ID, account.getId());
|
||||
eventResultIntent.putExtra(TYPE, body.getType().name());
|
||||
Intent eventResultIntent = MainActivity.openNotificationIntent(context, account.getId(), body.getType());
|
||||
|
||||
TaskStackBuilder eventStackBuilder = TaskStackBuilder.create(context);
|
||||
eventStackBuilder.addParentStack(MainActivity.class);
|
||||
eventStackBuilder.addNextIntent(eventResultIntent);
|
||||
|
@ -464,12 +454,7 @@ public class NotificationHelper {
|
|||
composeOptions.setLanguage(actionableStatus.getLanguage());
|
||||
composeOptions.setKind(ComposeActivity.ComposeKind.NEW);
|
||||
|
||||
Intent composeIntent = ComposeActivity.startIntent(
|
||||
context,
|
||||
composeOptions,
|
||||
notificationId,
|
||||
account.getId()
|
||||
);
|
||||
Intent composeIntent = MainActivity.composeIntent(context, composeOptions, account.getId(), body.getId(), (int)account.getId());
|
||||
|
||||
composeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
|
@ -46,7 +45,6 @@ import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
|
|||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import at.connyduck.sparkbutton.helpers.Utils
|
||||
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.R
|
||||
|
@ -123,21 +121,6 @@ class NotificationsFragment :
|
|||
return inflater.inflate(R.layout.fragment_timeline_notifications, container, false)
|
||||
}
|
||||
|
||||
private fun updateFilterVisibility(showFilter: Boolean) {
|
||||
val params = binding.swipeRefreshLayout.layoutParams as CoordinatorLayout.LayoutParams
|
||||
if (showFilter) {
|
||||
binding.appBarOptions.setExpanded(true, false)
|
||||
binding.appBarOptions.visibility = View.VISIBLE
|
||||
// Set content behaviour to hide filter on scroll
|
||||
params.behavior = ScrollingViewBehavior()
|
||||
} else {
|
||||
binding.appBarOptions.setExpanded(false, false)
|
||||
binding.appBarOptions.visibility = View.GONE
|
||||
// Clear behaviour to hide app bar
|
||||
params.behavior = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun confirmClearNotifications() {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setMessage(R.string.notification_clear_text)
|
||||
|
@ -215,8 +198,6 @@ class NotificationsFragment :
|
|||
footer = NotificationsLoadStateAdapter { adapter.retry() }
|
||||
)
|
||||
|
||||
binding.buttonClear.setOnClickListener { confirmClearNotifications() }
|
||||
binding.buttonFilter.setOnClickListener { showFilterDialog() }
|
||||
(binding.recyclerView.itemAnimator as SimpleItemAnimator?)!!.supportsChangeAnimations =
|
||||
false
|
||||
|
||||
|
@ -369,10 +350,10 @@ class NotificationsFragment :
|
|||
}
|
||||
}
|
||||
|
||||
// Update filter option visibility from uiState
|
||||
launch {
|
||||
viewModel.uiState.collectLatest { updateFilterVisibility(it.showFilterOptions) }
|
||||
}
|
||||
// Collect the uiState. Nothing is done with it, but if you don't collect it then
|
||||
// accessing viewModel.uiState.value (e.g., when the filter dialog is created)
|
||||
// returns an empty object.
|
||||
launch { viewModel.uiState.collect() }
|
||||
|
||||
// Update status display from statusDisplayOptions. If the new options request
|
||||
// relative time display collect the flow to periodically update the timestamp in the list gui elements.
|
||||
|
@ -439,10 +420,17 @@ class NotificationsFragment :
|
|||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_notifications, menu)
|
||||
val iconColor = MaterialColors.getColor(binding.root, android.R.attr.textColorPrimary)
|
||||
menu.findItem(R.id.action_refresh)?.apply {
|
||||
icon = IconicsDrawable(requireContext(), GoogleMaterial.Icon.gmd_refresh).apply {
|
||||
sizeDp = 20
|
||||
colorInt = MaterialColors.getColor(binding.root, android.R.attr.textColorPrimary)
|
||||
colorInt = iconColor
|
||||
}
|
||||
}
|
||||
menu.findItem(R.id.action_edit_notification_filter)?.apply {
|
||||
icon = IconicsDrawable(requireContext(), GoogleMaterial.Icon.gmd_tune).apply {
|
||||
sizeDp = 20
|
||||
colorInt = iconColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -458,6 +446,14 @@ class NotificationsFragment :
|
|||
viewModel.accept(InfallibleUiAction.LoadNewest)
|
||||
true
|
||||
}
|
||||
R.id.action_edit_notification_filter -> {
|
||||
showFilterDialog()
|
||||
true
|
||||
}
|
||||
R.id.action_clear_notifications -> {
|
||||
confirmClearNotifications()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +621,6 @@ class NotificationsFragment :
|
|||
|
||||
override fun onReselect() {
|
||||
if (isAdded) {
|
||||
binding.appBarOptions.setExpanded(true, false)
|
||||
layoutManager.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,23 +74,18 @@ data class UiState(
|
|||
/** Filtered notification types */
|
||||
val activeFilter: Set<Notification.Type> = emptySet(),
|
||||
|
||||
/** True if the UI to filter and clear notifications should be shown */
|
||||
val showFilterOptions: Boolean = false,
|
||||
|
||||
/** True if the FAB should be shown while scrolling */
|
||||
val showFabWhileScrolling: Boolean = true
|
||||
)
|
||||
|
||||
/** Preferences the UI reacts to */
|
||||
data class UiPrefs(
|
||||
val showFabWhileScrolling: Boolean,
|
||||
val showFilter: Boolean
|
||||
val showFabWhileScrolling: Boolean
|
||||
) {
|
||||
companion object {
|
||||
/** Relevant preference keys. Changes to any of these trigger a display update */
|
||||
val prefKeys = setOf(
|
||||
PrefKeys.FAB_HIDE,
|
||||
PrefKeys.SHOW_NOTIFICATIONS_FILTER
|
||||
PrefKeys.FAB_HIDE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -495,7 +490,6 @@ class NotificationsViewModel @Inject constructor(
|
|||
uiState = combine(notificationFilter, getUiPrefs()) { filter, prefs ->
|
||||
UiState(
|
||||
activeFilter = filter.filter,
|
||||
showFilterOptions = prefs.showFilter,
|
||||
showFabWhileScrolling = prefs.showFabWhileScrolling
|
||||
)
|
||||
}.stateIn(
|
||||
|
@ -544,8 +538,7 @@ class NotificationsViewModel @Inject constructor(
|
|||
.onStart { emit(toPrefs()) }
|
||||
|
||||
private fun toPrefs() = UiPrefs(
|
||||
showFabWhileScrolling = !preferences.getBoolean(PrefKeys.FAB_HIDE, false),
|
||||
showFilter = preferences.getBoolean(PrefKeys.SHOW_NOTIFICATIONS_FILTER, true)
|
||||
showFabWhileScrolling = !preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -208,13 +208,6 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
isSingleLineTitle = false
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
setDefaultValue(true)
|
||||
key = PrefKeys.SHOW_NOTIFICATIONS_FILTER
|
||||
setTitle(R.string.pref_title_show_notifications_filter)
|
||||
isSingleLineTitle = false
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
setDefaultValue(true)
|
||||
key = PrefKeys.CONFIRM_REBLOGS
|
||||
|
|
|
@ -48,7 +48,7 @@ class TrendingActivity : BaseActivity(), HasAndroidInjector {
|
|||
|
||||
if (supportFragmentManager.findFragmentById(R.id.fragmentContainer) == null) {
|
||||
supportFragmentManager.commit {
|
||||
val fragment = TrendingFragment.newInstance()
|
||||
val fragment = TrendingTagsFragment.newInstance()
|
||||
replace(R.id.fragmentContainer, fragment)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import com.keylesspalace.tusky.databinding.ItemTrendingCellBinding
|
|||
import com.keylesspalace.tusky.databinding.ItemTrendingDateBinding
|
||||
import com.keylesspalace.tusky.viewdata.TrendingViewData
|
||||
|
||||
class TrendingAdapter(
|
||||
class TrendingTagsAdapter(
|
||||
private val onViewTag: (String) -> Unit
|
||||
) : ListAdapter<TrendingViewData, RecyclerView.ViewHolder>(TrendingDifferCallback) {
|
||||
|
|
@ -33,8 +33,8 @@ import at.connyduck.sparkbutton.helpers.Utils
|
|||
import com.keylesspalace.tusky.BaseActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.StatusListActivity
|
||||
import com.keylesspalace.tusky.components.trending.viewmodel.TrendingViewModel
|
||||
import com.keylesspalace.tusky.databinding.FragmentTrendingBinding
|
||||
import com.keylesspalace.tusky.components.trending.viewmodel.TrendingTagsViewModel
|
||||
import com.keylesspalace.tusky.databinding.FragmentTrendingTagsBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||
|
@ -48,8 +48,8 @@ import kotlinx.coroutines.flow.collectLatest
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class TrendingFragment :
|
||||
Fragment(R.layout.fragment_trending),
|
||||
class TrendingTagsFragment :
|
||||
Fragment(R.layout.fragment_trending_tags),
|
||||
OnRefreshListener,
|
||||
Injectable,
|
||||
ReselectableFragment,
|
||||
|
@ -58,11 +58,11 @@ class TrendingFragment :
|
|||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
||||
private val viewModel: TrendingViewModel by viewModels { viewModelFactory }
|
||||
private val viewModel: TrendingTagsViewModel by viewModels { viewModelFactory }
|
||||
|
||||
private val binding by viewBinding(FragmentTrendingBinding::bind)
|
||||
private val binding by viewBinding(FragmentTrendingTagsBinding::bind)
|
||||
|
||||
private val adapter = TrendingAdapter(::onViewTag)
|
||||
private val adapter = TrendingTagsAdapter(::onViewTag)
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
@ -111,8 +111,8 @@ class TrendingFragment :
|
|||
spanSizeLookup = object : SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return when (adapter.getItemViewType(position)) {
|
||||
TrendingAdapter.VIEW_TYPE_HEADER -> columnCount
|
||||
TrendingAdapter.VIEW_TYPE_TAG -> 1
|
||||
TrendingTagsAdapter.VIEW_TYPE_HEADER -> columnCount
|
||||
TrendingTagsAdapter.VIEW_TYPE_TAG -> 1
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
@ -139,15 +139,15 @@ class TrendingFragment :
|
|||
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag))
|
||||
}
|
||||
|
||||
private fun processViewState(uiState: TrendingViewModel.TrendingUiState) {
|
||||
private fun processViewState(uiState: TrendingTagsViewModel.TrendingTagsUiState) {
|
||||
Log.d(TAG, uiState.loadingState.name)
|
||||
when (uiState.loadingState) {
|
||||
TrendingViewModel.LoadingState.INITIAL -> clearLoadingState()
|
||||
TrendingViewModel.LoadingState.LOADING -> applyLoadingState()
|
||||
TrendingViewModel.LoadingState.REFRESHING -> applyRefreshingState()
|
||||
TrendingViewModel.LoadingState.LOADED -> applyLoadedState(uiState.trendingViewData)
|
||||
TrendingViewModel.LoadingState.ERROR_NETWORK -> networkError()
|
||||
TrendingViewModel.LoadingState.ERROR_OTHER -> otherError()
|
||||
TrendingTagsViewModel.LoadingState.INITIAL -> clearLoadingState()
|
||||
TrendingTagsViewModel.LoadingState.LOADING -> applyLoadingState()
|
||||
TrendingTagsViewModel.LoadingState.REFRESHING -> applyRefreshingState()
|
||||
TrendingTagsViewModel.LoadingState.LOADED -> applyLoadedState(uiState.trendingViewData)
|
||||
TrendingTagsViewModel.LoadingState.ERROR_NETWORK -> networkError()
|
||||
TrendingTagsViewModel.LoadingState.ERROR_OTHER -> otherError()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,8 +247,8 @@ class TrendingFragment :
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "TrendingFragment"
|
||||
private const val TAG = "TrendingTagsFragment"
|
||||
|
||||
fun newInstance() = TrendingFragment()
|
||||
fun newInstance() = TrendingTagsFragment()
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ import kotlinx.coroutines.launch
|
|||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
class TrendingViewModel @Inject constructor(
|
||||
class TrendingTagsViewModel @Inject constructor(
|
||||
private val mastodonApi: MastodonApi,
|
||||
private val eventHub: EventHub
|
||||
) : ViewModel() {
|
||||
|
@ -43,13 +43,13 @@ class TrendingViewModel @Inject constructor(
|
|||
INITIAL, LOADING, REFRESHING, LOADED, ERROR_NETWORK, ERROR_OTHER
|
||||
}
|
||||
|
||||
data class TrendingUiState(
|
||||
data class TrendingTagsUiState(
|
||||
val trendingViewData: List<TrendingViewData>,
|
||||
val loadingState: LoadingState
|
||||
)
|
||||
|
||||
val uiState: Flow<TrendingUiState> get() = _uiState
|
||||
private val _uiState = MutableStateFlow(TrendingUiState(listOf(), LoadingState.INITIAL))
|
||||
val uiState: Flow<TrendingTagsUiState> get() = _uiState
|
||||
private val _uiState = MutableStateFlow(TrendingTagsUiState(listOf(), LoadingState.INITIAL))
|
||||
|
||||
init {
|
||||
invalidate()
|
||||
|
@ -73,9 +73,9 @@ class TrendingViewModel @Inject constructor(
|
|||
*/
|
||||
fun invalidate(refresh: Boolean = false) = viewModelScope.launch {
|
||||
if (refresh) {
|
||||
_uiState.value = TrendingUiState(emptyList(), LoadingState.REFRESHING)
|
||||
_uiState.value = TrendingTagsUiState(emptyList(), LoadingState.REFRESHING)
|
||||
} else {
|
||||
_uiState.value = TrendingUiState(emptyList(), LoadingState.LOADING)
|
||||
_uiState.value = TrendingTagsUiState(emptyList(), LoadingState.LOADING)
|
||||
}
|
||||
|
||||
val deferredFilters = async { mastodonApi.getFilters() }
|
||||
|
@ -85,7 +85,7 @@ class TrendingViewModel @Inject constructor(
|
|||
|
||||
val firstTag = tagResponse.firstOrNull()
|
||||
_uiState.value = if (firstTag == null) {
|
||||
TrendingUiState(emptyList(), LoadingState.LOADED)
|
||||
TrendingTagsUiState(emptyList(), LoadingState.LOADED)
|
||||
} else {
|
||||
val homeFilters = deferredFilters.await().getOrNull()?.filter { filter ->
|
||||
filter.context.contains(Filter.Kind.HOME.kind)
|
||||
|
@ -100,15 +100,15 @@ class TrendingViewModel @Inject constructor(
|
|||
.toViewData()
|
||||
|
||||
val header = TrendingViewData.Header(firstTag.start(), firstTag.end())
|
||||
TrendingUiState(listOf(header) + tags, LoadingState.LOADED)
|
||||
TrendingTagsUiState(listOf(header) + tags, LoadingState.LOADED)
|
||||
}
|
||||
},
|
||||
{ error ->
|
||||
Log.w(TAG, "failed loading trending tags", error)
|
||||
if (error is IOException) {
|
||||
_uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_NETWORK)
|
||||
_uiState.value = TrendingTagsUiState(emptyList(), LoadingState.ERROR_NETWORK)
|
||||
} else {
|
||||
_uiState.value = TrendingUiState(emptyList(), LoadingState.ERROR_OTHER)
|
||||
_uiState.value = TrendingTagsUiState(emptyList(), LoadingState.ERROR_OTHER)
|
||||
}
|
||||
}
|
||||
)
|
|
@ -42,12 +42,12 @@ import java.io.File;
|
|||
TimelineAccountEntity.class,
|
||||
ConversationEntity.class
|
||||
},
|
||||
version = 52,
|
||||
version = 53,
|
||||
autoMigrations = {
|
||||
@AutoMigration(from = 48, to = 49),
|
||||
@AutoMigration(from = 49, to = 50, spec = AppDatabase.MIGRATION_49_50.class),
|
||||
@AutoMigration(from = 50, to = 51),
|
||||
@AutoMigration(from = 51, to = 52)
|
||||
@AutoMigration(from = 51, to = 52),
|
||||
}
|
||||
)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
|
@ -674,4 +674,15 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||
|
||||
@DeleteColumn(tableName = "AccountEntity", columnName = "activeNotifications")
|
||||
static class MIGRATION_49_50 implements AutoMigrationSpec { }
|
||||
|
||||
/**
|
||||
* TabData.TRENDING was renamed to TabData.TRENDING_TAGS, and the text
|
||||
* representation was changed from "Trending" to "TrendingTags".
|
||||
*/
|
||||
public static final Migration MIGRATION_52_53 = new Migration(52, 53) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("UPDATE `AccountEntity` SET `tabpreferences` = REPLACE(tabpreferences, 'Trending:', 'TrendingTags:')");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ class AppModule {
|
|||
AppDatabase.MIGRATION_38_39, AppDatabase.MIGRATION_39_40, AppDatabase.MIGRATION_40_41,
|
||||
AppDatabase.MIGRATION_41_42, AppDatabase.MIGRATION_42_43, AppDatabase.MIGRATION_43_44,
|
||||
AppDatabase.MIGRATION_44_45, AppDatabase.MIGRATION_45_46, AppDatabase.MIGRATION_46_47,
|
||||
AppDatabase.MIGRATION_47_48
|
||||
AppDatabase.MIGRATION_47_48, AppDatabase.MIGRATION_52_53
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import com.keylesspalace.tusky.components.search.fragments.SearchAccountsFragmen
|
|||
import com.keylesspalace.tusky.components.search.fragments.SearchHashtagsFragment
|
||||
import com.keylesspalace.tusky.components.search.fragments.SearchStatusesFragment
|
||||
import com.keylesspalace.tusky.components.timeline.TimelineFragment
|
||||
import com.keylesspalace.tusky.components.trending.TrendingFragment
|
||||
import com.keylesspalace.tusky.components.trending.TrendingTagsFragment
|
||||
import com.keylesspalace.tusky.components.viewthread.ViewThreadFragment
|
||||
import com.keylesspalace.tusky.components.viewthread.edits.ViewEditsFragment
|
||||
import com.keylesspalace.tusky.fragment.ViewVideoFragment
|
||||
|
@ -99,7 +99,7 @@ abstract class FragmentBuildersModule {
|
|||
abstract fun listsForAccountFragment(): ListsForAccountFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun trendingFragment(): TrendingFragment
|
||||
abstract fun trendingTagsFragment(): TrendingTagsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun viewVideoFragment(): ViewVideoFragment
|
||||
|
|
|
@ -38,7 +38,7 @@ import com.keylesspalace.tusky.components.scheduled.ScheduledStatusViewModel
|
|||
import com.keylesspalace.tusky.components.search.SearchViewModel
|
||||
import com.keylesspalace.tusky.components.timeline.viewmodel.CachedTimelineViewModel
|
||||
import com.keylesspalace.tusky.components.timeline.viewmodel.NetworkTimelineViewModel
|
||||
import com.keylesspalace.tusky.components.trending.viewmodel.TrendingViewModel
|
||||
import com.keylesspalace.tusky.components.trending.viewmodel.TrendingTagsViewModel
|
||||
import com.keylesspalace.tusky.components.viewthread.ViewThreadViewModel
|
||||
import com.keylesspalace.tusky.components.viewthread.edits.ViewEditsViewModel
|
||||
import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel
|
||||
|
@ -172,8 +172,8 @@ abstract class ViewModelModule {
|
|||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(TrendingViewModel::class)
|
||||
internal abstract fun trendingViewModel(viewModel: TrendingViewModel): ViewModel
|
||||
@ViewModelKey(TrendingTagsViewModel::class)
|
||||
internal abstract fun trendingTagsViewModel(viewModel: TrendingTagsViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
|
|
|
@ -379,9 +379,7 @@ class SendStatusService : Service(), Injectable {
|
|||
accountId: Long,
|
||||
statusId: Int
|
||||
): Notification {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.putExtra(NotificationHelper.ACCOUNT_ID, accountId)
|
||||
intent.putExtra(MainActivity.OPEN_DRAFTS, true)
|
||||
val intent = MainActivity.draftIntent(this, accountId)
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
|
|
|
@ -19,6 +19,7 @@ import android.annotation.TargetApi
|
|||
import android.content.Intent
|
||||
import android.service.quicksettings.TileService
|
||||
import com.keylesspalace.tusky.MainActivity
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity
|
||||
|
||||
/**
|
||||
* Small Addition that adds in a QuickSettings tile
|
||||
|
@ -29,11 +30,8 @@ import com.keylesspalace.tusky.MainActivity
|
|||
class TuskyTileService : TileService() {
|
||||
|
||||
override fun onClick() {
|
||||
val intent = Intent(this, MainActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
action = Intent.ACTION_SEND
|
||||
type = "text/plain"
|
||||
}
|
||||
val intent = MainActivity.composeIntent(this, ComposeActivity.ComposeOptions())
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivityAndCollapse(intent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ enum class AppTheme(val value: String) {
|
|||
*
|
||||
* - Adding a new preference that does not change the interpretation of an existing preference
|
||||
*/
|
||||
const val SCHEMA_VERSION = 2023022701
|
||||
const val SCHEMA_VERSION = 2023072401
|
||||
|
||||
object PrefKeys {
|
||||
// Note: not all of these keys are actually used as SharedPreferences keys but we must give
|
||||
|
@ -61,7 +61,6 @@ object PrefKeys {
|
|||
const val ANIMATE_GIF_AVATARS = "animateGifAvatars"
|
||||
const val USE_BLURHASH = "useBlurhash"
|
||||
const val SHOW_SELF_USERNAME = "showSelfUsername"
|
||||
const val SHOW_NOTIFICATIONS_FILTER = "showNotificationsFilter"
|
||||
const val SHOW_CARDS_IN_TIMELINES = "showCardsInTimelines"
|
||||
const val CONFIRM_REBLOGS = "confirmReblogs"
|
||||
const val CONFIRM_FAVOURITES = "confirmFavourites"
|
||||
|
@ -104,4 +103,9 @@ object PrefKeys {
|
|||
|
||||
/** UI text scaling factor, stored as float, 100 = 100% = no scaling */
|
||||
const val UI_TEXT_SCALE_RATIO = "uiTextScaleRatio"
|
||||
|
||||
/** Keys that are no longer used (e.g., the preference has been removed */
|
||||
object Deprecated {
|
||||
const val SHOW_NOTIFICATIONS_FILTER = "showNotificationsFilter"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import androidx.core.graphics.drawable.IconCompat
|
|||
import com.bumptech.glide.Glide
|
||||
import com.keylesspalace.tusky.MainActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.components.notifications.NotificationHelper
|
||||
import com.keylesspalace.tusky.db.AccountEntity
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
|
@ -72,7 +71,7 @@ fun updateShortcut(context: Context, account: AccountEntity) {
|
|||
val intent = Intent(context, MainActivity::class.java).apply {
|
||||
action = Intent.ACTION_SEND
|
||||
type = "text/plain"
|
||||
putExtra(NotificationHelper.ACCOUNT_ID, account.id)
|
||||
putExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID, account.id.toString())
|
||||
}
|
||||
|
||||
val shortcutInfo = ShortcutInfoCompat.Builder(context, account.id.toString())
|
||||
|
|
|
@ -29,22 +29,14 @@
|
|||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:navigationContentDescription="@string/action_open_drawer" />
|
||||
|
||||
<LinearLayout
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/topNav"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/topNavAvatar"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:contentDescription="@string/action_open_drawer"
|
||||
app:shapeAppearance="@style/ShapeAppearance.Avatar"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
app:contentInsetStart="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:navigationContentDescription="@string/action_open_drawer">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
|
@ -54,8 +46,7 @@
|
|||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
@ -73,33 +64,16 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
app:contentInsetStart="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:fabAlignmentMode="end">
|
||||
|
||||
<LinearLayout
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/bottomTabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/bottomNavAvatar"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:contentDescription="@string/action_open_drawer"
|
||||
app:shapeAppearance="@style/ShapeAppearance.Avatar"
|
||||
tools:src="@drawable/avatar_default" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/bottomTabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:tabGravity="fill"
|
||||
app:tabIndicator="@null"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:tabGravity="fill"
|
||||
app:tabIndicatorGravity="top"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
||||
|
||||
|
@ -132,4 +106,3 @@
|
|||
android:fitsSystemWindows="true" />
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
|
||||
|
|
|
@ -18,53 +18,10 @@
|
|||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/colorBackground">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarOptions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorSurface"
|
||||
app:elevation="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/topButtonsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonClear"
|
||||
style="@style/TuskyButton.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/notifications_clear"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonFilter"
|
||||
style="@style/TuskyButton.TextButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/notifications_apply_filter"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?android:attr/listDivider"
|
||||
app:layout_scrollFlags="scroll|enterAlways" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -27,4 +27,14 @@
|
|||
android:id="@+id/load_newest"
|
||||
android:title="@string/load_newest_notifications"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_edit_notification_filter"
|
||||
android:title="@string/notifications_apply_filter"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_clear_notifications"
|
||||
android:title="@string/notifications_clear"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
|
|
@ -450,7 +450,6 @@
|
|||
<string name="report_remote_instance">أعد تحويله إلى %s</string>
|
||||
<string name="failed_report">فشل الابلاغ</string>
|
||||
<string name="failed_fetch_posts">فشلت عملية جلب المنشورات</string>
|
||||
<string name="pref_title_show_notifications_filter">اعرض مصفاة الإشعارات</string>
|
||||
<string name="filter_dialog_whole_word">الكلمة كاملة</string>
|
||||
<string name="description_poll">استطلاع رأي بالخيارات: %1$s, %2$s, %3$s, %4$s; %5$s</string>
|
||||
<string name="mute_domain_warning">هل أنت متأكد من أنك تريد حجب كافة %s؟ سوف لن يكون باستطاعتك رؤية أي محتوى قادم من هذا النطاق بعد الآن ، لا في الخيوط الزمنية العامة ولا في إخطاراتك. سيتم إزالة متابِعيك الذين هم على هذا النطاق.</string>
|
||||
|
|
|
@ -483,7 +483,6 @@
|
|||
<string name="hint_additional_info">Дадатковыя каментары</string>
|
||||
<string name="title_accounts">Уліковыя запісы</string>
|
||||
<string name="failed_search">Пошук не атрымаўся</string>
|
||||
<string name="pref_title_show_notifications_filter">Паказаць фільтр апавяшчэнняў</string>
|
||||
<string name="create_poll_title">Апытанне</string>
|
||||
<string name="duration_1_day">1 дзень</string>
|
||||
<string name="duration_3_days">3 дні</string>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
<string name="label_duration">Продължителност</string>
|
||||
<string name="create_poll_title">Анкета</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Активиране на плъзгащия жест за превключване между раздели</string>
|
||||
<string name="pref_title_show_notifications_filter">Показване на филтър за известия</string>
|
||||
<string name="failed_search">Търсенето бе неуспешно</string>
|
||||
<string name="title_accounts">Акаунти</string>
|
||||
<string name="report_description_remote_instance">Акаунтът е от друг сървър. Да изпратите ли и там анонимно копие на доклада\?</string>
|
||||
|
|
|
@ -362,7 +362,6 @@
|
|||
<string name="title_accounts">অ্যাকাউন্টগুলো</string>
|
||||
<string name="filter_dialog_whole_word_description">যখন শব্দ বা বাক্যাংশটি শুধুমাত্র আলফানিউমেরিক হয় তখন এটি শুধুমাত্র তখনই প্রয়োগ করা হবে যদি এটি সম্পূর্ণ শব্দটির সাথে মেলে</string>
|
||||
<string name="filter_dialog_whole_word">সম্পূর্ণ শব্দ</string>
|
||||
<string name="pref_title_show_notifications_filter">বিজ্ঞপ্তি ফিল্টার দেখান</string>
|
||||
<string name="mute_domain_warning_dialog_ok">পুরো ডোমেইন লুকান</string>
|
||||
<string name="mute_domain_warning">আপনি কি সব %s ব্লক করতে চান\? আপনি যে ডোমেন থেকে কোনও পাবলিক টাইমলাইনে বা আপনার বিজ্ঞপ্তিগুলিতে সামগ্রী দেখতে পাবেন না। আপনার অনুসরণকারীদের সরানো হবে।</string>
|
||||
<string name="action_mute_domain">নিঃশব্দ %s</string>
|
||||
|
|
|
@ -389,7 +389,6 @@
|
|||
<string name="mute_domain_warning_dialog_ok">পুরো ডোমেইন লুকান</string>
|
||||
<string name="filter_dialog_whole_word">সম্পূর্ণ শব্দ</string>
|
||||
<string name="filter_dialog_whole_word_description">যখন শব্দ বা বাক্যাংশটি শুধুমাত্র আলফানিউমেরিক হয় তখন এটি শুধুমাত্র তখনই প্রয়োগ করা হবে যদি এটি সম্পূর্ণ শব্দটির সাথে মেলে</string>
|
||||
<string name="pref_title_show_notifications_filter">বিজ্ঞপ্তি ফিল্টার দেখান</string>
|
||||
<string name="pref_title_alway_open_spoiler">সর্বদা সামগ্রী সতর্কতা সহ চিহ্নিত টুটগুলি প্রসারিত করুন</string>
|
||||
<string name="title_accounts">অক্কোউন্টগুলি</string>
|
||||
<string name="failed_search">অনুসন্ধান করতে ব্যর্থ</string>
|
||||
|
|
|
@ -397,7 +397,6 @@
|
|||
<string name="failed_fetch_posts">Error obtenint els estats</string>
|
||||
<string name="report_description_1">L\'informe s\'enviarà al moderador del teu servidor. Pots afegir una explicació del motiu d\'aquest informe del compte a sota:</string>
|
||||
<string name="title_accounts">Comptes</string>
|
||||
<string name="pref_title_show_notifications_filter">Mostrar el filtre de les notificacions</string>
|
||||
<string name="mute_domain_warning">Estàs segur que vols bloquejar tot de %s\? No veuràs cap contingut de domini ni en els fils públics ni a les teves notificacions. Els teus seguidors d\'aquest domini seran eliminats.</string>
|
||||
<string name="filter_dialog_whole_word_description">Quan la paraula o la frase siguin només alfanumèrica , només s\'aplicarà si coincideix amb tota la paraula</string>
|
||||
<string name="report_sent_success">\@%s reportat satisfactoriament</string>
|
||||
|
|
|
@ -261,7 +261,6 @@
|
|||
<string name="create_poll_title">ڕاپرسی</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">چالاککردنی ئاماژەکردنی لێدانی چالاک بۆ گۆڕین لە نێوان خشتەبەندەکان</string>
|
||||
<string name="license_description">تاسکی کۆد و سەرمایەکانی تێدایە لەم پڕۆژە کراوەی سەرچاوە:</string>
|
||||
<string name="pref_title_show_notifications_filter">فلتەری ئاگانامەکان نیشان بدە</string>
|
||||
<string name="failed_search">گەڕانەکە سەرکەوتوو نەبوو</string>
|
||||
<string name="title_accounts">ئەژمێرەکان</string>
|
||||
<string name="report_description_remote_instance">هەژمارەلە ڕاژەیەکی دیکەیە ترە. کۆپیەکی بێ سەروبەر بنێرە بۆ ڕاپۆرتەکە لەوێ؟</string>
|
||||
|
|
|
@ -423,7 +423,6 @@
|
|||
<string name="failed_fetch_posts">Stahování příspěvků selhalo</string>
|
||||
<string name="report_description_1">Nahlášení bude zasláno moderátorovi vašeho serveru. Níže můžete uvést, proč tento účet nahlašujete:</string>
|
||||
<string name="report_description_remote_instance">Tento účet je z jiného serveru. Chcete na něj také poslat anonymizovanou kopii nahlášení\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Zobrazit filtr oznámení</string>
|
||||
<string name="create_poll_title">Anketa</string>
|
||||
<string name="duration_5_min">5 minut</string>
|
||||
<string name="duration_30_min">30 minut</string>
|
||||
|
|
|
@ -307,7 +307,6 @@
|
|||
<string name="notification_clear_text">Ydych chi\'n siŵr eich bod chi am glirio\'ch holl hysbysiadau\'n barhaol\?</string>
|
||||
<string name="error_multimedia_size_limit">Ni all ffeiliau fideo a sain fod yn fwy na %s MB.</string>
|
||||
<string name="url_domain_notifier">%s (🔗 %s)</string>
|
||||
<string name="pref_title_show_notifications_filter">Dangos yr hidlydd hysbysiadau</string>
|
||||
<string name="error_following_hashtag_format">Gwall wrth ddilyn #%s</string>
|
||||
<string name="error_unfollowing_hashtag_format">Gwall wrth ddad-ddilyn #%s</string>
|
||||
<string name="action_unmute_domain">Dad-dewi %s</string>
|
||||
|
@ -732,4 +731,4 @@
|
|||
<string name="about_copied">Copïwyd fersiwn a gwybodaeth dyfais</string>
|
||||
<string name="list_exclusive_label">Cuddio o\'r ffrwd cartref</string>
|
||||
<string name="error_media_playback">Methodd chwarae: %s</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -395,7 +395,6 @@
|
|||
<string name="failed_report">Melden fehlgeschlagen</string>
|
||||
<string name="report_description_1">Die Meldung wird an die Moderator*innen deines Servers geschickt. Du kannst hier eine Erklärung angeben, warum du dieses Konto meldest:</string>
|
||||
<string name="report_description_remote_instance">Dieses Konto ist von einem anderen Server. Soll eine anonymisierte Kopie der Meldung auch dorthin geschickt werden\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Benachrichtigungsfilter anzeigen</string>
|
||||
<string name="create_poll_title">Umfrage</string>
|
||||
<string name="duration_5_min">5 Minuten</string>
|
||||
<string name="duration_30_min">30 Minuten</string>
|
||||
|
@ -683,4 +682,4 @@
|
|||
<string name="about_copy">Version und Geräteinformationen kopieren</string>
|
||||
<string name="list_exclusive_label">Nicht in der Startseite anzeigen</string>
|
||||
<string name="error_media_upload_sending_fmt">Das Hochladen ist fehlgeschlagen: %s</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -403,7 +403,6 @@
|
|||
<string name="failed_fetch_posts">Venigo de statusoj malsukcesis</string>
|
||||
<string name="report_description_1">La signalo estos sendita al la kontrolantoj de via servilo. Vi povas doni klarigon pri kial vi signalas ĉi tiun konton sube:</string>
|
||||
<string name="report_description_remote_instance">La konto estas en alia servilo. Ĉu sendi sennomigitan kopion de la signalo ankaŭ tien\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Montri filtrilon de sciigoj</string>
|
||||
<string name="filter_dialog_whole_word">Tuta vorto</string>
|
||||
<string name="filter_dialog_whole_word_description">Ŝlosilvorto aŭ frazo litercifera aplikiĝos, nur se ĝi kongruas kun la tuta vorto</string>
|
||||
<string name="title_accounts">Kontoj</string>
|
||||
|
|
|
@ -431,7 +431,6 @@
|
|||
<string name="failed_fetch_posts">Fallo al obtener estados</string>
|
||||
<string name="report_description_1">El reporte será enviado a un moderador de tu servidor. Puedes añadir una explicación de por qué estás reportando esta cuenta a continuación:</string>
|
||||
<string name="report_description_remote_instance">La cuenta es de otro servidor. ¿Enviar una copia anónima del reporte\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Mostrar filtro de notificaciones</string>
|
||||
<string name="pref_title_alway_open_spoiler">Mostrar siempre publicaciones marcadas con avisos de contenido</string>
|
||||
<string name="title_accounts">Cuentas</string>
|
||||
<string name="failed_search">Error al buscar</string>
|
||||
|
|
|
@ -417,7 +417,6 @@
|
|||
<string name="report_description_remote_instance">Kontua beste zerbitzari batekoa da. Bidali txostenaren kopia anonimatua hara ere\?</string>
|
||||
<string name="title_accounts">Kontuak</string>
|
||||
<string name="failed_search">Bilaketa huts egin du</string>
|
||||
<string name="pref_title_show_notifications_filter">Erakutsi jakinarazpenen iragazkia</string>
|
||||
<string name="create_poll_title">Inkesta</string>
|
||||
<string name="duration_5_min">5 minutu</string>
|
||||
<string name="duration_30_min">30 minutu</string>
|
||||
|
|
|
@ -398,7 +398,6 @@
|
|||
<string name="failed_fetch_posts">شکست در واکشی فرستهها</string>
|
||||
<string name="title_accounts">حسابها</string>
|
||||
<string name="failed_search">شکست در جستوجو</string>
|
||||
<string name="pref_title_show_notifications_filter">نمایش پالایهٔ آگاهیها</string>
|
||||
<string name="create_poll_title">نظرسنجی</string>
|
||||
<string name="duration_5_min">۵ دقیقه</string>
|
||||
<string name="duration_30_min">۳۰ دقیقه</string>
|
||||
|
@ -684,4 +683,4 @@
|
|||
<string name="about_copied">اطّلاعات افزاره و نگارش رونوشت شد</string>
|
||||
<string name="list_exclusive_label">نهفتن از خط زمانی خانگی</string>
|
||||
<string name="error_media_playback">پخش شکست خورد: %s</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -422,7 +422,6 @@
|
|||
<string name="mute_domain_warning">Êtes-vous sûr⋅e de vouloir bloquer %s en entier \? Vous ne verrez plus de contenu provenant de ce domaine, ni dans les fils publics, ni dans vos notifications. Vos abonné·e·s utilisant ce domaine seront retiré·e·s.</string>
|
||||
<string name="button_done">Terminé</string>
|
||||
<string name="report_description_remote_instance">Le compte provient d’un autre serveur. Envoyez également une copie anonyme du rapport \?</string>
|
||||
<string name="pref_title_show_notifications_filter">Montrer le filtre des notifications</string>
|
||||
<string name="filter_dialog_whole_word">Mot entier</string>
|
||||
<string name="filter_dialog_whole_word_description">Un mot-clé ou une phrase alphanumérique sera appliqué·e seulement s’il ou elle correspond au mot entier</string>
|
||||
<string name="title_accounts">Comptes</string>
|
||||
|
|
|
@ -439,7 +439,6 @@
|
|||
<string name="report_description_remote_instance">Is ó fhreastalaí eile an cuntas. Seol cóip gan ainm den tuarascáil ansin freisin\?</string>
|
||||
<string name="title_accounts">Cuntais</string>
|
||||
<string name="failed_search">Theip ar chuardach</string>
|
||||
<string name="pref_title_show_notifications_filter">Taispeáin scagaire Fógraí</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Cumasaigh gotha swipe aistriú idir cluaisíní</string>
|
||||
<string name="create_poll_title">Vótaíocht</string>
|
||||
<string name="duration_5_min">5 nóiméid</string>
|
||||
|
|
|
@ -109,7 +109,6 @@
|
|||
<string name="add_poll_choice">Cuir roghainn ris</string>
|
||||
<string name="create_poll_title">Cunntas-bheachd</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Cuir an comas gluasad grad-shlaighdidh airson leum a ghearradh o thaba gu taba</string>
|
||||
<string name="pref_title_show_notifications_filter">Seall criathrag nam brathan</string>
|
||||
<string name="failed_search">Dh’fhàillig leis an lorg</string>
|
||||
<string name="title_accounts">Cunntasan</string>
|
||||
<string name="report_description_remote_instance">Chaidh an cunntas a chlàradh air frithealaiche eile. A bheil thu airson lethbhreac dhen ghearan a chur dha-san gun ainm cuideachd\?</string>
|
||||
|
@ -700,4 +699,4 @@
|
|||
<string name="about_copied">Chaidh lethbhreac de thionndadh is fiosrachadh an uidheim a dhèanamh</string>
|
||||
<string name="list_exclusive_label">Falaich o loidhne-ama na dachaigh</string>
|
||||
<string name="error_media_upload_sending_fmt">Dh’fhàillig leis an luchdadh suas: %s</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -128,7 +128,6 @@
|
|||
<string name="label_duration">Duración</string>
|
||||
<string name="create_poll_title">Enquisa</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Activar xestos de desprazamento para moverse entre lapelas</string>
|
||||
<string name="pref_title_show_notifications_filter">Mostrar filtro das notificacións</string>
|
||||
<string name="failed_search">Fallou a busca</string>
|
||||
<string name="title_accounts">Contas</string>
|
||||
<string name="report_description_remote_instance">A conta pertence a outro servidor. Queres enviar unha copia anónima da denuncia alí tamén\?</string>
|
||||
|
|
|
@ -256,7 +256,6 @@
|
|||
<string name="no_drafts">आपके पास कोई ड्राफ्ट नहीं है।</string>
|
||||
<string name="post_lookup_error_format">%s पोस्ट खोजने में त्रुटि</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">टैब के बीच स्विच करने के लिए स्वाइप जेस्चर को सक्षम करें</string>
|
||||
<string name="pref_title_show_notifications_filter">सूचना फ़िल्टर दिखाएं</string>
|
||||
<string name="failed_search">खोज करने में विफल</string>
|
||||
<string name="report_description_remote_instance">खाता किसी अन्य सर्वर से है। रिपोर्ट की अनाम प्रति वहां भी भेजें\?</string>
|
||||
<string name="report_description_1">रिपोर्ट आपके सर्वर मॉडरेटर को भेजी जाएगी। आप इस बारे में स्पष्टीकरण दे सकते हैं कि आप इस खाते को रिपोर्ट क्यों कर रहे हैं:</string>
|
||||
|
|
|
@ -412,7 +412,6 @@
|
|||
<string name="failed_fetch_posts">Sikertelen a bejegyzések letöltése</string>
|
||||
<string name="report_description_1">A bejelentést a szervered moderátorának küldjük el. Alább megadhatsz egy magyarázatot arra, hogy miért jelented be ezt a fiókot:</string>
|
||||
<string name="report_description_remote_instance">A fiók egy másik szerverről származik. Küldjünk oda is egy anonimizált másolatot a bejelentésről\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Értesítések szűrőjének mutatása</string>
|
||||
<string name="pref_title_alway_open_spoiler">Tartalomfigyelmeztetéssel ellátott bejegyzések kinyitása mindig</string>
|
||||
<string name="title_accounts">Fiókok</string>
|
||||
<string name="failed_search">Sikertelen keresés</string>
|
||||
|
|
|
@ -396,7 +396,6 @@
|
|||
<string name="report_description_remote_instance">Notandaaðgangurinn er af öðrum vefþjóni. Á einnig að senda nafnlaust afrit af kærunni þangað\?</string>
|
||||
<string name="title_accounts">Notandaaðgangar</string>
|
||||
<string name="failed_search">Tókst ekki að leita</string>
|
||||
<string name="pref_title_show_notifications_filter">Birta tilkynningasíu</string>
|
||||
<string name="create_poll_title">Athuga</string>
|
||||
<string name="duration_5_min">5 mínútur</string>
|
||||
<string name="duration_30_min">30 mínútur</string>
|
||||
|
|
|
@ -439,7 +439,6 @@
|
|||
<string name="report_description_remote_instance">L\'utente è su un altro server. Mandare una copia della segnalazione anche lì\?</string>
|
||||
<string name="title_accounts">Utenti</string>
|
||||
<string name="failed_search">Errore durante la ricerca</string>
|
||||
<string name="pref_title_show_notifications_filter">Mostra il filtro delle notifiche</string>
|
||||
<string name="create_poll_title">Sondaggio</string>
|
||||
<string name="duration_5_min">5 minuti</string>
|
||||
<string name="duration_30_min">30 minuti</string>
|
||||
|
|
|
@ -389,7 +389,6 @@
|
|||
<string name="poll_info_closed">投票終了</string>
|
||||
<string name="title_accounts">アカウント</string>
|
||||
<string name="failed_search">検索に失敗しました</string>
|
||||
<string name="pref_title_show_notifications_filter">通知フィルターを表示</string>
|
||||
<string name="action_reset_schedule">リセット</string>
|
||||
<string name="title_bookmarks">ブックマーク</string>
|
||||
<string name="action_bookmark">ブックマーク</string>
|
||||
|
|
|
@ -402,7 +402,6 @@
|
|||
<string name="failed_fetch_posts">게시물을 불러오지 못했습니다</string>
|
||||
<string name="report_description_1">인스턴스 관리자에게 신고합니다. 이 계정을 신고하려는 이유를 작성하실 수 있습니다:</string>
|
||||
<string name="report_description_remote_instance">이 유저는 다른 인스턴스에 속해 있습니다. 그 인스턴스에도 익명으로 신고 내용을 보내시겠습니까\?</string>
|
||||
<string name="pref_title_show_notifications_filter">알림 필터 보이기</string>
|
||||
<string name="action_open_as">%s(으)로 열기</string>
|
||||
<string name="title_accounts">계정</string>
|
||||
<string name="failed_search">검색하지 못했습니다</string>
|
||||
|
|
|
@ -425,7 +425,6 @@
|
|||
<string name="pref_title_show_self_username">Rādīt lietotājvārdu rīkjoslās</string>
|
||||
<string name="pref_title_confirm_reblogs">Pirms pastiprināšanas rādīt apstiprināšanas dialogu</string>
|
||||
<string name="failed_search">Meklēšana neizdevās</string>
|
||||
<string name="pref_title_show_notifications_filter">Rādīt paziņojumu filtru</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Iespējot vilkšanas žestu, lai pārslēgtos starp cilnēm</string>
|
||||
<string name="pref_title_hide_top_toolbar">Paslēpt augšējās rīkjoslas virsrakstu</string>
|
||||
<string name="pref_title_notification_filter_reports">iesniegts jauns ziņojums</string>
|
||||
|
|
|
@ -404,7 +404,6 @@
|
|||
<string name="confirmation_domain_unmuted">%s er ikke lenger skjult</string>
|
||||
<string name="mute_domain_warning">Er du sikker på at du vil blokkere alt fra %s\? Du kommer ikke til å se innhold fra domenet i noen offentlige tidslinjer, eller i varslene dine. Kontoer som følger deg fra dette domenet vil bli fjernet.</string>
|
||||
<string name="mute_domain_warning_dialog_ok">Skjul hele domenet</string>
|
||||
<string name="pref_title_show_notifications_filter">Vis varselfilter</string>
|
||||
<string name="filter_dialog_whole_word">Helt ord</string>
|
||||
<string name="filter_dialog_whole_word_description">Når nøkkelordet eller frasen kun inneholder bokstaver og tall, vil det bare brukes dersom det stemmer overens med hele ordet</string>
|
||||
<string name="title_accounts">Kontoer</string>
|
||||
|
|
|
@ -411,7 +411,6 @@
|
|||
<string name="failed_fetch_posts">Het ophalen van berichten is mislukt</string>
|
||||
<string name="report_description_1">Deze rapportage wordt naar jouw servermoderator(en) gestuurd. Je kunt hieronder een uitleg geven over waarom je het account wilt rapporteren:</string>
|
||||
<string name="report_description_remote_instance">Het account is van een andere server. Wil je ook een geanonimiseerde kopie van de rapportage daarnaartoe sturen\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Meldingenfilter tonen</string>
|
||||
<string name="filter_dialog_whole_word">Heel woord</string>
|
||||
<string name="filter_dialog_whole_word_description">Wanneer het trefwoord of zinsdeel alfanumeriek is, wordt het alleen gefilterd wanneer het hele woord overeenkomt</string>
|
||||
<string name="duration_5_min">5 minuten</string>
|
||||
|
@ -664,4 +663,4 @@
|
|||
<string name="about_copied">Versie en apparaatinformatie gekopieerd</string>
|
||||
<string name="error_media_upload_sending_fmt">De upload is mislukt: %s</string>
|
||||
<string name="socket_timeout_exception">Contact zoeken met je server duurde te lang</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -411,7 +411,6 @@
|
|||
<string name="confirmation_domain_unmuted">%s es pas mai rescondut</string>
|
||||
<string name="mute_domain_warning">Volètz vertadièrament blocar complètament %s \? Veiretz pas mai de contengut venent d’aqueste domeni, nimai los flux d’actualitat o las notificacion. Vòstres seguidors d’aqueste domeni seràn tirats.</string>
|
||||
<string name="mute_domain_warning_dialog_ok">Rescondre tot lo domeni</string>
|
||||
<string name="pref_title_show_notifications_filter">Mostrar lo filtre de las notificacions</string>
|
||||
<string name="filter_dialog_whole_word">Mot complèt</string>
|
||||
<string name="filter_dialog_whole_word_description">Quand un mot clau o una frasa es solament alfanumeric, serà pas qu’aplicat se correspond al mot complèt</string>
|
||||
<string name="pref_title_alway_open_spoiler">Totjorn desplegar las publicacions marcadas amb un avertiment de contengut</string>
|
||||
|
|
|
@ -440,7 +440,6 @@
|
|||
<string name="report_description_remote_instance">To konto jest na innym serwerze. Czy przesłać anonimizowaną kopię zgłoszenia na ten serwer\?</string>
|
||||
<string name="title_accounts">Konta</string>
|
||||
<string name="failed_search">Wyszukiwanie nie powidło się</string>
|
||||
<string name="pref_title_show_notifications_filter">Pokaż filtr powiadomień</string>
|
||||
<string name="create_poll_title">Głosowanie</string>
|
||||
<string name="duration_5_min">5 minut</string>
|
||||
<string name="duration_30_min">30 minut</string>
|
||||
|
|
|
@ -422,7 +422,6 @@
|
|||
<string name="confirmation_domain_unmuted">%s desbloqueada</string>
|
||||
<string name="mute_domain_warning">Tem certeza de que deseja bloquear tudo de %s\? Você não verá mais o conteúdo desta instância em nenhuma linha do tempo pública ou nas suas notificações. Seus seguidores desta instância serão removidos.</string>
|
||||
<string name="mute_domain_warning_dialog_ok">Bloquear instância</string>
|
||||
<string name="pref_title_show_notifications_filter">Mostrar filtro de notificações</string>
|
||||
<string name="filter_dialog_whole_word">Toda palavra</string>
|
||||
<string name="filter_dialog_whole_word_description">Se for apenas alfanumérico, só se aplicará se combinar com a palavra inteira</string>
|
||||
<string name="action_add_poll">Adicionar enquete</string>
|
||||
|
|
|
@ -467,7 +467,6 @@
|
|||
<string name="report_description_remote_instance">A conta está noutra instância. Quer enviar uma cópia anónima da denúncia para lá\?</string>
|
||||
<string name="title_accounts">Contas</string>
|
||||
<string name="failed_search">Erro ao pesquisar</string>
|
||||
<string name="pref_title_show_notifications_filter">Mostrar Filtro das Notificações</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Ativar gesto de deslizar para alternar entre separadores</string>
|
||||
<string name="create_poll_title">Votação</string>
|
||||
<string name="label_duration">Duração</string>
|
||||
|
|
|
@ -433,7 +433,6 @@
|
|||
<string name="failed_fetch_posts">Не удалось получить статусы</string>
|
||||
<string name="report_description_1">Жалоба будет отправлена модератору вашего узла. Ниже вы можете добавить пояснение о причинах жалобы:</string>
|
||||
<string name="report_description_remote_instance">Этот аккаунт расположен на другом узле. Отправить анонимную копию жалобы туда\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Показать фильтр уведомлений</string>
|
||||
<string name="action_add_poll">Создать опрос</string>
|
||||
<string name="pref_title_alway_open_spoiler">Всегда разворачивать посты с предупреждением о содержимом</string>
|
||||
<string name="title_accounts">Аккаунты</string>
|
||||
|
|
|
@ -378,7 +378,6 @@
|
|||
<string name="duration_5_min">५ निमेषाः</string>
|
||||
<string name="create_poll_title">मतपेटिका</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">सारणहावभावस्य संयुतनं पीठिकापरिवर्तनार्थं कार्यम्</string>
|
||||
<string name="pref_title_show_notifications_filter">सूचनाशोधकं दृश्यताम्</string>
|
||||
<string name="failed_search">अन्वेषणे विफलता जाता</string>
|
||||
<string name="title_accounts">व्यक्तित्वविवरणलेखाः</string>
|
||||
<string name="report_description_remote_instance">अन्यजालवितारकादियं व्यक्तित्वविवरणलेखा । आवेदनस्य रक्षितप्रतिलिपिरपि प्रेष्यतां वा \?</string>
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
<string name="hint_display_name">දර්ශන නාමය</string>
|
||||
<string name="post_text_size_medium">මධ්යම</string>
|
||||
<string name="post_media_audio">ශ්රව්ය</string>
|
||||
<string name="pref_title_show_notifications_filter">දැනුම්දීම් පෙරහන පෙන්වන්න</string>
|
||||
<string name="abbreviated_days_ago">දව. %d</string>
|
||||
<string name="action_logout">නික්මෙන්න</string>
|
||||
<string name="filter_edit_title">පෙරහන සංස්කරණය</string>
|
||||
|
@ -268,4 +267,4 @@
|
|||
<string name="pref_title_browser_settings">අතිරික්සුව</string>
|
||||
<string name="abbreviated_seconds_ago">තත්. %d</string>
|
||||
<string name="url_domain_notifier">%s (🔗 %s)</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -407,7 +407,6 @@
|
|||
<string name="confirmation_domain_unmuted">Domena %s odrita</string>
|
||||
<string name="mute_domain_warning">Ali ste prepričani, da želite blokirati vse iz domene %s\? Vsebine iz te domene ne boste videli v nobeni javni časovnici ali v obvestilih. Vaši sledilci iz te domene bodo odstranjeni.</string>
|
||||
<string name="mute_domain_warning_dialog_ok">Skrij celotno domeno</string>
|
||||
<string name="pref_title_show_notifications_filter">Pokaži filtre obvestil</string>
|
||||
<string name="filter_dialog_whole_word">Cela beseda</string>
|
||||
<string name="filter_dialog_whole_word_description">Če je ključna beseda ali fraza samo alfanumerična, bo uporabljena le, če se ujema s celotno besedo</string>
|
||||
<string name="title_accounts">Računi</string>
|
||||
|
|
|
@ -418,7 +418,6 @@
|
|||
<string name="failed_fetch_posts">Misslyckades att hämta status</string>
|
||||
<string name="report_description_1">Anmälan kommer att skickas till din servermoderator. Du kan beskriva varför du anmäler kontot nedan:</string>
|
||||
<string name="report_description_remote_instance">Kontot är från en annan server. Skicka en anonym kopia av anmälan dit också\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Visa notifikationsfilter</string>
|
||||
<string name="filter_dialog_whole_word">Helt ord</string>
|
||||
<string name="filter_dialog_whole_word_description">När nyckelordet eller frasen enbart är alfanumerisk, appliceras den om den matchar hela ordet</string>
|
||||
<string name="pref_title_alway_open_spoiler">Expandera alltid inlägg med innehållsvarningar</string>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<string name="duration_5_min">5 นาที</string>
|
||||
<string name="create_poll_title">โพล</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">เปิดใช้งานการเลื่อนนิ้วเพื่อสลับระหว่างแท็บ</string>
|
||||
<string name="pref_title_show_notifications_filter">แสดงตัวกรองการแจ้งเตือน</string>
|
||||
<string name="failed_search">ค้นหาล้มเหลว</string>
|
||||
<string name="title_accounts">บัญชี</string>
|
||||
<string name="report_description_remote_instance">บัญชีนี้มาจากเซิร์ฟเวอร์อื่น ส่งสำเนารายงานที่ไม่ระบุชื่อไปที่นั่นด้วยหรือไม่\?</string>
|
||||
|
|
|
@ -395,7 +395,6 @@
|
|||
<string name="failed_fetch_posts">Yayınlar getirilemedi</string>
|
||||
<string name="report_description_1">Bildirim sunucu yöneticinize gönderilecektir. Bu hesabı neden bildirdiğinizle ilgili açıklama yapabilirsiniz:</string>
|
||||
<string name="report_description_remote_instance">Hesap başka bir sunucudan. Raporun anonim bir kopyasını da oraya gönderilsin mi\?</string>
|
||||
<string name="pref_title_show_notifications_filter">Bildirim süzgecini göster</string>
|
||||
<string name="action_mentions">Bahsedenler</string>
|
||||
<string name="action_open_reblogger">Gönderi yazanını aç</string>
|
||||
<string name="action_open_reblogged_by">Yeniden paylaşımları göster</string>
|
||||
|
@ -684,4 +683,4 @@
|
|||
<string name="about_copied">Kopyalanan sürüm ve cihaz bilgileri</string>
|
||||
<string name="list_exclusive_label">Anasayfa ağ akışından gizle</string>
|
||||
<string name="error_media_playback">Oynatma başarısız oldu: %s</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -473,7 +473,6 @@
|
|||
<string name="no_drafts">У вас немає чернеток.</string>
|
||||
<string name="post_lookup_error_format">Помилка пошуку допису %s</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Увімкнути перемикання між вкладками жестом проведення пальцем</string>
|
||||
<string name="pref_title_show_notifications_filter">Показати фільтр сповіщень</string>
|
||||
<string name="failed_search">Не вдалося здійснити пошук</string>
|
||||
<string name="report_description_remote_instance">Обліковий запис з іншого сервера. Надіслати анонімізовану копію звіту й туди\?</string>
|
||||
<string name="report_description_1">Скаргу буде надіслано вашому модератору сервера. Ви можете надати пояснення, чому ви повідомляєте про цей обліковий запис знизу:</string>
|
||||
|
|
|
@ -324,7 +324,6 @@
|
|||
<string name="duration_5_min">5 phút</string>
|
||||
<string name="create_poll_title">Bình chọn</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Vuốt qua lại giữa các tab</string>
|
||||
<string name="pref_title_show_notifications_filter">Hiện bộ lọc thông báo</string>
|
||||
<string name="failed_search">Không thể tìm thấy</string>
|
||||
<string name="title_accounts">Người</string>
|
||||
<string name="report_description_remote_instance">Người này thuộc máy chủ khác. Gửi luôn cho máy chủ đó\?</string>
|
||||
|
@ -666,4 +665,4 @@
|
|||
<string name="about_device_info_title">Thiết bị của bạn</string>
|
||||
<string name="list_exclusive_label">Ẩn khỏi bảng tin</string>
|
||||
<string name="error_media_playback">Không thể phát: %s</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -421,7 +421,6 @@
|
|||
<string name="report_description_remote_instance">该账户来自其他服务器。向那里发送一份匿名的报告副本?</string>
|
||||
<string name="title_accounts">账户</string>
|
||||
<string name="failed_search">搜索失败</string>
|
||||
<string name="pref_title_show_notifications_filter">显示通知过滤器</string>
|
||||
<string name="create_poll_title">投票</string>
|
||||
<string name="duration_5_min">5 分钟</string>
|
||||
<string name="duration_30_min">30 分钟</string>
|
||||
|
@ -680,4 +679,4 @@
|
|||
<string name="about_copy">复制版本及设备信息</string>
|
||||
<string name="list_exclusive_label">不出现在首页时间线中</string>
|
||||
<string name="error_media_playback">播放失败了:%s</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -471,7 +471,6 @@
|
|||
<string name="label_duration">期間</string>
|
||||
<string name="create_poll_title">投票</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">啟用在分頁間切換的滑動手勢</string>
|
||||
<string name="pref_title_show_notifications_filter">顯示通知過濾器</string>
|
||||
<string name="failed_search">搜尋失敗</string>
|
||||
<string name="title_accounts">帳號</string>
|
||||
<string name="failed_fetch_posts">擷取狀態失敗</string>
|
||||
|
|
|
@ -426,7 +426,6 @@
|
|||
<string name="failed_fetch_posts">無法獲取狀態</string>
|
||||
<string name="report_description_1">該報告將發送給您的伺服器管理員。 您可以在下面提供有關回報此帳戶的原因的說明:</string>
|
||||
<string name="report_description_remote_instance">該帳戶來自其他伺服器。向那裡發送一份匿名的報告副本?</string>
|
||||
<string name="pref_title_show_notifications_filter">顯示通知過濾器</string>
|
||||
<string name="hashtags">話題</string>
|
||||
<string name="notification_follow_request_name">關注請求</string>
|
||||
<string name="edit_poll">編輯</string>
|
||||
|
|
|
@ -610,8 +610,8 @@
|
|||
<string name="error_list_load">Error loading lists</string>
|
||||
<string name="list_exclusive_label">Hide from the home timeline</string>
|
||||
<string name="list">List</string>
|
||||
<string name="notifications_clear">Clear</string>
|
||||
<string name="notifications_apply_filter">Filter</string>
|
||||
<string name="notifications_clear">Delete notifications</string>
|
||||
<string name="notifications_apply_filter">Filter notifications</string>
|
||||
<string name="filter_apply">Apply</string>
|
||||
|
||||
<string name="compose_shortcut_long_label">Compose post</string>
|
||||
|
@ -670,7 +670,6 @@
|
|||
<string name="title_accounts">Accounts</string>
|
||||
<string name="failed_search">Failed to search</string>
|
||||
|
||||
<string name="pref_title_show_notifications_filter">Show Notifications filter</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Enable swipe gesture to switch between tabs</string>
|
||||
<string name="pref_title_show_stat_inline">Show post statistics in timeline</string>
|
||||
|
||||
|
|
|
@ -179,9 +179,4 @@
|
|||
<item name="materialDrawerMaskDrawable">@drawable/materialdrawer_shape_small</item>
|
||||
<item name="materialDrawerDrawCircularShadow">false</item>
|
||||
</style>
|
||||
|
||||
<style name="ShapeAppearance.Avatar" parent="ShapeAppearance.MaterialComponents">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">12.5%</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -99,7 +99,6 @@ abstract class NotificationsViewModelTestBase {
|
|||
PrefKeys.CONFIRM_REBLOGS to true,
|
||||
PrefKeys.CONFIRM_FAVOURITES to false,
|
||||
PrefKeys.WELLBEING_HIDE_STATS_POSTS to false,
|
||||
PrefKeys.SHOW_NOTIFICATIONS_FILTER to true,
|
||||
PrefKeys.FAB_HIDE to false
|
||||
)
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ class NotificationsViewModelTestUiState : NotificationsViewModelTestBase() {
|
|||
|
||||
private val initialUiState = UiState(
|
||||
activeFilter = setOf(Notification.Type.FOLLOW),
|
||||
showFilterOptions = true,
|
||||
showFabWhileScrolling = true
|
||||
)
|
||||
|
||||
|
@ -64,23 +63,4 @@ class NotificationsViewModelTestUiState : NotificationsViewModelTestBase() {
|
|||
assertThat(expectMostRecentItem().showFabWhileScrolling).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `showFilterOptions depends on SHOW_NOTIFICATIONS_FILTER preference`() = runTest {
|
||||
// Prior
|
||||
viewModel.uiState.test {
|
||||
assertThat(expectMostRecentItem().showFilterOptions).isTrue()
|
||||
}
|
||||
|
||||
// Given
|
||||
sharedPreferencesMap[PrefKeys.SHOW_NOTIFICATIONS_FILTER] = false
|
||||
|
||||
// When
|
||||
eventHub.dispatch(PreferenceChangedEvent(PrefKeys.SHOW_NOTIFICATIONS_FILTER))
|
||||
|
||||
// Then
|
||||
viewModel.uiState.test {
|
||||
assertThat(expectMostRecentItem().showFilterOptions).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue