Add toolbar to MediaPreviewV2 implementation.

This commit is contained in:
Nicholas 2022-10-03 15:10:30 -04:00 committed by Greyson Parrelli
parent 79b3b9190a
commit 4f3910e3ae
7 changed files with 139 additions and 21 deletions

View file

@ -7,7 +7,6 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.logging.Log
import org.signal.core.util.requireLong
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.database.AttachmentDatabase
import org.thoughtcrime.securesms.database.MediaDatabase
import org.thoughtcrime.securesms.database.MediaDatabase.Sorting
@ -29,11 +28,11 @@ class MediaPreviewRepository {
* @param sorting the ordering of the results
* @param limit the maximum quantity of the results
*/
fun getAttachments(startingUri: Uri, threadId: Long, sorting: Sorting, limit: Int = 500): Flowable<List<DatabaseAttachment>> {
fun getAttachments(startingUri: Uri, threadId: Long, sorting: Sorting, limit: Int = 500): Flowable<List<MediaDatabase.MediaRecord>> {
return Single.fromCallable {
val cursor = media.getGalleryMediaForThread(threadId, sorting)
val acc = mutableListOf<DatabaseAttachment>()
val acc = mutableListOf<MediaDatabase.MediaRecord>()
var attachmentUri: Uri? = null
while (cursor.moveToNext()) {
val attachmentId = AttachmentId(cursor.requireLong(AttachmentDatabase.ROW_ID), cursor.requireLong(AttachmentDatabase.UNIQUE_ID))
@ -45,7 +44,7 @@ class MediaPreviewRepository {
if (attachmentUri == startingUri) {
for (i in 0..limit) {
val element = MediaDatabase.MediaRecord.from(cursor).attachment
val element = MediaDatabase.MediaRecord.from(cursor)
if (element != null) {
acc.add(element)
}

View file

@ -21,8 +21,5 @@ class MediaPreviewV2Activity : AppCompatActivity(R.layout.activity_mediapreview_
companion object {
private const val FRAGMENT_TAG = "media_preview_fragment_v2"
private const val NOT_IN_A_THREAD = -2
const val THREAD_ID_EXTRA = "thread_id"
}
}

View file

@ -6,8 +6,9 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.util.MediaUtil
class PreviewMediaAdapter(val fragment: Fragment, val items: List<Attachment>) : FragmentStateAdapter(fragment) {
var autoPlayPosition = -1
class MediaPreviewV2Adapter(val fragment: Fragment) : FragmentStateAdapter(fragment) {
private var items: List<Attachment> = listOf()
private var autoPlayPosition = -1
override fun getItemCount(): Int {
return items.count()
@ -36,4 +37,11 @@ class PreviewMediaAdapter(val fragment: Fragment, val items: List<Attachment>) :
return fragment
}
fun updateBackingItems(newItems: Collection<Attachment>) {
if (newItems != items) {
items = newItems.toList()
notifyDataSetChanged()
}
}
}

View file

@ -1,9 +1,11 @@
package org.thoughtcrime.securesms.mediapreview
import android.content.Context
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
@ -11,7 +13,11 @@ import org.thoughtcrime.securesms.animation.DepthPageTransformer
import org.thoughtcrime.securesms.components.ViewBinderDelegate
import org.thoughtcrime.securesms.database.MediaDatabase
import org.thoughtcrime.securesms.databinding.FragmentMediaPreviewV2Binding
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.FullscreenHelper
import org.thoughtcrime.securesms.util.LifecycleDisposable
import java.util.Locale
class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), MediaPreviewFragment.Events {
private val TAG = Log.tag(MediaPreviewV2Fragment::class.java)
@ -20,24 +26,92 @@ class MediaPreviewV2Fragment : Fragment(R.layout.fragment_media_preview_v2), Med
private val binding by ViewBinderDelegate(FragmentMediaPreviewV2Binding::bind)
private val viewModel: MediaPreviewV2ViewModel by viewModels()
private lateinit var fullscreenHelper: FullscreenHelper
override fun onAttach(context: Context) {
super.onAttach(context)
fullscreenHelper = FullscreenHelper(requireActivity())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initializeViewModel()
binding.mediaPager.offscreenPageLimit = 1
binding.mediaPager.setPageTransformer(DepthPageTransformer())
lifecycleDisposable += viewModel.state.distinctUntilChanged().observeOn(AndroidSchedulers.mainThread()).subscribe {
if (it.loadState == MediaPreviewV2State.LoadState.READY) {
binding.mediaPager.adapter = PreviewMediaAdapter(this, it.attachments)
val adapter = MediaPreviewV2Adapter(this)
binding.mediaPager.adapter = adapter
binding.mediaPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
viewModel.setCurrentPage(position)
}
})
initializeFullScreenUi()
lifecycleDisposable += viewModel.state.distinctUntilChanged().observeOn(AndroidSchedulers.mainThread()).subscribe {
bindCurrentState(it)
}
initializeViewModel()
}
private fun initializeFullScreenUi() {
fullscreenHelper.configureToolbarLayout(binding.toolbarCutoutSpacer, binding.toolbar)
fullscreenHelper.hideSystemUI()
}
private fun initializeViewModel() {
val args = MediaIntentFactory.requireArguments(requireArguments())
viewModel.setShowThread(args.showThread)
val sorting = MediaDatabase.Sorting.values()[args.sorting]
viewModel.fetchAttachments(args.initialMediaUri, args.threadId, sorting)
}
private fun bindCurrentState(currentState: MediaPreviewV2State) {
when (currentState.loadState) {
MediaPreviewV2State.LoadState.READY -> bindReadyState(currentState)
// INIT, else -> no-op
}
}
private fun bindReadyState(currentState: MediaPreviewV2State) {
(binding.mediaPager.adapter as MediaPreviewV2Adapter).updateBackingItems(currentState.mediaRecords.mapNotNull { it.attachment })
val currentItem: MediaDatabase.MediaRecord = currentState.mediaRecords[currentState.position]
binding.toolbar.title = getTitleText(currentItem, currentState.showThread)
binding.toolbar.subtitle = getSubTitleText(currentItem)
}
private fun getTitleText(mediaRecord: MediaDatabase.MediaRecord, showThread: Boolean): String {
val recipient: Recipient = Recipient.live(mediaRecord.recipientId).get()
val defaultFromString: String = if (mediaRecord.isOutgoing) {
getString(R.string.MediaPreviewActivity_you)
} else {
recipient.getDisplayName(requireContext())
}
if (!showThread) {
return defaultFromString
}
val threadRecipient = Recipient.live(mediaRecord.threadRecipientId).get()
return if (mediaRecord.isOutgoing) {
if (threadRecipient.isSelf) {
getString(R.string.note_to_self)
} else {
getString(R.string.MediaPreviewActivity_you_to_s, threadRecipient.getDisplayName(requireContext()))
}
} else {
if (threadRecipient.isGroup) {
getString(R.string.MediaPreviewActivity_s_to_s, defaultFromString, threadRecipient.getDisplayName(requireContext()))
} else {
getString(R.string.MediaPreviewActivity_s_to_you, defaultFromString)
}
}
}
private fun getSubTitleText(mediaRecord: MediaDatabase.MediaRecord): String =
if (mediaRecord.date > 0) {
DateUtils.getExtendedRelativeTimeSpanString(requireContext(), Locale.getDefault(), mediaRecord.date)
} else {
getString(R.string.MediaPreviewActivity_draft)
}
override fun singleTapOnMedia(): Boolean {
Log.d(TAG, "singleTapOnMedia()")
return true

View file

@ -1,10 +1,12 @@
package org.thoughtcrime.securesms.mediapreview
import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.database.MediaDatabase
data class MediaPreviewV2State(
val attachments: List<Attachment> = emptyList(),
val loadState: LoadState = LoadState.INIT
val mediaRecords: List<MediaDatabase.MediaRecord> = emptyList(),
val loadState: LoadState = LoadState.INIT,
val position: Int = 0,
val showThread: Boolean = false
) {
enum class LoadState { INIT, READY, }
}

View file

@ -19,8 +19,23 @@ class MediaPreviewV2ViewModel : ViewModel() {
val state: Flowable<MediaPreviewV2State> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
fun fetchAttachments(startingUri: Uri, threadId: Long, sorting: MediaDatabase.Sorting) {
disposables += store.update(repository.getAttachments(startingUri, threadId, sorting)) { attachments, oldState ->
oldState.copy(attachments = attachments, loadState = MediaPreviewV2State.LoadState.READY)
disposables += store.update(repository.getAttachments(startingUri, threadId, sorting)) { mediaRecords: List<MediaDatabase.MediaRecord>, oldState: MediaPreviewV2State ->
oldState.copy(
mediaRecords = mediaRecords,
loadState = MediaPreviewV2State.LoadState.READY
)
}
}
fun setShowThread(value: Boolean) {
store.update { oldState ->
oldState.copy(showThread = value)
}
}
fun setCurrentPage(position: Int) {
store.update { oldState ->
oldState.copy(position = position)
}
}

View file

@ -1,9 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="@color/core_grey_95"
android:theme="@style/TextSecure.MediaPreview">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/media_preview_bar_background"
app:elevation="0dp">
<View
android:id="@+id/toolbar_cutout_spacer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="gone" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:theme="?actionBarStyle"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/transparent" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/media_pager"
android:layout_width="match_parent"