Add "Refresh" accessibility menu (#3121)
* Add "Refresh" accessibility menu to TimelineFragment Per https://developer.android.com/reference/androidx/swiperefreshlayout/widget/SwipeRefreshLayout the layout does not provide accessibility events, and a menu item should be provided as an alternative method for refreshing the content. In `TimelineFragment`: - Implement the `MenuProvider` interface so it can populate the action bar menu in activities that host the fragment - Create a "Refresh" menu item, and refresh the state when it is selected `MainActivity` has to change how the menu is created, so that fragments can add items to it. In `MainActivity`: - Call `setSupportActionBar` so `mainToolbar` participates in menus - Implement the `MenuProvider` interface, and move menu creation there - Set the title via supportActionBar * Never show the refresh item as a menubar action Per guidelines in https://developer.android.com/develop/ui/views/touch-and-input/swipe/add-swipe-interface#AddRefreshAction * Add "Refresh" menu item for AccountMediaFragment Also, fix the colour of the refresh progress indicator * Implement "Refresh" for AnnouncementsActivity * Add "Refresh" menu for ConversationsFragment * Keep the tabs adapter over the life of the viewpager Make `tabs` `var` instead of `val` in `MainPagerAdapter` so it can be updated when tabs change. Then detach the `tabLayoutMediator`, update the tabs, and call `notifyItemRangeChanged` in `setupTabs()`. This fixes a bug (not sure if it's this code, or in ViewPager2) where assigning a new adapter to the view pager seemed to result in a leak of one or more fragments. This wasn't user-visible, but it's a leak, and it becomes user-visible when fragments want to display menus. This also fixes two other bugs: 1. Be on the left-most tab. Scroll down a bit. Then modify the tabs at "Account preferences > tabs", but keep the left-most tab as-is. Then go back to MainActivity. Your reading position in the left-most tab has been jumped to the top. 2. Be on any non-left-most tab. Then modify the tab list by reordering tabs (adding/removing tabs is also OK). Then go back to MainActivity. Your tab selection has been overridden, and the left-most tab has been selected. Because the fragments are not destroyed unnecessarily your reading position is retained. And it remembers the tab you had selected, and as long as that tab is still present you will be returned to it, even if it's changed position in the list. Fixes https://github.com/tuskyapp/Tusky/issues/3251 * Add "Refresh" menu for ScheduledStatusActivity * Lint * Add "Refresh" menu for SearchFragment / SearchActivity * Explicitly set the searchview width Using "collapseActionView" requires the user to press "Back" twice to exit the activity, which is not acceptable. * Move toolbar handling in to ViewThreadActivity Previous code had the toolbar in the fragment's layout. Refactor to make consistent with other activities, and move the toolbar in to the activity layout. Implement MenuProvider in ViewThreadFragment to adjust the menu in the activity. * Add "Refresh" menu to ViewThreadFragment * Implement "Refresh" for ViewEditsFragment * Lint * Add "Refresh" menu to ReportStatusesFragment * Add "Refresh" menu to NotificationsFragment * Rename menu resource files Be consistent with the layout resource files, which have an activity/fragment prefix, then the lower_snake_case name of the activity or fragment it's for. * Only enable refresh menu if swiptorefresh is enabled Some timelines don't have swipetorefresh enabled (e.g., those shown on AccountActivity). In those cases don't add the refresh menu, rely on the hosting activity to provide it. Update AccountActivity to provide the refresh menu item.
This commit is contained in:
parent
f9588b48e2
commit
1b6108ca94
|
@ -29,6 +29,8 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
|
@ -40,6 +42,7 @@ import androidx.core.app.ActivityCompat
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
|
@ -135,7 +138,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector {
|
||||
class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector, MenuProvider {
|
||||
@Inject
|
||||
lateinit var androidInjector: DispatchingAndroidInjector<Any>
|
||||
|
||||
|
@ -244,6 +247,7 @@ 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)
|
||||
|
||||
|
@ -257,17 +261,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
|
||||
loadDrawerAvatar(activeAccount.profilePictureUrl, true)
|
||||
|
||||
binding.mainToolbar.menu.add(R.string.action_search).apply {
|
||||
setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
icon = IconicsDrawable(this@MainActivity, GoogleMaterial.Icon.gmd_search).apply {
|
||||
sizeDp = 20
|
||||
colorInt = MaterialColors.getColor(binding.mainToolbar, android.R.attr.textColorPrimary)
|
||||
}
|
||||
setOnMenuItemClickListener {
|
||||
startActivity(SearchActivity.getIntent(this@MainActivity))
|
||||
true
|
||||
}
|
||||
}
|
||||
addMenuProvider(this)
|
||||
|
||||
binding.viewPager.reduceSwipeSensitivity()
|
||||
|
||||
|
@ -352,6 +346,26 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
draftsAlert.observeInContext(this, true)
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.activity_main, menu)
|
||||
menu.findItem(R.id.action_search)?.apply {
|
||||
icon = IconicsDrawable(this@MainActivity, GoogleMaterial.Icon.gmd_search).apply {
|
||||
sizeDp = 20
|
||||
colorInt = MaterialColors.getColor(binding.mainToolbar, android.R.attr.textColorPrimary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.action_search -> {
|
||||
startActivity(SearchActivity.getIntent(this@MainActivity))
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val currentEmojiPack = preferences.getString(EMOJI_PREFERENCE, "")
|
||||
|
@ -745,7 +759,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
|
||||
val activeTabPosition = if (selectNotificationTab) notificationTabPosition else 0
|
||||
binding.mainToolbar.title = tabs[activeTabPosition].title(this@MainActivity)
|
||||
supportActionBar?.title = tabs[activeTabPosition].title(this@MainActivity)
|
||||
binding.mainToolbar.setOnClickListener {
|
||||
(tabAdapter.getFragment(activeTabLayout.selectedTabPosition) as? ReselectableFragment)?.onReselect()
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.graphics.drawable.LayerDrawable
|
|||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -35,6 +36,7 @@ import androidx.annotation.Px
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
@ -88,6 +90,10 @@ import com.keylesspalace.tusky.util.unsafeLazy
|
|||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import com.keylesspalace.tusky.view.showMuteAccountDialog
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import java.text.NumberFormat
|
||||
|
@ -97,7 +103,7 @@ import java.util.Locale
|
|||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
|
||||
class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector, LinkListener {
|
||||
class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider, HasAndroidInjector, LinkListener {
|
||||
|
||||
@Inject
|
||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
||||
|
@ -153,6 +159,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
loadResources()
|
||||
makeNotificationBarTransparent()
|
||||
setContentView(binding.root)
|
||||
addMenuProvider(this)
|
||||
|
||||
// Obtain information to fill out the profile.
|
||||
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!)
|
||||
|
@ -414,14 +421,16 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
draftsAlert.observeInContext(this, true)
|
||||
}
|
||||
|
||||
private fun onRefresh() {
|
||||
viewModel.refresh()
|
||||
adapter.refreshContent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup swipe to refresh layout
|
||||
*/
|
||||
private fun setupRefreshLayout() {
|
||||
binding.swipeToRefreshLayout.setOnRefreshListener {
|
||||
viewModel.refresh()
|
||||
adapter.refreshContent()
|
||||
}
|
||||
binding.swipeToRefreshLayout.setOnRefreshListener { onRefresh() }
|
||||
viewModel.isRefreshing.observe(
|
||||
this
|
||||
) { isRefreshing ->
|
||||
|
@ -731,7 +740,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.account_toolbar, menu)
|
||||
|
||||
val openAsItem = menu.findItem(R.id.action_open_as)
|
||||
|
@ -796,7 +805,12 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
menu.removeItem(R.id.action_add_or_remove_from_list)
|
||||
}
|
||||
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
menu.findItem(R.id.action_search)?.apply {
|
||||
icon = IconicsDrawable(this@AccountActivity, GoogleMaterial.Icon.gmd_search).apply {
|
||||
sizeDp = 20
|
||||
colorInt = MaterialColors.getColor(binding.collapsingToolbar, android.R.attr.textColorPrimary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFollowRequestPendingDialog() {
|
||||
|
@ -884,7 +898,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
viewUrl(url)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_open_in_web -> {
|
||||
// If the account isn't loaded yet, eat the input.
|
||||
|
@ -949,6 +963,11 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
viewModel.changeShowReblogsState()
|
||||
return true
|
||||
}
|
||||
R.id.action_refresh -> {
|
||||
binding.swipeToRefreshLayout.isRefreshing = true
|
||||
onRefresh()
|
||||
return true
|
||||
}
|
||||
R.id.action_report -> {
|
||||
loadedAccount?.let { loadedAccount ->
|
||||
startActivity(ReportActivity.getIntent(this, viewModel.accountId, loadedAccount.username))
|
||||
|
@ -956,7 +975,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
|||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getActionButton(): FloatingActionButton? {
|
||||
|
|
|
@ -16,15 +16,21 @@
|
|||
package com.keylesspalace.tusky.components.account.media
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.LoadState
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.databinding.FragmentTimelineBinding
|
||||
|
@ -39,20 +45,22 @@ import com.keylesspalace.tusky.util.openLink
|
|||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by charlag on 26/10/2017.
|
||||
*
|
||||
* Fragment with multiple columns of media previews for the specified account.
|
||||
*/
|
||||
|
||||
class AccountMediaFragment :
|
||||
Fragment(R.layout.fragment_timeline),
|
||||
RefreshableFragment,
|
||||
MenuProvider,
|
||||
Injectable {
|
||||
|
||||
@Inject
|
||||
|
@ -73,6 +81,7 @@ class AccountMediaFragment :
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
|
||||
val alwaysShowSensitiveMedia = accountManager.activeAccount!!.alwaysShowSensitiveMedia
|
||||
|
||||
|
@ -95,6 +104,8 @@ class AccountMediaFragment :
|
|||
binding.recyclerView.adapter = adapter
|
||||
|
||||
binding.swipeRefreshLayout.isEnabled = false
|
||||
binding.swipeRefreshLayout.setOnRefreshListener { refreshContent() }
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
|
||||
binding.statusView.visibility = View.GONE
|
||||
|
||||
|
@ -108,6 +119,10 @@ class AccountMediaFragment :
|
|||
binding.statusView.hide()
|
||||
binding.progressBar.hide()
|
||||
|
||||
if (loadState.refresh != LoadState.Loading && loadState.source.refresh != LoadState.Loading) {
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
if (adapter.itemCount == 0) {
|
||||
when (loadState.refresh) {
|
||||
is LoadState.NotLoading -> {
|
||||
|
@ -133,6 +148,27 @@ class AccountMediaFragment :
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_account_media, menu)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
refreshContent()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAttachmentClick(selected: AttachmentViewData, view: View) {
|
||||
if (!selected.isRevealed) {
|
||||
viewModel.revealAttachment(selected)
|
||||
|
|
|
@ -19,12 +19,17 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.PopupWindow
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.keylesspalace.tusky.BottomSheetActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.StatusListActivity
|
||||
|
@ -42,9 +47,18 @@ import com.keylesspalace.tusky.util.show
|
|||
import com.keylesspalace.tusky.util.unsafeLazy
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.view.EmojiPicker
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import javax.inject.Inject
|
||||
|
||||
class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener, OnEmojiSelectedListener, Injectable {
|
||||
class AnnouncementsActivity :
|
||||
BottomSheetActivity(),
|
||||
AnnouncementActionListener,
|
||||
OnEmojiSelectedListener,
|
||||
MenuProvider,
|
||||
Injectable {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -71,6 +85,7 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
addMenuProvider(this)
|
||||
|
||||
setSupportActionBar(binding.includedToolbar.toolbar)
|
||||
supportActionBar?.apply {
|
||||
|
@ -130,6 +145,27 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
|
|||
binding.progressBar.show()
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.activity_announcements, menu)
|
||||
menu.findItem(R.id.action_search)?.apply {
|
||||
icon = IconicsDrawable(this@AnnouncementsActivity, GoogleMaterial.Icon.gmd_search).apply {
|
||||
sizeDp = 20
|
||||
colorInt = MaterialColors.getColor(binding.includedToolbar.toolbar, android.R.attr.textColorPrimary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
refreshAnnouncements()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshAnnouncements() {
|
||||
viewModel.load()
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
|
|
|
@ -17,10 +17,14 @@ package com.keylesspalace.tusky.components.conversation
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -32,6 +36,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import at.connyduck.sparkbutton.helpers.Utils
|
||||
import autodispose2.androidx.lifecycle.autoDispose
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.StatusListActivity
|
||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder
|
||||
|
@ -52,6 +57,10 @@ import com.keylesspalace.tusky.util.hide
|
|||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
@ -61,7 +70,12 @@ import javax.inject.Inject
|
|||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
class ConversationsFragment : SFragment(), StatusActionListener, Injectable, ReselectableFragment {
|
||||
class ConversationsFragment :
|
||||
SFragment(),
|
||||
StatusActionListener,
|
||||
Injectable,
|
||||
ReselectableFragment,
|
||||
MenuProvider {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -82,6 +96,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(view.context)
|
||||
|
||||
val statusDisplayOptions = StatusDisplayOptions(
|
||||
|
@ -189,6 +205,27 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_conversations, menu)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
refreshContent()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(context)
|
||||
|
@ -200,10 +237,12 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
|||
binding.recyclerView.adapter = adapter.withLoadStateFooter(ConversationLoadStateAdapter(adapter::retry))
|
||||
}
|
||||
|
||||
private fun initSwipeToRefresh() {
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
private fun refreshContent() {
|
||||
adapter.refresh()
|
||||
}
|
||||
|
||||
private fun initSwipeToRefresh() {
|
||||
binding.swipeRefreshLayout.setOnRefreshListener { refreshContent() }
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,17 +16,24 @@
|
|||
package com.keylesspalace.tusky.components.report.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.LoadState
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.StatusListActivity
|
||||
|
@ -48,11 +55,20 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions
|
|||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Injectable, AdapterHandler {
|
||||
class ReportStatusesFragment :
|
||||
Fragment(R.layout.fragment_report_statuses),
|
||||
Injectable,
|
||||
OnRefreshListener,
|
||||
MenuProvider,
|
||||
AdapterHandler {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -90,18 +106,42 @@ class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), Inje
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
handleClicks()
|
||||
initStatusesView()
|
||||
setupSwipeRefreshLayout()
|
||||
}
|
||||
|
||||
private fun setupSwipeRefreshLayout() {
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_report_statuses, menu)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
onRefresh()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
snackbarErrorRetry?.dismiss()
|
||||
adapter.refresh()
|
||||
}
|
||||
|
||||
private fun setupSwipeRefreshLayout() {
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this)
|
||||
}
|
||||
|
||||
private fun initStatusesView() {
|
||||
|
|
|
@ -18,13 +18,18 @@ package com.keylesspalace.tusky.components.scheduled
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.LoadState
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import autodispose2.androidx.lifecycle.autoDispose
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.keylesspalace.tusky.BaseActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
|
@ -36,12 +41,21 @@ import com.keylesspalace.tusky.di.ViewModelFactory
|
|||
import com.keylesspalace.tusky.entity.ScheduledStatus
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class ScheduledStatusActivity : BaseActivity(), ScheduledStatusActionListener, Injectable {
|
||||
class ScheduledStatusActivity :
|
||||
BaseActivity(),
|
||||
ScheduledStatusActionListener,
|
||||
MenuProvider,
|
||||
Injectable {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -51,13 +65,15 @@ class ScheduledStatusActivity : BaseActivity(), ScheduledStatusActionListener, I
|
|||
|
||||
private val viewModel: ScheduledStatusViewModel by viewModels { viewModelFactory }
|
||||
|
||||
private val binding by viewBinding(ActivityScheduledStatusBinding::inflate)
|
||||
|
||||
private val adapter = ScheduledStatusAdapter(this)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val binding = ActivityScheduledStatusBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
addMenuProvider(this)
|
||||
|
||||
setSupportActionBar(binding.includedToolbar.toolbar)
|
||||
supportActionBar?.run {
|
||||
|
@ -113,6 +129,27 @@ class ScheduledStatusActivity : BaseActivity(), ScheduledStatusActionListener, I
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.activity_announcements, menu)
|
||||
menu.findItem(R.id.action_search)?.apply {
|
||||
icon = IconicsDrawable(this@ScheduledStatusActivity, GoogleMaterial.Icon.gmd_search).apply {
|
||||
sizeDp = 20
|
||||
colorInt = MaterialColors.getColor(binding.includedToolbar.toolbar, android.R.attr.textColorPrimary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
refreshStatuses()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshStatuses() {
|
||||
adapter.refresh()
|
||||
}
|
||||
|
|
|
@ -20,8 +20,11 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.keylesspalace.tusky.BottomSheetActivity
|
||||
|
@ -37,7 +40,7 @@ import dagger.android.DispatchingAndroidInjector
|
|||
import dagger.android.HasAndroidInjector
|
||||
import javax.inject.Inject
|
||||
|
||||
class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
|
||||
class SearchActivity : BottomSheetActivity(), HasAndroidInjector, MenuProvider {
|
||||
@Inject
|
||||
lateinit var androidInjector: DispatchingAndroidInjector<Any>
|
||||
|
||||
|
@ -59,6 +62,7 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
setDisplayShowHomeEnabled(true)
|
||||
setDisplayShowTitleEnabled(false)
|
||||
}
|
||||
addMenuProvider(this)
|
||||
setupPages()
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
@ -81,17 +85,18 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
handleIntent(intent)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
super.onCreateOptionsMenu(menu)
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.search_toolbar, menu)
|
||||
val searchView = menu.findItem(R.id.action_search)
|
||||
.actionView as SearchView
|
||||
val searchViewMenuItem = menu.findItem(R.id.action_search)
|
||||
searchViewMenuItem.expandActionView()
|
||||
val searchView = searchViewMenuItem.actionView as SearchView
|
||||
setupSearchView(searchView)
|
||||
|
||||
searchView.setQuery(viewModel.currentQuery, false)
|
||||
}
|
||||
|
||||
return true
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
|
@ -116,17 +121,42 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
|
||||
private fun setupSearchView(searchView: SearchView) {
|
||||
searchView.setIconifiedByDefault(false)
|
||||
|
||||
searchView.setSearchableInfo((getSystemService(Context.SEARCH_SERVICE) as? SearchManager)?.getSearchableInfo(componentName))
|
||||
|
||||
searchView.requestFocus()
|
||||
// SearchView has a bug. If it's displayed 'app:showAsAction="always"' it's too wide,
|
||||
// pushing other icons (including the options menu '...' icon) off the edge of the
|
||||
// screen.
|
||||
//
|
||||
// E.g., see:
|
||||
//
|
||||
// - https://stackoverflow.com/questions/41662373/android-toolbar-searchview-too-wide-to-move-other-items
|
||||
// - https://stackoverflow.com/questions/51525088/how-to-control-size-of-a-searchview-in-toolbar
|
||||
// - https://stackoverflow.com/questions/36976163/push-icons-away-when-expandig-searchview-in-android-toolbar
|
||||
// - https://issuetracker.google.com/issues/36976484
|
||||
//
|
||||
// The fix is to use 'app:showAsAction="ifRoom|collapseActionView"' and then immediately
|
||||
// expand it after inflating. That sets the width correctly.
|
||||
//
|
||||
// But if you do that code in AppCompatDelegateImpl activates, and when the user presses
|
||||
// the "Back" button the SearchView is first set to its collapsed state. The user has to
|
||||
// press "Back" again to exit the activity. This is clearly unacceptable.
|
||||
//
|
||||
// It appears to be impossible to override this behaviour on API level < 33.
|
||||
//
|
||||
// SearchView does allow you to specify the maximum width. So take the screen width,
|
||||
// subtract 48dp * 2 (for the menu icon and back icon on either side), convert to pixels,
|
||||
// and use that.
|
||||
val pxScreenWidth = resources.displayMetrics.widthPixels
|
||||
val pxBuffer = ((48 * 2) * resources.displayMetrics.density).toInt()
|
||||
searchView.maxWidth = pxScreenWidth - pxBuffer
|
||||
|
||||
searchView.maxWidth = Integer.MAX_VALUE
|
||||
searchView.requestFocus()
|
||||
}
|
||||
|
||||
override fun androidInjector() = androidInjector
|
||||
|
||||
companion object {
|
||||
const val TAG = "SearchActivity"
|
||||
fun getIntent(context: Context) = Intent(context, SearchActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package com.keylesspalace.tusky.components.search.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.PagingData
|
||||
|
@ -12,6 +17,7 @@ import androidx.recyclerview.widget.DividerItemDecoration
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.BottomSheetActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
|
@ -25,6 +31,10 @@ import com.keylesspalace.tusky.interfaces.LinkListener
|
|||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
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
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -34,7 +44,8 @@ abstract class SearchFragment<T : Any> :
|
|||
Fragment(R.layout.fragment_search),
|
||||
LinkListener,
|
||||
Injectable,
|
||||
SwipeRefreshLayout.OnRefreshListener {
|
||||
SwipeRefreshLayout.OnRefreshListener,
|
||||
MenuProvider {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -58,6 +69,7 @@ abstract class SearchFragment<T : Any> :
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
initAdapter()
|
||||
setupSwipeRefreshLayout()
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
subscribeObservables()
|
||||
}
|
||||
|
||||
|
@ -95,6 +107,27 @@ abstract class SearchFragment<T : Any> :
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_timeline, menu)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
onRefresh()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun initAdapter() {
|
||||
binding.searchRecyclerView.addItemDecoration(DividerItemDecoration(binding.searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||
binding.searchRecyclerView.layoutManager = LinearLayoutManager(binding.searchRecyclerView.context)
|
||||
|
|
|
@ -18,10 +18,14 @@ package com.keylesspalace.tusky.components.timeline
|
|||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -34,6 +38,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator
|
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import at.connyduck.sparkbutton.helpers.Utils
|
||||
import autodispose2.androidx.lifecycle.autoDispose
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.keylesspalace.tusky.BaseActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder
|
||||
|
@ -65,6 +70,10 @@ import com.keylesspalace.tusky.util.unsafeLazy
|
|||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
@ -79,7 +88,8 @@ class TimelineFragment :
|
|||
StatusActionListener,
|
||||
Injectable,
|
||||
ReselectableFragment,
|
||||
RefreshableFragment {
|
||||
RefreshableFragment,
|
||||
MenuProvider {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -198,6 +208,8 @@ class TimelineFragment :
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
|
||||
setupSwipeRefreshLayout()
|
||||
setupRecyclerView()
|
||||
|
||||
|
@ -293,6 +305,35 @@ class TimelineFragment :
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
if (isSwipeToRefreshEnabled) {
|
||||
menuInflater.inflate(R.menu.fragment_timeline, menu)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
if (isSwipeToRefreshEnabled) {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
|
||||
refreshContent()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the correct reading position in the timeline after the user clicked "Load more",
|
||||
* assuming the reading position should be below the freshly-loaded statuses.
|
||||
|
|
|
@ -21,18 +21,28 @@ import android.os.Bundle
|
|||
import androidx.fragment.app.commit
|
||||
import com.keylesspalace.tusky.BottomSheetActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.databinding.ActivityViewThreadBinding
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import javax.inject.Inject
|
||||
|
||||
class ViewThreadActivity : BottomSheetActivity(), HasAndroidInjector {
|
||||
|
||||
private val binding by viewBinding(ActivityViewThreadBinding::inflate)
|
||||
|
||||
@Inject
|
||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_view_thread)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
supportActionBar?.run {
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
setDisplayShowHomeEnabled(true)
|
||||
setDisplayShowTitleEnabled(true)
|
||||
}
|
||||
val id = intent.getStringExtra(ID_EXTRA)!!
|
||||
val url = intent.getStringExtra(URL_EXTRA)!!
|
||||
val fragment =
|
||||
|
|
|
@ -18,10 +18,14 @@ package com.keylesspalace.tusky.components.viewthread
|
|||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.CheckResult
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
|
@ -59,7 +63,12 @@ import kotlinx.coroutines.launch
|
|||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener, Injectable {
|
||||
class ViewThreadFragment :
|
||||
SFragment(),
|
||||
OnRefreshListener,
|
||||
StatusActionListener,
|
||||
MenuProvider,
|
||||
Injectable {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -74,6 +83,16 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
private var alwaysShowSensitiveMedia = false
|
||||
private var alwaysOpenSpoiler = false
|
||||
|
||||
/**
|
||||
* State of the "reveal" menu item that shows/hides content that is behind a content
|
||||
* warning. Setting this invalidates the menu to redraw the menu item.
|
||||
*/
|
||||
private var revealButtonState = RevealButtonState.NO_BUTTON
|
||||
set(value) {
|
||||
field = value
|
||||
requireActivity().invalidateMenu()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
thisThreadsStatusId = requireArguments().getString(ID_EXTRA)!!
|
||||
|
@ -107,24 +126,7 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||
}
|
||||
binding.toolbar.inflateMenu(R.menu.view_thread_toolbar)
|
||||
binding.toolbar.setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_reveal -> {
|
||||
viewModel.toggleRevealButton()
|
||||
true
|
||||
}
|
||||
R.id.action_open_in_web -> {
|
||||
context?.openLink(requireArguments().getString(URL_EXTRA)!!)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this)
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
|
@ -154,7 +156,7 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
viewModel.uiState.collect { uiState ->
|
||||
when (uiState) {
|
||||
is ThreadUiState.Loading -> {
|
||||
updateRevealButton(RevealButtonState.NO_BUTTON)
|
||||
revealButtonState = RevealButtonState.NO_BUTTON
|
||||
|
||||
binding.recyclerView.hide()
|
||||
binding.statusView.hide()
|
||||
|
@ -175,7 +177,7 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
|
||||
adapter.submitList(listOf(uiState.statusViewDatum))
|
||||
|
||||
updateRevealButton(uiState.revealButton)
|
||||
revealButtonState = uiState.revealButton
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
|
||||
binding.recyclerView.show()
|
||||
|
@ -186,18 +188,24 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
initialProgressBar.cancel()
|
||||
threadProgressBar.cancel()
|
||||
|
||||
updateRevealButton(RevealButtonState.NO_BUTTON)
|
||||
revealButtonState = RevealButtonState.NO_BUTTON
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
|
||||
binding.recyclerView.hide()
|
||||
binding.statusView.show()
|
||||
|
||||
if (uiState.throwable is IOException) {
|
||||
binding.statusView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
binding.statusView.setup(
|
||||
R.drawable.elephant_offline,
|
||||
R.string.error_network
|
||||
) {
|
||||
viewModel.retry(thisThreadsStatusId)
|
||||
}
|
||||
} else {
|
||||
binding.statusView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
binding.statusView.setup(
|
||||
R.drawable.elephant_error,
|
||||
R.string.error_generic
|
||||
) {
|
||||
viewModel.retry(thisThreadsStatusId)
|
||||
}
|
||||
}
|
||||
|
@ -216,11 +224,14 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
viewModel.isInitialLoad = false
|
||||
|
||||
// Ensure the top of the status is visible
|
||||
(binding.recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(uiState.detailedStatusPosition, 0)
|
||||
(binding.recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
|
||||
uiState.detailedStatusPosition,
|
||||
0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
updateRevealButton(uiState.revealButton)
|
||||
revealButtonState = uiState.revealButton
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
|
||||
binding.recyclerView.show()
|
||||
|
@ -247,6 +258,41 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
viewModel.loadThread(thisThreadsStatusId)
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_view_thread, menu)
|
||||
val actionReveal = menu.findItem(R.id.action_reveal)
|
||||
actionReveal.isVisible = revealButtonState != RevealButtonState.NO_BUTTON
|
||||
actionReveal.setIcon(
|
||||
when (revealButtonState) {
|
||||
RevealButtonState.REVEAL -> R.drawable.ic_eye_24dp
|
||||
else -> R.drawable.ic_hide_media_24dp
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_reveal -> {
|
||||
viewModel.toggleRevealButton()
|
||||
true
|
||||
}
|
||||
R.id.action_open_in_web -> {
|
||||
context?.openLink(requireArguments().getString(URL_EXTRA)!!)
|
||||
true
|
||||
}
|
||||
R.id.action_refresh -> {
|
||||
onRefresh()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
requireActivity().title = getString(R.string.title_view_thread)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a job to implement a delayed-visible progress bar.
|
||||
*
|
||||
|
@ -269,13 +315,6 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener,
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateRevealButton(state: RevealButtonState) {
|
||||
val menuItem = binding.toolbar.menu.findItem(R.id.action_reveal)
|
||||
|
||||
menuItem.isVisible = state != RevealButtonState.NO_BUTTON
|
||||
menuItem.setIcon(if (state == RevealButtonState.REVEAL) R.drawable.ic_eye_24dp else R.drawable.ic_hide_media_24dp)
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
viewModel.refresh(thisThreadsStatusId)
|
||||
}
|
||||
|
|
|
@ -17,15 +17,22 @@ package com.keylesspalace.tusky.components.viewthread.edits
|
|||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.keylesspalace.tusky.BottomSheetActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.StatusListActivity
|
||||
|
@ -38,11 +45,20 @@ import com.keylesspalace.tusky.settings.PrefKeys
|
|||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
class ViewEditsFragment : Fragment(R.layout.fragment_view_thread), LinkListener, Injectable {
|
||||
class ViewEditsFragment :
|
||||
Fragment(R.layout.fragment_view_thread),
|
||||
LinkListener,
|
||||
OnRefreshListener,
|
||||
MenuProvider,
|
||||
Injectable {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -54,12 +70,10 @@ class ViewEditsFragment : Fragment(R.layout.fragment_view_thread), LinkListener,
|
|||
private lateinit var statusId: String
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
activity?.onBackPressedDispatcher?.onBackPressed()
|
||||
}
|
||||
binding.toolbar.title = getString(R.string.title_edits)
|
||||
binding.swipeRefreshLayout.isEnabled = false
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(this)
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(context)
|
||||
|
@ -84,9 +98,11 @@ class ViewEditsFragment : Fragment(R.layout.fragment_view_thread), LinkListener,
|
|||
binding.statusView.hide()
|
||||
binding.initialProgressBar.show()
|
||||
}
|
||||
EditsUiState.Refreshing -> {}
|
||||
is EditsUiState.Error -> {
|
||||
Log.w(TAG, "failed to load edits", uiState.throwable)
|
||||
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
binding.recyclerView.hide()
|
||||
binding.statusView.show()
|
||||
binding.initialProgressBar.hide()
|
||||
|
@ -102,6 +118,7 @@ class ViewEditsFragment : Fragment(R.layout.fragment_view_thread), LinkListener,
|
|||
}
|
||||
}
|
||||
is EditsUiState.Success -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
binding.recyclerView.show()
|
||||
binding.statusView.hide()
|
||||
binding.initialProgressBar.hide()
|
||||
|
@ -121,6 +138,36 @@ class ViewEditsFragment : Fragment(R.layout.fragment_view_thread), LinkListener,
|
|||
viewModel.loadEdits(statusId)
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_view_edits, menu)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_refresh -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = true
|
||||
onRefresh()
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
requireActivity().title = getString(R.string.title_edits)
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
viewModel.loadEdits(statusId, force = true, refreshing = true)
|
||||
}
|
||||
|
||||
override fun onViewAccount(id: String) {
|
||||
bottomSheetActivity?.startActivityWithSlideInAnimation(AccountActivity.getIntent(requireContext(), id))
|
||||
}
|
||||
|
|
|
@ -20,8 +20,9 @@ import androidx.lifecycle.viewModelScope
|
|||
import at.connyduck.calladapter.networkresult.fold
|
||||
import com.keylesspalace.tusky.entity.StatusEdit
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -30,14 +31,17 @@ class ViewEditsViewModel @Inject constructor(
|
|||
) : ViewModel() {
|
||||
|
||||
private val _uiState: MutableStateFlow<EditsUiState> = MutableStateFlow(EditsUiState.Initial)
|
||||
val uiState: Flow<EditsUiState>
|
||||
get() = _uiState
|
||||
val uiState: StateFlow<EditsUiState> = _uiState.asStateFlow()
|
||||
|
||||
fun loadEdits(statusId: String, force: Boolean = false, refreshing: Boolean = false) {
|
||||
if (force || _uiState.value is EditsUiState.Initial) {
|
||||
if (!refreshing) {
|
||||
if (!force && _uiState.value !is EditsUiState.Initial) return
|
||||
|
||||
if (refreshing) {
|
||||
_uiState.value = EditsUiState.Refreshing
|
||||
} else {
|
||||
_uiState.value = EditsUiState.Loading
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
api.statusEdits(statusId).fold(
|
||||
{ edits ->
|
||||
|
@ -51,11 +55,14 @@ class ViewEditsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface EditsUiState {
|
||||
object Initial : EditsUiState
|
||||
object Loading : EditsUiState
|
||||
|
||||
// "Refreshing" state is necessary, otherwise a refresh state transition is Success -> Success,
|
||||
// and state flows don't emit repeated states, so the UI never updates.
|
||||
object Refreshing : EditsUiState
|
||||
class Error(val throwable: Throwable) : EditsUiState
|
||||
data class Success(
|
||||
val edits: List<StatusEdit>
|
||||
|
|
|
@ -27,6 +27,9 @@ import android.os.Bundle;
|
|||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
@ -39,6 +42,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import androidx.arch.core.util.Function;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.core.view.MenuProvider;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.AsyncDifferConfig;
|
||||
|
@ -120,7 +124,9 @@ public class NotificationsFragment extends SFragment implements
|
|||
StatusActionListener,
|
||||
NotificationsAdapter.NotificationActionListener,
|
||||
AccountActionListener,
|
||||
Injectable, ReselectableFragment {
|
||||
Injectable,
|
||||
MenuProvider,
|
||||
ReselectableFragment {
|
||||
private static final String TAG = "NotificationF"; // logging tag
|
||||
|
||||
private static final int LOAD_AT_ONCE = 30;
|
||||
|
@ -205,6 +211,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
requireActivity().addMenuProvider(this, getViewLifecycleOwner(), Lifecycle.State.RESUMED);
|
||||
|
||||
binding = FragmentTimelineNotificationsBinding.inflate(inflater, container, false);
|
||||
|
||||
@NonNull Context context = inflater.getContext(); // from inflater to silence warning
|
||||
|
@ -287,6 +295,22 @@ public class NotificationsFragment extends SFragment implements
|
|||
binding = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
|
||||
menuInflater.inflate(R.menu.fragment_notifications, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
|
||||
if (menuItem.getItemId() == R.id.action_refresh) {
|
||||
binding.swipeRefreshLayout.setRefreshing(true);
|
||||
onRefresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateFilterVisibility() {
|
||||
CoordinatorLayout.LayoutParams params =
|
||||
(CoordinatorLayout.LayoutParams) binding.swipeRefreshLayout.getLayoutParams();
|
||||
|
|
|
@ -4,22 +4,6 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="@dimen/actionbar_elevation">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:navigationContentDescription="@string/abc_action_bar_up_description"
|
||||
app:navigationIcon="?attr/homeAsUpIndicator"
|
||||
app:title="@string/title_view_thread" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="640dp"
|
||||
|
|
|
@ -6,6 +6,23 @@
|
|||
android:layout_height="match_parent"
|
||||
tools:context="com.keylesspalace.tusky.components.viewthread.ViewThreadActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="@dimen/actionbar_elevation"
|
||||
app:elevationOverlayEnabled="false">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
style="@style/Widget.AppCompat.Toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:navigationContentDescription="@string/action_open_drawer" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -4,22 +4,6 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="@dimen/actionbar_elevation">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:navigationContentDescription="@string/abc_action_bar_up_description"
|
||||
app:navigationIcon="?attr/homeAsUpIndicator"
|
||||
app:title="@string/title_view_thread" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -43,6 +43,11 @@
|
|||
android:title="@string/action_hide_reblogs"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_report"
|
||||
android:title="@string/action_report" />
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:title="@string/action_search"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -13,5 +13,8 @@
|
|||
app:showAsAction="ifRoom"
|
||||
android:icon="@drawable/ic_eye_24dp" />
|
||||
|
||||
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/action_refresh"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_search"
|
||||
android:title="@string/action_search"
|
||||
|
|
|
@ -730,6 +730,7 @@
|
|||
<string name="report_category_other">Other</string>
|
||||
|
||||
<string name="action_unfollow_hashtag_format">Unfollow #%s?</string>
|
||||
<string name="action_refresh">Refresh</string>
|
||||
<string name="mute_notifications_switch">Mute notifications</string>
|
||||
|
||||
<!-- Reading order preference -->
|
||||
|
|
Loading…
Reference in New Issue