Merge branch 'develop' into prompt_to_save_before_leaving_changed_profile

This commit is contained in:
Martin Marconcini 2023-08-22 13:06:27 +02:00 committed by GitHub
commit 84915e6af5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 1641 additions and 727 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 dun 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 sil ou elle correspond au mot entier</string>
<string name="title_accounts">Comptes</string>

View File

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

View File

@ -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">Dhfhà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">Dhfhàillig leis an luchdadh suas: %s</string>
</resources>
</resources>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 daqueste domeni, nimai los flux dactualitat o las notificacion. Vòstres seguidors daqueste 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 quaplicat 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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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