Add toolbar to MediaPreviewV2 implementation.
This commit is contained in:
parent
79b3b9190a
commit
4f3910e3ae
7 changed files with 139 additions and 21 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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, }
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue