Add basic support for swiping between messages

This commit is contained in:
cketti 2022-07-22 14:56:25 +02:00
parent c1a97f2c97
commit de6d4197f2
6 changed files with 416 additions and 134 deletions

View file

@ -54,7 +54,9 @@ import com.fsck.k9.ui.changelog.RecentChangesActivity
import com.fsck.k9.ui.changelog.RecentChangesViewModel
import com.fsck.k9.ui.managefolders.ManageFoldersActivity
import com.fsck.k9.ui.messagelist.DefaultFolderProvider
import com.fsck.k9.ui.messageview.MessageViewFragment
import com.fsck.k9.ui.messageview.Direction
import com.fsck.k9.ui.messageview.MessageViewContainerFragment
import com.fsck.k9.ui.messageview.MessageViewContainerFragment.MessageViewContainerListener
import com.fsck.k9.ui.messageview.MessageViewFragment.MessageViewFragmentListener
import com.fsck.k9.ui.messageview.PlaceholderFragment
import com.fsck.k9.ui.onboarding.OnboardingActivity
@ -80,6 +82,7 @@ open class MessageList :
K9Activity(),
MessageListFragmentListener,
MessageViewFragmentListener,
MessageViewContainerListener,
FragmentManager.OnBackStackChangedListener,
OnSwitchCompleteListener,
PermissionUiHelper {
@ -102,11 +105,16 @@ open class MessageList :
private var progressBar: ProgressBar? = null
private var messageViewPlaceHolder: PlaceholderFragment? = null
private var messageListFragment: MessageListFragment? = null
private var messageViewFragment: MessageViewFragment? = null
private var messageViewContainerFragment: MessageViewContainerFragment? = null
private var account: Account? = null
private var search: LocalSearch? = null
private var singleFolderMode = false
private var lastDirection = if (K9.isMessageViewShowNext) NEXT else PREVIOUS
private val lastDirection: Direction
get() {
return messageViewContainerFragment?.lastDirection
?: if (K9.isMessageViewShowNext) Direction.NEXT else Direction.PREVIOUS
}
private var messageListActivityAppearance: MessageListActivityAppearance? = null
@ -225,7 +233,7 @@ open class MessageList :
supportFragmentManager.popBackStackImmediate(FIRST_FRAGMENT_TRANSACTION, FragmentManager.POP_BACK_STACK_INCLUSIVE)
removeMessageListFragment()
removeMessageViewFragment()
removeMessageViewContainerFragment()
messageReference = null
search = null
@ -253,9 +261,11 @@ open class MessageList :
private fun findFragments() {
val fragmentManager = supportFragmentManager
messageListFragment = fragmentManager.findFragmentById(R.id.message_list_container) as MessageListFragment?
messageViewFragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG_MESSAGE_VIEW) as MessageViewFragment?
messageViewContainerFragment =
fragmentManager.findFragmentByTag(FRAGMENT_TAG_MESSAGE_VIEW_CONTAINER) as MessageViewContainerFragment?
messageListFragment?.let { messageListFragment ->
messageViewContainerFragment?.setViewModel(messageListFragment.viewModel)
initializeFromLocalSearch(messageListFragment.localSearch)
}
}
@ -278,7 +288,7 @@ open class MessageList :
// Check if the fragment wasn't restarted and has a MessageReference in the arguments.
// If so, open the referenced message.
if (!hasMessageListFragment && messageViewFragment == null && messageReference != null) {
if (!hasMessageListFragment && messageViewContainerFragment == null && messageReference != null) {
openMessage(messageReference!!)
}
}
@ -304,7 +314,7 @@ open class MessageList :
}
}
displayMode = if (messageViewFragment != null || messageReference != null) {
displayMode = if (messageViewContainerFragment != null || messageReference != null) {
DisplayMode.MESSAGE_VIEW
} else {
DisplayMode.MESSAGE_LIST
@ -337,12 +347,12 @@ open class MessageList :
messageListWasDisplayed = true
messageListFragment.isActive = true
messageViewFragment.let { messageViewFragment ->
if (messageViewFragment == null) {
messageViewContainerFragment.let { messageViewContainerFragment ->
if (messageViewContainerFragment == null) {
showMessageViewPlaceHolder()
} else {
messageViewFragment.isActive = true
val activeMessage = messageViewFragment.messageReference
messageViewContainerFragment.isActive = true
val activeMessage = messageViewContainerFragment.messageReference
messageListFragment.setActiveMessage(activeMessage)
}
}
@ -607,7 +617,7 @@ open class MessageList :
fun openFolder(folderId: Long) {
if (displayMode == DisplayMode.SPLIT_VIEW) {
removeMessageViewFragment()
removeMessageViewContainerFragment()
showMessageViewPlaceHolder()
}
@ -736,7 +746,7 @@ open class MessageList :
when (event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP -> {
if (messageViewFragment != null && displayMode != DisplayMode.MESSAGE_LIST &&
if (messageViewContainerFragment != null && displayMode != DisplayMode.MESSAGE_LIST &&
K9.isUseVolumeKeysForNavigation
) {
showPreviousMessage()
@ -747,7 +757,7 @@ open class MessageList :
}
}
KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (messageViewFragment != null && displayMode != DisplayMode.MESSAGE_LIST &&
if (messageViewContainerFragment != null && displayMode != DisplayMode.MESSAGE_LIST &&
K9.isUseVolumeKeysForNavigation
) {
showNextMessage()
@ -762,14 +772,14 @@ open class MessageList :
return true
}
KeyEvent.KEYCODE_DPAD_LEFT -> {
return if (messageViewFragment != null && displayMode == DisplayMode.MESSAGE_VIEW) {
return if (messageViewContainerFragment != null && displayMode == DisplayMode.MESSAGE_VIEW) {
showPreviousMessage()
} else {
false
}
}
KeyEvent.KEYCODE_DPAD_RIGHT -> {
return if (messageViewFragment != null && displayMode == DisplayMode.MESSAGE_VIEW) {
return if (messageViewContainerFragment != null && displayMode == DisplayMode.MESSAGE_VIEW) {
showNextMessage()
} else {
false
@ -801,69 +811,69 @@ open class MessageList :
'g' -> {
if (displayMode == DisplayMode.MESSAGE_LIST) {
messageListFragment!!.onToggleFlagged()
} else if (messageViewFragment != null) {
messageViewFragment!!.onToggleFlagged()
} else if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onToggleFlagged()
}
return true
}
'm' -> {
if (displayMode == DisplayMode.MESSAGE_LIST) {
messageListFragment!!.onMove()
} else if (messageViewFragment != null) {
messageViewFragment!!.onMove()
} else if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onMove()
}
return true
}
'v' -> {
if (displayMode == DisplayMode.MESSAGE_LIST) {
messageListFragment!!.onArchive()
} else if (messageViewFragment != null) {
messageViewFragment!!.onArchive()
} else if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onArchive()
}
return true
}
'y' -> {
if (displayMode == DisplayMode.MESSAGE_LIST) {
messageListFragment!!.onCopy()
} else if (messageViewFragment != null) {
messageViewFragment!!.onCopy()
} else if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onCopy()
}
return true
}
'z' -> {
if (displayMode == DisplayMode.MESSAGE_LIST) {
messageListFragment!!.onToggleRead()
} else if (messageViewFragment != null) {
messageViewFragment!!.onToggleRead()
} else if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onToggleRead()
}
return true
}
'f' -> {
if (messageViewFragment != null) {
messageViewFragment!!.onForward()
if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onForward()
}
return true
}
'a' -> {
if (messageViewFragment != null) {
messageViewFragment!!.onReplyAll()
if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onReplyAll()
}
return true
}
'r' -> {
if (messageViewFragment != null) {
messageViewFragment!!.onReply()
if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onReply()
}
return true
}
'j', 'p' -> {
if (messageViewFragment != null) {
if (messageViewContainerFragment != null) {
showPreviousMessage()
}
return true
}
'n', 'k' -> {
if (messageViewFragment != null) {
if (messageViewContainerFragment != null) {
showNextMessage()
}
return true
@ -885,8 +895,8 @@ open class MessageList :
private fun onDeleteHotKey() {
if (displayMode == DisplayMode.MESSAGE_LIST) {
messageListFragment!!.onDelete()
} else if (messageViewFragment != null) {
messageViewFragment!!.onDelete()
} else if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onDelete()
}
}
@ -979,12 +989,16 @@ open class MessageList :
messageListFragment!!.setActiveMessage(messageReference)
}
val fragment = MessageViewFragment.newInstance(messageReference)
val fragment = MessageViewContainerFragment.newInstance(messageReference)
supportFragmentManager.commitNow {
replace(R.id.message_view_container, fragment, FRAGMENT_TAG_MESSAGE_VIEW)
replace(R.id.message_view_container, fragment, FRAGMENT_TAG_MESSAGE_VIEW_CONTAINER)
}
messageViewFragment = fragment
messageViewContainerFragment = fragment
messageListFragment?.let { messageListFragment ->
fragment.setViewModel(messageListFragment.viewModel)
}
if (displayMode == DisplayMode.SPLIT_VIEW) {
fragment.isActive = true
@ -1089,7 +1103,7 @@ open class MessageList :
}
private fun showMessageViewPlaceHolder() {
removeMessageViewFragment()
removeMessageViewContainerFragment()
// Add placeholder fragment if necessary
val fragmentManager = supportFragmentManager
@ -1102,11 +1116,11 @@ open class MessageList :
messageListFragment!!.setActiveMessage(null)
}
private fun removeMessageViewFragment() {
if (messageViewFragment != null) {
private fun removeMessageViewContainerFragment() {
if (messageViewContainerFragment != null) {
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.remove(messageViewFragment!!)
messageViewFragment = null
fragmentTransaction.remove(messageViewContainerFragment!!)
messageViewContainerFragment = null
fragmentTransaction.commit()
showDefaultTitleView()
@ -1129,29 +1143,35 @@ open class MessageList :
}
}
override fun closeMessageView() {
returnToMessageList()
}
override fun setActiveMessage(messageReference: MessageReference) {
val messageListFragment = checkNotNull(messageListFragment)
messageListFragment.setActiveMessage(messageReference)
}
override fun showNextMessageOrReturn() {
if (K9.isMessageViewReturnToList || !showLogicalNextMessage()) {
if (displayMode == DisplayMode.SPLIT_VIEW) {
showMessageViewPlaceHolder()
} else {
showMessageList()
}
returnToMessageList()
}
}
private fun returnToMessageList() {
if (displayMode == DisplayMode.SPLIT_VIEW) {
showMessageViewPlaceHolder()
} else {
showMessageList()
}
}
private fun showLogicalNextMessage(): Boolean {
var result = false
if (lastDirection == NEXT) {
result = showNextMessage()
} else if (lastDirection == PREVIOUS) {
result = showPreviousMessage()
return when (lastDirection) {
Direction.NEXT -> showNextMessage()
Direction.PREVIOUS -> showPreviousMessage()
}
if (!result) {
result = showNextMessage() || showPreviousMessage()
}
return result
}
override fun setProgress(enable: Boolean) {
@ -1159,25 +1179,15 @@ open class MessageList :
}
private fun showNextMessage(): Boolean {
val ref = messageViewFragment!!.messageReference
if (ref != null) {
if (messageListFragment!!.openNext(ref)) {
lastDirection = NEXT
return true
}
}
return false
val messageViewContainerFragment = checkNotNull(messageViewContainerFragment)
return messageViewContainerFragment.showNextMessage()
}
private fun showPreviousMessage(): Boolean {
val ref = messageViewFragment!!.messageReference
if (ref != null) {
if (messageListFragment!!.openPrevious(ref)) {
lastDirection = PREVIOUS
return true
}
}
return false
val messageViewContainerFragment = checkNotNull(messageViewContainerFragment)
return messageViewContainerFragment.showPreviousMessage()
}
private fun showMessageList() {
@ -1186,7 +1196,7 @@ open class MessageList :
displayMode = DisplayMode.MESSAGE_LIST
viewSwitcher!!.showFirstView()
messageViewFragment?.isActive = false
messageViewContainerFragment?.isActive = false
messageListFragment!!.isActive = true
messageListFragment!!.setActiveMessage(null)
@ -1208,11 +1218,11 @@ open class MessageList :
}
private fun showMessageView() {
val messageViewFragment = checkNotNull(this.messageViewFragment)
val messageViewContainerFragment = checkNotNull(this.messageViewContainerFragment)
displayMode = DisplayMode.MESSAGE_VIEW
messageListFragment?.isActive = false
messageViewFragment.isActive = true
messageViewContainerFragment.isActive = true
if (!messageListWasDisplayed) {
viewSwitcher!!.animateFirstView = false
@ -1238,7 +1248,7 @@ open class MessageList :
override fun onSwitchComplete(displayedChild: Int) {
if (displayedChild == 0) {
removeMessageViewFragment()
removeMessageViewContainerFragment()
}
}
@ -1276,8 +1286,8 @@ open class MessageList :
if (requestCode and REQUEST_FLAG_PENDING_INTENT != 0) {
val originalRequestCode = requestCode xor REQUEST_FLAG_PENDING_INTENT
if (messageViewFragment != null) {
messageViewFragment!!.onPendingIntentResult(originalRequestCode, resultCode, data)
if (messageViewContainerFragment != null) {
messageViewContainerFragment!!.onPendingIntentResult(originalRequestCode, resultCode, data)
}
}
}
@ -1387,16 +1397,11 @@ open class MessageList :
private const val STATE_DISPLAY_MODE = "displayMode"
private const val STATE_MESSAGE_VIEW_ONLY = "messageViewOnly"
private const val STATE_MESSAGE_LIST_WAS_DISPLAYED = "messageListWasDisplayed"
private const val STATE_FIRST_BACK_STACK_ID = "firstBackstackId"
private const val FIRST_FRAGMENT_TRANSACTION = "first"
private const val FRAGMENT_TAG_MESSAGE_VIEW = "MessageViewFragment"
private const val FRAGMENT_TAG_MESSAGE_VIEW_CONTAINER = "MessageViewContainerFragment"
private const val FRAGMENT_TAG_PLACEHOLDER = "MessageViewPlaceholder"
// Used for navigating to next/previous message
private const val PREVIOUS = 1
private const val NEXT = 2
private const val REQUEST_CODE_MASK = 0xFFFF0000.toInt()
private const val REQUEST_FLAG_PENDING_INTENT = 1 shl 15

View file

@ -66,7 +66,7 @@ class MessageListFragment :
ConfirmationDialogFragmentListener,
MessageListItemActionListener {
private val viewModel: MessageListViewModel by viewModel()
val viewModel: MessageListViewModel by viewModel()
private val sortTypeToastProvider: SortTypeToastProvider by inject()
private val folderNameFormatterFactory: FolderNameFormatterFactory by inject()
private val folderNameFormatter: FolderNameFormatter by lazy { folderNameFormatterFactory.create(requireContext()) }
@ -1299,30 +1299,6 @@ class MessageListFragment :
}
}
fun openPrevious(messageReference: MessageReference): Boolean {
val position = getPosition(messageReference)
if (position <= 0) return false
openMessageAtPosition(position - 1)
return true
}
fun openNext(messageReference: MessageReference): Boolean {
val position = getPosition(messageReference)
if (position < 0 || position == adapter.count - 1) return false
openMessageAtPosition(position + 1)
return true
}
fun isFirst(messageReference: MessageReference): Boolean {
return adapter.isEmpty || messageReference == getReferenceForPosition(0)
}
fun isLast(messageReference: MessageReference): Boolean {
return adapter.isEmpty || messageReference == getReferenceForPosition(adapter.count - 1)
}
private fun getReferenceForPosition(position: Int): MessageReference {
val item = adapter.getItem(position)
return MessageReference(item.account.uuid, item.folderId, item.messageUid)
@ -1349,14 +1325,6 @@ class MessageListFragment :
fragmentListener.openMessage(messageReference)
}
private fun getPosition(messageReference: MessageReference): Int {
return adapter.messages.indexOfFirst { messageListItem ->
messageListItem.account.uuid == messageReference.accountUuid &&
messageListItem.folderId == messageReference.folderId &&
messageListItem.messageUid == messageReference.uid
}
}
fun onReverseSort() {
changeSort(sortType)
}

View file

@ -0,0 +1,6 @@
package com.fsck.k9.ui.messageview
enum class Direction {
PREVIOUS,
NEXT
}

View file

@ -0,0 +1,311 @@
package com.fsck.k9.ui.messageview
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.fsck.k9.controller.MessageReference
import com.fsck.k9.ui.R
import com.fsck.k9.ui.messagelist.MessageListItem
import com.fsck.k9.ui.messagelist.MessageListViewModel
import com.fsck.k9.ui.withArguments
/**
* A fragment that uses [ViewPager2] to allow the user to swipe between messages.
*
* Individual messages are displayed using a [MessageViewFragment].
*/
class MessageViewContainerFragment : Fragment() {
var isActive: Boolean = false
set(value) {
field = value
setMenuVisibility(value)
}
lateinit var messageReference: MessageReference
private set
var lastDirection: Direction? = null
private set
private lateinit var fragmentListener: MessageViewContainerListener
private lateinit var viewPager: ViewPager2
private lateinit var adapter: MessageViewContainerAdapter
private var currentPosition: Int? = null
private val messageViewFragment: MessageViewFragment
get() {
check(isResumed)
val itemId = adapter.getItemId(messageReference)
// ViewPager2/FragmentStateAdapter don't provide an easy way to get hold of the Fragment for the active
// page. So we're using an implementation detail (the fragment tag) to find the fragment.
return childFragmentManager.findFragmentByTag("f$itemId") as MessageViewFragment
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
if (savedInstanceState == null) {
messageReference = MessageReference.parse(arguments?.getString(ARG_REFERENCE))
?: error("Missing argument $ARG_REFERENCE")
} else {
messageReference = MessageReference.parse(savedInstanceState.getString(STATE_MESSAGE_REFERENCE))
?: error("Missing state $STATE_MESSAGE_REFERENCE")
lastDirection = savedInstanceState.getSerializable(STATE_LAST_DIRECTION) as Direction?
}
adapter = MessageViewContainerAdapter(this)
}
override fun onAttach(context: Context) {
super.onAttach(context)
fragmentListener = try {
context as MessageViewContainerListener
} catch (e: ClassCastException) {
throw ClassCastException("This fragment must be attached to a MessageViewContainerListener")
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.message_view_container, container, false)
viewPager = view.findViewById(R.id.message_viewpager)
viewPager.isUserInputEnabled = true
viewPager.offscreenPageLimit = ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT
viewPager.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.HORIZONTAL))
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
// The message list is updated each time the active message is changed. To avoid message list updates
// during the animation, we only set the active message after the animation has finished.
override fun onPageScrollStateChanged(state: Int) {
if (state == ViewPager2.SCROLL_STATE_IDLE) {
setActiveMessage(viewPager.currentItem)
}
}
override fun onPageSelected(position: Int) {
if (viewPager.scrollState == ViewPager2.SCROLL_STATE_IDLE) {
setActiveMessage(position)
}
}
})
return view
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(STATE_MESSAGE_REFERENCE, messageReference.toIdentityString())
outState.putSerializable(STATE_LAST_DIRECTION, lastDirection)
}
fun setViewModel(viewModel: MessageListViewModel) {
viewModel.getMessageListLiveData().observe(this) { messageListInfo ->
updateMessageList(messageListInfo.messageListItems)
}
}
private fun updateMessageList(messageListItems: List<MessageListItem>) {
if (messageListItems.isEmpty() || messageListItems.none { it.messageReference == messageReference }) {
fragmentListener.closeMessageView()
return
}
adapter.messageList = messageListItems
// We only set the adapter on ViewPager2 after the message list has been loaded. This way ViewPager2 can
// restore its saved state after a configuration change.
if (viewPager.adapter == null) {
viewPager.adapter = adapter
}
val position = adapter.getPosition(messageReference)
viewPager.setCurrentItem(position, false)
}
private fun setActiveMessage(position: Int) {
rememberNavigationDirection(position, messageReference)
messageReference = adapter.getMessageReference(position)
fragmentListener.setActiveMessage(messageReference)
}
private fun rememberNavigationDirection(newPosition: Int, currentMessageReference: MessageReference) {
// When messages are added or removed from the list, the current position will change even though we're still
// displaying the same message. In those cases we don't want to update `lastDirection`.
val newMessageReference = adapter.getMessageReference(newPosition)
if (newMessageReference == currentMessageReference) {
currentPosition = newPosition
return
}
currentPosition?.let { currentPosition ->
lastDirection = if (newPosition < currentPosition) Direction.PREVIOUS else Direction.NEXT
}
currentPosition = newPosition
}
fun showPreviousMessage(): Boolean {
val newPosition = viewPager.currentItem - 1
return if (newPosition >= 0) {
setActiveMessage(newPosition)
val smoothScroll = true
viewPager.setCurrentItem(newPosition, smoothScroll)
true
} else {
false
}
}
fun showNextMessage(): Boolean {
val newPosition = viewPager.currentItem + 1
return if (newPosition < adapter.itemCount) {
setActiveMessage(newPosition)
val smoothScroll = true
viewPager.setCurrentItem(newPosition, smoothScroll)
true
} else {
false
}
}
fun onToggleFlagged() {
messageViewFragment.onToggleFlagged()
}
fun onMove() {
messageViewFragment.onMove()
}
fun onArchive() {
messageViewFragment.onArchive()
}
fun onCopy() {
messageViewFragment.onCopy()
}
fun onToggleRead() {
messageViewFragment.onToggleRead()
}
fun onForward() {
messageViewFragment.onForward()
}
fun onReplyAll() {
messageViewFragment.onReplyAll()
}
fun onReply() {
messageViewFragment.onReply()
}
fun onDelete() {
messageViewFragment.onDelete()
}
fun onPendingIntentResult(requestCode: Int, resultCode: Int, data: Intent?) {
messageViewFragment.onPendingIntentResult(requestCode, resultCode, data)
}
private class MessageViewContainerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
var messageList: List<MessageListItem> = emptyList()
set(value) {
val diffResult = DiffUtil.calculateDiff(
MessageListDiffCallback(oldMessageList = messageList, newMessageList = value)
)
field = value
diffResult.dispatchUpdatesTo(this)
}
override fun getItemCount(): Int {
return messageList.size
}
override fun getItemId(position: Int): Long {
return messageList[position].uniqueId
}
override fun containsItem(itemId: Long): Boolean {
return messageList.any { it.uniqueId == itemId }
}
override fun createFragment(position: Int): Fragment {
check(position in messageList.indices)
val messageReference = messageList[position].messageReference
return MessageViewFragment.newInstance(messageReference)
}
fun getMessageReference(position: Int): MessageReference {
check(position in messageList.indices)
return messageList[position].messageReference
}
fun getPosition(messageReference: MessageReference): Int {
return messageList.indexOfFirst { it.messageReference == messageReference }
}
fun getItemId(messageReference: MessageReference): Long {
return messageList.first { it.messageReference == messageReference }.uniqueId
}
}
private class MessageListDiffCallback(
private val oldMessageList: List<MessageListItem>,
private val newMessageList: List<MessageListItem>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldMessageList.size
override fun getNewListSize(): Int = newMessageList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldMessageList[oldItemPosition].uniqueId == newMessageList[newItemPosition].uniqueId
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
// Let MessageViewFragment deal with content changes
return areItemsTheSame(oldItemPosition, newItemPosition)
}
}
interface MessageViewContainerListener {
fun closeMessageView()
fun setActiveMessage(messageReference: MessageReference)
}
companion object {
private const val ARG_REFERENCE = "reference"
private const val STATE_MESSAGE_REFERENCE = "messageReference"
private const val STATE_LAST_DIRECTION = "lastDirection"
fun newInstance(reference: MessageReference): MessageViewContainerFragment {
return MessageViewContainerFragment().withArguments(
ARG_REFERENCE to reference.toIdentityString()
)
}
}
}
private val MessageListItem.messageReference: MessageReference
get() = MessageReference(account.uuid, folderId, messageUid)

View file

@ -90,17 +90,6 @@ class MessageViewFragment :
private var currentAttachmentViewInfo: AttachmentViewInfo? = null
private var isDeleteMenuItemDisabled: Boolean = false
/**
* Set this to `true` when the fragment should be considered active. When active, the fragment adds its actions to
* the toolbar. When inactive, the fragment won't add its actions to the toolbar, even it is still visible, e.g. as
* part of an animation.
*/
var isActive: Boolean = false
set(value) {
field = value
invalidateMenu()
}
override fun onAttach(context: Context) {
super.onAttach(context)
@ -191,8 +180,6 @@ class MessageViewFragment :
}
override fun onPrepareOptionsMenu(menu: Menu) {
if (!isActive) return
menu.findItem(R.id.delete).apply {
isVisible = K9.isMessageViewDeleteActionVisible
isEnabled = !isDeleteMenuItemDisabled

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager2.widget.ViewPager2 xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/message_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />