mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-30 02:09:20 +00:00
Improved media grid, making sub-sections by months
This commit is contained in:
parent
54a775e0fd
commit
d8f0338f7c
10 changed files with 64 additions and 16 deletions
|
|
@ -19,7 +19,9 @@
|
|||
*/
|
||||
package org.linphone.ui.main.chat.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.databinding.DataBindingUtil
|
||||
|
|
@ -30,16 +32,35 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import org.linphone.R
|
||||
import org.linphone.databinding.ChatDocumentContentListCellBinding
|
||||
import org.linphone.databinding.ChatMediaContentGridCellBinding
|
||||
import org.linphone.databinding.MeetingsListDecorationBinding
|
||||
import org.linphone.ui.main.chat.model.FileModel
|
||||
import org.linphone.utils.HeaderAdapter
|
||||
|
||||
class ConversationsFilesAdapter : ListAdapter<FileModel, RecyclerView.ViewHolder>(
|
||||
FilesDiffCallback()
|
||||
) {
|
||||
class ConversationsFilesAdapter :
|
||||
ListAdapter<FileModel, RecyclerView.ViewHolder>(
|
||||
FilesDiffCallback()
|
||||
),
|
||||
HeaderAdapter {
|
||||
companion object {
|
||||
const val MEDIA_FILE = 1
|
||||
const val DOCUMENT_FILE = 2
|
||||
}
|
||||
|
||||
override fun displayHeaderForPosition(position: Int): Boolean {
|
||||
if (position == 0) return true
|
||||
|
||||
val previous = getItem(position - 1)
|
||||
val item = getItem(position)
|
||||
return previous.month != item.month
|
||||
}
|
||||
|
||||
override fun getHeaderViewForPosition(context: Context, position: Int): View {
|
||||
val binding = MeetingsListDecorationBinding.inflate(LayoutInflater.from(context))
|
||||
val item = getItem(position)
|
||||
binding.header.text = item.month
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
MEDIA_FILE -> createMediaFileViewHolder(parent)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import org.linphone.ui.main.chat.viewmodel.ConversationDocumentsListViewModel
|
|||
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.FileUtils
|
||||
import org.linphone.utils.RecyclerViewHeaderDecoration
|
||||
|
||||
@UiThread
|
||||
class ConversationDocumentsListFragment : SlidingPaneChildFragment() {
|
||||
|
|
@ -92,6 +93,9 @@ class ConversationDocumentsListFragment : SlidingPaneChildFragment() {
|
|||
val chatRoom = sharedViewModel.displayedChatRoom
|
||||
viewModel.findChatRoom(chatRoom, localSipUri, remoteSipUri)
|
||||
|
||||
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
|
||||
binding.documentsList.addItemDecoration(headerItemDecoration)
|
||||
|
||||
binding.documentsList.setHasFixedSize(true)
|
||||
val layoutManager = LinearLayoutManager(requireContext())
|
||||
binding.documentsList.layoutManager = layoutManager
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import org.linphone.ui.main.chat.viewmodel.ConversationMediaListViewModel
|
|||
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.FileUtils
|
||||
import org.linphone.utils.RecyclerViewHeaderDecoration
|
||||
|
||||
@UiThread
|
||||
class ConversationMediaListFragment : SlidingPaneChildFragment() {
|
||||
|
|
@ -93,14 +94,29 @@ class ConversationMediaListFragment : SlidingPaneChildFragment() {
|
|||
val chatRoom = sharedViewModel.displayedChatRoom
|
||||
viewModel.findChatRoom(chatRoom, localSipUri, remoteSipUri)
|
||||
|
||||
val headerItemDecoration = RecyclerViewHeaderDecoration(requireContext(), adapter)
|
||||
binding.mediaList.addItemDecoration(headerItemDecoration)
|
||||
|
||||
binding.mediaList.setHasFixedSize(true)
|
||||
val layoutManager = object : GridLayoutManager(requireContext(), 4) {
|
||||
val spanCount = 4
|
||||
val layoutManager = object : GridLayoutManager(requireContext(), spanCount) {
|
||||
override fun checkLayoutParams(lp: RecyclerView.LayoutParams): Boolean {
|
||||
lp.width = width / spanCount
|
||||
lp.height = lp.width
|
||||
return true
|
||||
}
|
||||
}
|
||||
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
if (position < adapter.currentList.size - 1) {
|
||||
// Have last item of month takes all remaining horizontal space to have a "line break"
|
||||
if (adapter.displayHeaderForPosition(position + 1)) {
|
||||
return spanCount - (position % spanCount)
|
||||
}
|
||||
}
|
||||
return 1
|
||||
}
|
||||
}
|
||||
// This isn't supported by GridLayoutManager, it will crash
|
||||
// layoutManager.stackFromEnd = true
|
||||
binding.mediaList.layoutManager = layoutManager
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class FileModel @AnyThread constructor(
|
|||
val file: String,
|
||||
val fileName: String,
|
||||
val fileSize: Long,
|
||||
val fileCreationTimestamp: Long,
|
||||
private val fileCreationTimestamp: Long,
|
||||
private val isEncrypted: Boolean,
|
||||
val isWaitingToBeDownloaded: Boolean = false,
|
||||
private val onClicked: ((model: FileModel) -> Unit)? = null
|
||||
|
|
@ -67,6 +67,14 @@ class FileModel @AnyThread constructor(
|
|||
|
||||
val isAudio: Boolean
|
||||
|
||||
val month = TimestampUtils.month(fileCreationTimestamp)
|
||||
|
||||
val dateTime = TimestampUtils.toString(
|
||||
fileCreationTimestamp,
|
||||
shortDate = false,
|
||||
hideYear = false
|
||||
)
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
init {
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ class MessageModel @WorkerThread constructor(
|
|||
)
|
||||
|
||||
val fileSize = content.fileSize.toLong()
|
||||
val timestamp = -1L // TODO FIXME: use content.creationTimestamp
|
||||
val timestamp = content.creationTimestamp
|
||||
when (content.type) {
|
||||
"image", "video" -> {
|
||||
val fileModel = FileModel(
|
||||
|
|
@ -411,7 +411,7 @@ class MessageModel @WorkerThread constructor(
|
|||
allFilesDownloaded = false
|
||||
filesContentCount += 1
|
||||
val name = content.name ?: ""
|
||||
val timestamp = -1L // TODO FIXME: use content.creationTimestamp
|
||||
val timestamp = content.creationTimestamp
|
||||
if (name.isNotEmpty()) {
|
||||
val fileModel = if (isOutgoing && chatMessage.isFileTransferInProgress) {
|
||||
val path = content.filePath ?: ""
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class ConversationDocumentsListViewModel @UiThread constructor() : AbstractConve
|
|||
}
|
||||
val name = documentContent.name.orEmpty()
|
||||
val size = documentContent.size.toLong()
|
||||
val timestamp = -1L // TODO FIXME: use documentContent.creationTimestamp
|
||||
val timestamp = documentContent.creationTimestamp
|
||||
if (path.isNotEmpty() && name.isNotEmpty()) {
|
||||
val model = FileModel(path, name, size, timestamp, isEncrypted) {
|
||||
openDocumentEvent.postValue(Event(it))
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class ConversationMediaListViewModel @UiThread constructor() : AbstractConversat
|
|||
}
|
||||
val name = mediaContent.name.orEmpty()
|
||||
val size = mediaContent.size.toLong()
|
||||
val timestamp = -1L // TODO FIXME: use mediaContent.creationTimestamp
|
||||
val timestamp = mediaContent.creationTimestamp
|
||||
if (path.isNotEmpty() && name.isNotEmpty()) {
|
||||
val model = FileModel(path, name, size, timestamp, isEncrypted) {
|
||||
openMediaEvent.postValue(Event(it))
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class MediaListViewerFragment : GenericFragment() {
|
|||
val list = viewModel.mediaList.value.orEmpty()
|
||||
if (position >= 0 && position < list.size) {
|
||||
val model = list[position]
|
||||
viewModel.currentlyDisplayedFileName.value = model.fileName
|
||||
viewModel.currentlyDisplayedFileName.value = "${model.fileName}\n${model.dateTime}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp">
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/file_icon"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
coilBubbleGrid="@{model.file}"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
|
|
@ -57,16 +56,16 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/audio_file"
|
||||
android:layout_width="@dimen/chat_bubble_grid_image_size"
|
||||
android:layout_height="@dimen/chat_bubble_grid_image_size"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:padding="18dp"
|
||||
android:background="@drawable/shape_squircle_main2_200"
|
||||
android:src="@drawable/file_audio"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="@{model.isAudio ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:tint="?attr/color_main2_600" />
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue