Uploads: add screen - WIP
This commit is contained in:
parent
0992e76800
commit
e9ca876444
@ -220,3 +220,20 @@ fun Event.isImageMessage(): Boolean {
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.isVideoMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE
|
||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||
MessageType.MSGTYPE_VIDEO -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun Event.isPreviewableMessage(): Boolean {
|
||||
return getClearType() == EventType.MESSAGE
|
||||
&& when (getClearContent()?.toModel<MessageContent>()?.msgType) {
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_VIDEO -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.room.uploads
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
|
||||
data class GetUploadsResult(
|
||||
// List of fetched Events
|
||||
// List of fetched Events, most recent first
|
||||
val events: List<Event>,
|
||||
// token to get more events, or null if there is no more event to fetch
|
||||
val nextToken: String?
|
||||
|
@ -38,7 +38,9 @@ internal class DefaultUploadsService @AssistedInject constructor(
|
||||
|
||||
override fun getUploads(numberOfEvents: Int, since: String?, callback: MatrixCallback<GetUploadsResult>): Cancelable {
|
||||
return getUploadsTask
|
||||
.configureWith(GetUploadsTask.Params(roomId, numberOfEvents, since))
|
||||
.configureWith(GetUploadsTask.Params(roomId, numberOfEvents, since)) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ internal class DefaultGetUploadsTask @Inject constructor(
|
||||
}
|
||||
|
||||
return GetUploadsResult(
|
||||
events = chunk.events, // reverse?
|
||||
events = chunk.events,
|
||||
nextToken = chunk.end?.takeIf { it != chunk.start }
|
||||
)
|
||||
}
|
||||
|
@ -77,8 +77,8 @@ import im.vector.riotx.features.roomprofile.RoomProfileFragment
|
||||
import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
|
||||
import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.child.RoomUploadsFilesFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.child.RoomUploadsMediaFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.files.RoomUploadsFilesFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.media.RoomUploadsMediaFragment
|
||||
import im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment
|
||||
import im.vector.riotx.features.settings.VectorSettingsHelpAboutFragment
|
||||
import im.vector.riotx.features.settings.VectorSettingsLabsFragment
|
||||
|
@ -14,20 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.child
|
||||
package im.vector.riotx.core.epoxy
|
||||
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A placeholder fragment containing a simple view.
|
||||
*/
|
||||
class RoomUploadsFilesFragment @Inject constructor() : VectorBaseFragment() {
|
||||
@EpoxyModelClass(layout = R.layout.item_loading_square)
|
||||
abstract class SquareLoadingItem : VectorEpoxyModel<SquareLoadingItem.Holder>() {
|
||||
|
||||
private val uploadsViewModel by parentFragmentViewModel(RoomUploadsViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||
class Holder : VectorEpoxyHolder()
|
||||
}
|
@ -36,4 +36,8 @@ class DimensionConverter @Inject constructor(val resources: Resources) {
|
||||
resources.displayMetrics
|
||||
).toInt()
|
||||
}
|
||||
|
||||
fun pdToDp(px: Int): Int {
|
||||
return (px.toFloat() / resources.displayMetrics.density).toInt()
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.Spannable
|
||||
@ -30,12 +29,10 @@ import android.view.HapticFeedbackConstants
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.buildSpannedString
|
||||
@ -150,7 +147,6 @@ import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import im.vector.riotx.features.html.PillImageSpan
|
||||
import im.vector.riotx.features.invite.VectorInviteView
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
import im.vector.riotx.features.media.ImageMediaViewerActivity
|
||||
import im.vector.riotx.features.media.VideoContentRenderer
|
||||
import im.vector.riotx.features.media.VideoMediaViewerActivity
|
||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||
@ -998,31 +994,14 @@ class RoomDetailFragment @Inject constructor(
|
||||
}
|
||||
|
||||
override fun onImageMessageClicked(messageImageContent: MessageImageInfoContent, mediaData: ImageContentRenderer.Data, view: View) {
|
||||
// TODO Use navigator
|
||||
|
||||
val intent = ImageMediaViewerActivity.newIntent(vectorBaseActivity, mediaData, ViewCompat.getTransitionName(view))
|
||||
val pairs = ArrayList<Pair<View, String>>()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
requireActivity().window.decorView.findViewById<View>(android.R.id.statusBarBackground)?.let {
|
||||
pairs.add(Pair(it, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME))
|
||||
}
|
||||
requireActivity().window.decorView.findViewById<View>(android.R.id.navigationBarBackground)?.let {
|
||||
pairs.add(Pair(it, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME))
|
||||
}
|
||||
navigator.openImageViewer(requireActivity(), mediaData, view) { pairs ->
|
||||
pairs.add(Pair(roomToolbar, ViewCompat.getTransitionName(roomToolbar) ?: ""))
|
||||
pairs.add(Pair(composerLayout, ViewCompat.getTransitionName(composerLayout) ?: ""))
|
||||
}
|
||||
pairs.add(Pair(view, ViewCompat.getTransitionName(view) ?: ""))
|
||||
pairs.add(Pair(roomToolbar, ViewCompat.getTransitionName(roomToolbar) ?: ""))
|
||||
pairs.add(Pair(composerLayout, ViewCompat.getTransitionName(composerLayout) ?: ""))
|
||||
|
||||
val bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||
requireActivity(), *pairs.toTypedArray()).toBundle()
|
||||
startActivity(intent, bundle)
|
||||
}
|
||||
|
||||
override fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View) {
|
||||
// TODO Use navigator
|
||||
val intent = VideoMediaViewerActivity.newIntent(vectorBaseActivity, mediaData)
|
||||
startActivity(intent)
|
||||
navigator.openVideoViewer(requireActivity(), mediaData)
|
||||
}
|
||||
|
||||
override fun onFileMessageClicked(eventId: String, messageFileContent: MessageFileContent) {
|
||||
|
@ -65,6 +65,17 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
||||
STICKER
|
||||
}
|
||||
|
||||
/**
|
||||
* For gallery
|
||||
*/
|
||||
fun render(data: Data, imageView: ImageView, size: Int) {
|
||||
// a11y
|
||||
imageView.contentDescription = data.filename
|
||||
|
||||
createGlideRequest(data, Mode.THUMBNAIL, imageView, Size(size, size))
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
fun render(data: Data, mode: Mode, imageView: ImageView) {
|
||||
val size = processSize(data, mode)
|
||||
imageView.layoutParams.width = size.width
|
||||
|
@ -19,9 +19,12 @@ package im.vector.riotx.features.navigation
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.app.TaskStackBuilder
|
||||
import androidx.core.util.Pair
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.matrix.android.api.session.crypto.verification.IncomingSasVerificationTransaction
|
||||
@ -45,6 +48,10 @@ import im.vector.riotx.features.home.room.detail.RoomDetailArgs
|
||||
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
|
||||
import im.vector.riotx.features.invite.InviteUsersToRoomActivity
|
||||
import im.vector.riotx.features.media.BigImageViewerActivity
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
import im.vector.riotx.features.media.ImageMediaViewerActivity
|
||||
import im.vector.riotx.features.media.VideoContentRenderer
|
||||
import im.vector.riotx.features.media.VideoMediaViewerActivity
|
||||
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
|
||||
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
|
||||
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewActivity
|
||||
@ -215,6 +222,29 @@ class DefaultNavigator @Inject constructor(
|
||||
fragment.startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
override fun openImageViewer(activity: Activity, mediaData: ImageContentRenderer.Data, view: View, options: ((MutableList<Pair<View, String>>) -> Unit)?) {
|
||||
val intent = ImageMediaViewerActivity.newIntent(activity, mediaData, ViewCompat.getTransitionName(view))
|
||||
val pairs = ArrayList<Pair<View, String>>()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
activity.window.decorView.findViewById<View>(android.R.id.statusBarBackground)?.let {
|
||||
pairs.add(Pair(it, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME))
|
||||
}
|
||||
activity.window.decorView.findViewById<View>(android.R.id.navigationBarBackground)?.let {
|
||||
pairs.add(Pair(it, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME))
|
||||
}
|
||||
}
|
||||
pairs.add(Pair(view, ViewCompat.getTransitionName(view) ?: ""))
|
||||
options?.invoke(pairs)
|
||||
|
||||
val bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, *pairs.toTypedArray()).toBundle()
|
||||
activity.startActivity(intent, bundle)
|
||||
}
|
||||
|
||||
override fun openVideoViewer(activity: Activity, mediaData: VideoContentRenderer.Data) {
|
||||
val intent = VideoMediaViewerActivity.newIntent(activity, mediaData)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
private fun startActivity(context: Context, intent: Intent, buildTask: Boolean) {
|
||||
if (buildTask) {
|
||||
val stackBuilder = TaskStackBuilder.create(context)
|
||||
|
@ -19,10 +19,13 @@ package im.vector.riotx.features.navigation
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.core.util.Pair
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||
import im.vector.matrix.android.api.session.terms.TermsService
|
||||
import im.vector.matrix.android.api.util.MatrixItem
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
import im.vector.riotx.features.media.VideoContentRenderer
|
||||
import im.vector.riotx.features.settings.VectorSettingsActivity
|
||||
import im.vector.riotx.features.share.SharedData
|
||||
import im.vector.riotx.features.terms.ReviewTermsActivity
|
||||
@ -76,4 +79,8 @@ interface Navigator {
|
||||
baseUrl: String,
|
||||
token: String?,
|
||||
requestCode: Int = ReviewTermsActivity.TERMS_REQUEST_CODE)
|
||||
|
||||
fun openImageViewer(activity: Activity, mediaData: ImageContentRenderer.Data, view: View, options: ((MutableList<Pair<View, String>>) -> Unit)?)
|
||||
|
||||
fun openVideoViewer(activity: Activity, mediaData: VideoContentRenderer.Data)
|
||||
}
|
||||
|
@ -16,6 +16,12 @@
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class RoomUploadsAction : VectorViewModelAction
|
||||
sealed class RoomUploadsAction : VectorViewModelAction {
|
||||
data class Download(val event: Event) : RoomUploadsAction()
|
||||
data class Share(val event: Event) : RoomUploadsAction()
|
||||
|
||||
object Retry : RoomUploadsAction()
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import android.view.View
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import im.vector.matrix.android.api.util.toMatrixItem
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
@ -45,11 +46,17 @@ class RoomUploadsFragment @Inject constructor(
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val sectionsPagerAdapter = RoomUploadsPagerAdapter(childFragmentManager, stringProvider)
|
||||
view_pager.adapter = sectionsPagerAdapter
|
||||
tabs.setupWithViewPager(view_pager)
|
||||
val sectionsPagerAdapter = RoomUploadsPagerAdapter(this)
|
||||
roomUploadsViewPager.adapter = sectionsPagerAdapter
|
||||
|
||||
setupToolbar(matrixProfileToolbar)
|
||||
TabLayoutMediator(roomUploadsTabs, roomUploadsViewPager) { tab, position ->
|
||||
when (position) {
|
||||
0 -> tab.text = stringProvider.getString(R.string.uploads_media_title)
|
||||
1 -> tab.text = stringProvider.getString(R.string.uploads_files_title)
|
||||
}
|
||||
}.attach()
|
||||
|
||||
setupToolbar(roomUploadsToolbar)
|
||||
|
||||
// Initialize your view, subscribe to viewModel...
|
||||
}
|
||||
@ -68,8 +75,8 @@ class RoomUploadsFragment @Inject constructor(
|
||||
|
||||
private fun renderRoomSummary(state: RoomUploadsViewState) {
|
||||
state.roomSummary()?.let {
|
||||
matrixProfileToolbarTitleView.text = it.displayName
|
||||
avatarRenderer.render(it.toMatrixItem(), matrixProfileToolbarAvatarImageView)
|
||||
roomUploadsToolbarTitleView.text = it.displayName
|
||||
avatarRenderer.render(it.toMatrixItem(), roomUploadsToolbarAvatarImageView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,43 +17,21 @@
|
||||
package im.vector.riotx.features.roomprofile.uploads
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.features.roomprofile.uploads.child.RoomUploadsFilesFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.child.RoomUploadsMediaFragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import im.vector.riotx.features.roomprofile.uploads.files.RoomUploadsFilesFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.media.RoomUploadsMediaFragment
|
||||
|
||||
private val TAB_TITLES = arrayOf(
|
||||
R.string.uploads_title_media,
|
||||
R.string.uploads_title_files
|
||||
)
|
||||
|
||||
/**
|
||||
* A [FragmentPagerAdapter] that returns a fragment corresponding to
|
||||
* one of the sections/tabs/pages.
|
||||
*/
|
||||
class RoomUploadsPagerAdapter(
|
||||
fm: FragmentManager,
|
||||
private val stringProvider: StringProvider
|
||||
) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
private val fragment: Fragment
|
||||
) : FragmentStateAdapter(fragment) {
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
// getItem is called to instantiate the fragment for the given page.
|
||||
// Return a PlaceholderFragment (defined as a static inner class below).
|
||||
override fun getItemCount() = 2
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return if (position == 0) {
|
||||
RoomUploadsMediaFragment()
|
||||
fragment.childFragmentManager.fragmentFactory.instantiate(fragment.requireContext().classLoader, RoomUploadsMediaFragment::class.java.name)
|
||||
} else {
|
||||
RoomUploadsFilesFragment()
|
||||
fragment.childFragmentManager.fragmentFactory.instantiate(fragment.requireContext().classLoader, RoomUploadsFilesFragment::class.java.name)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? {
|
||||
return stringProvider.getString(TAB_TITLES[position])
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
// Show 2 total pages.
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,10 @@ import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.isPreviewableMessage
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.uploads.GetUploadsResult
|
||||
import im.vector.matrix.android.internal.util.awaitCallback
|
||||
import im.vector.matrix.rx.rx
|
||||
@ -90,10 +94,15 @@ class RoomUploadsViewModel @AssistedInject constructor(
|
||||
|
||||
token = result.nextToken
|
||||
|
||||
val groupedEvents = result.events
|
||||
.filter { it.getClearType() == EventType.MESSAGE && it.getClearContent()?.toModel<MessageContent>() != null }
|
||||
.groupBy { it.isPreviewableMessage() }
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncEventsRequest = Uninitialized,
|
||||
events = this.events + result.events,
|
||||
mediaEvents = this.mediaEvents + groupedEvents[true].orEmpty(),
|
||||
fileEvents = this.fileEvents + groupedEvents[false].orEmpty(),
|
||||
hasMore = result.nextToken != null
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.files
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.cleanup
|
||||
import im.vector.riotx.core.extensions.configureWith
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsAction
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsViewModel
|
||||
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomUploadsFilesFragment @Inject constructor(
|
||||
private val controller: UploadsFileController
|
||||
) : VectorBaseFragment(), UploadsFileController.Listener {
|
||||
|
||||
private val uploadsViewModel by parentFragmentViewModel(RoomUploadsViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
recyclerView.configureWith(controller, showDivider = true)
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
recyclerView.cleanup()
|
||||
controller.listener = null
|
||||
}
|
||||
|
||||
override fun onOpenClicked(event: Event) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
override fun onRetry() {
|
||||
uploadsViewModel.handle(RoomUploadsAction.Retry)
|
||||
}
|
||||
|
||||
override fun onDownloadClicked(event: Event) {
|
||||
uploadsViewModel.handle(RoomUploadsAction.Download(event))
|
||||
}
|
||||
|
||||
override fun onShareClicked(event: Event) {
|
||||
uploadsViewModel.handle(RoomUploadsAction.Share(event))
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(uploadsViewModel) { state ->
|
||||
controller.setData(state)
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.files
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.errorWithRetryItem
|
||||
import im.vector.riotx.core.epoxy.loadingItem
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsViewState
|
||||
import javax.inject.Inject
|
||||
|
||||
class UploadsFileController @Inject constructor(
|
||||
private val errorFormatter: ErrorFormatter,
|
||||
colorProvider: ColorProvider
|
||||
) : TypedEpoxyController<RoomUploadsViewState>() {
|
||||
|
||||
interface Listener {
|
||||
fun onRetry()
|
||||
fun onOpenClicked(event: Event)
|
||||
fun onDownloadClicked(event: Event)
|
||||
fun onShareClicked(event: Event)
|
||||
}
|
||||
|
||||
private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
init {
|
||||
setData(null)
|
||||
}
|
||||
|
||||
override fun buildModels(data: RoomUploadsViewState?) {
|
||||
data ?: return
|
||||
|
||||
if (data.fileEvents.isEmpty()) {
|
||||
when (data.asyncEventsRequest) {
|
||||
is Loading -> {
|
||||
loadingItem {
|
||||
id("loading")
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
text(errorFormatter.toHumanReadable(data.asyncEventsRequest.error))
|
||||
listener { listener?.onRetry() }
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buildFileItems(data.fileEvents)
|
||||
|
||||
if (data.hasMore) {
|
||||
loadingItem {
|
||||
id("loadMore")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildFileItems(fileEvents: List<Event>) {
|
||||
fileEvents.forEach {
|
||||
uploadsFileItem {
|
||||
id(it.eventId ?: "")
|
||||
title(it.getClearType())
|
||||
subtitle(it.getSenderKey())
|
||||
listener(object : UploadsFileItem.Listener {
|
||||
override fun onItemClicked() {
|
||||
listener?.onOpenClicked(it)
|
||||
}
|
||||
|
||||
override fun onDownloadClicked() {
|
||||
listener?.onDownloadClicked(it)
|
||||
}
|
||||
|
||||
override fun onShareClicked() {
|
||||
listener?.onShareClicked(it)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.files
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotx.core.extensions.setTextOrHide
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_uploads_file)
|
||||
abstract class UploadsFileItem : VectorEpoxyModel<UploadsFileItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute var title: String? = null
|
||||
@EpoxyAttribute var subtitle: String? = null
|
||||
|
||||
@EpoxyAttribute var listener: Listener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.view.setOnClickListener { listener?.onItemClicked() }
|
||||
holder.titleView.text = title
|
||||
holder.subtitleView.setTextOrHide(subtitle)
|
||||
holder.downloadView.setOnClickListener { listener?.onDownloadClicked() }
|
||||
holder.shareView.setOnClickListener { listener?.onShareClicked() }
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val titleView by bind<TextView>(R.id.uploadsFileTitle)
|
||||
val subtitleView by bind<TextView>(R.id.uploadsFileSubtitle)
|
||||
val downloadView by bind<View>(R.id.uploadsFileActionDownload)
|
||||
val shareView by bind<View>(R.id.uploadsFileActionShare)
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onItemClicked()
|
||||
fun onDownloadClicked()
|
||||
fun onShareClicked()
|
||||
}
|
||||
}
|
@ -14,20 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.child
|
||||
package im.vector.riotx.features.roomprofile.uploads.media
|
||||
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A placeholder fragment containing a simple view.
|
||||
*/
|
||||
class RoomUploadsMediaFragment @Inject constructor() : VectorBaseFragment() {
|
||||
|
||||
private val uploadsViewModel by parentFragmentViewModel(RoomUploadsViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||
}
|
||||
// Min image size. Size will be adjusted at runtime
|
||||
const val IMAGE_SIZE_DP = 120
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.media
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.extensions.cleanup
|
||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||
import im.vector.riotx.core.utils.DimensionConverter
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
import im.vector.riotx.features.media.VideoContentRenderer
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsAction
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsViewModel
|
||||
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomUploadsMediaFragment @Inject constructor(
|
||||
private val controller: UploadsMediaController,
|
||||
private val dimensionConverter: DimensionConverter
|
||||
) : VectorBaseFragment(), UploadsMediaController.Listener {
|
||||
|
||||
private val uploadsViewModel by parentFragmentViewModel(RoomUploadsViewModel::class)
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
recyclerView.layoutManager = GridLayoutManager(context, getNumberOfColumns())
|
||||
recyclerView.adapter = controller.adapter
|
||||
recyclerView.setHasFixedSize(true)
|
||||
|
||||
controller.listener = this
|
||||
}
|
||||
|
||||
private fun getNumberOfColumns(): Int {
|
||||
val displayMetrics = DisplayMetrics()
|
||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
return dimensionConverter.pdToDp(displayMetrics.widthPixels) / IMAGE_SIZE_DP
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
recyclerView.cleanup()
|
||||
controller.listener = null
|
||||
}
|
||||
|
||||
override fun onOpenImageClicked(view: View, mediaData: ImageContentRenderer.Data) {
|
||||
navigator.openImageViewer(requireActivity(), mediaData, view, null)
|
||||
}
|
||||
|
||||
override fun onOpenVideoClicked(view: View, mediaData: VideoContentRenderer.Data) {
|
||||
navigator.openVideoViewer(requireActivity(), mediaData)
|
||||
}
|
||||
|
||||
override fun onRetry() {
|
||||
uploadsViewModel.handle(RoomUploadsAction.Retry)
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(uploadsViewModel) { state ->
|
||||
controller.setData(state)
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.media
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_uploads_image)
|
||||
abstract class UploadsImageItem : VectorEpoxyModel<UploadsImageItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute lateinit var imageContentRenderer: ImageContentRenderer
|
||||
@EpoxyAttribute lateinit var data: ImageContentRenderer.Data
|
||||
|
||||
@EpoxyAttribute var listener: Listener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.view.setOnClickListener { listener?.onItemClicked(holder.imageView, data) }
|
||||
imageContentRenderer.render(data, holder.imageView, IMAGE_SIZE_DP)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val imageView by bind<ImageView>(R.id.uploadsImagePreview)
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onItemClicked(view: View, data: ImageContentRenderer.Data)
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.media
|
||||
|
||||
import android.view.View
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.isImageMessage
|
||||
import im.vector.matrix.android.api.session.events.model.isVideoMessage
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageImageInfoContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
|
||||
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.errorWithRetryItem
|
||||
import im.vector.riotx.core.epoxy.squareLoadingItem
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import im.vector.riotx.core.resources.ColorProvider
|
||||
import im.vector.riotx.core.utils.DimensionConverter
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
import im.vector.riotx.features.media.VideoContentRenderer
|
||||
import im.vector.riotx.features.roomprofile.uploads.RoomUploadsViewState
|
||||
import javax.inject.Inject
|
||||
|
||||
class UploadsMediaController @Inject constructor(
|
||||
private val errorFormatter: ErrorFormatter,
|
||||
private val imageContentRenderer: ImageContentRenderer,
|
||||
private val dimensionConverter: DimensionConverter,
|
||||
colorProvider: ColorProvider
|
||||
) : TypedEpoxyController<RoomUploadsViewState>() {
|
||||
|
||||
interface Listener {
|
||||
fun onRetry()
|
||||
fun onOpenImageClicked(view: View, mediaData: ImageContentRenderer.Data)
|
||||
fun onOpenVideoClicked(view: View, mediaData: VideoContentRenderer.Data)
|
||||
}
|
||||
|
||||
private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
private val itemSize = dimensionConverter.dpToPx(64)
|
||||
|
||||
init {
|
||||
setData(null)
|
||||
}
|
||||
|
||||
override fun buildModels(data: RoomUploadsViewState?) {
|
||||
data ?: return
|
||||
|
||||
if (data.mediaEvents.isEmpty()) {
|
||||
when (data.asyncEventsRequest) {
|
||||
is Loading -> {
|
||||
squareLoadingItem {
|
||||
id("loading")
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
text(errorFormatter.toHumanReadable(data.asyncEventsRequest.error))
|
||||
listener { listener?.onRetry() }
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buildMediaItems(data.mediaEvents)
|
||||
|
||||
if (data.hasMore) {
|
||||
squareLoadingItem {
|
||||
id("loadMore")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildMediaItems(mediaEvents: List<Event>) {
|
||||
mediaEvents.forEach { event ->
|
||||
when {
|
||||
event.isImageMessage() -> {
|
||||
val data = event.toImageContentRendererData() ?: return@forEach
|
||||
uploadsImageItem {
|
||||
id(event.eventId ?: "")
|
||||
imageContentRenderer(imageContentRenderer)
|
||||
data(data)
|
||||
listener(object : UploadsImageItem.Listener {
|
||||
override fun onItemClicked(view: View, data: ImageContentRenderer.Data) {
|
||||
listener?.onOpenImageClicked(view, data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
event.isVideoMessage() -> {
|
||||
val data = event.toVideoContentRendererData() ?: return@forEach
|
||||
uploadsVideoItem {
|
||||
id(event.eventId ?: "")
|
||||
imageContentRenderer(imageContentRenderer)
|
||||
data(data)
|
||||
listener(object : UploadsVideoItem.Listener {
|
||||
override fun onItemClicked(view: View, data: VideoContentRenderer.Data) {
|
||||
listener?.onOpenVideoClicked(view, data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Event.toImageContentRendererData(): ImageContentRenderer.Data? {
|
||||
val messageContent = getClearContent()?.toModel<MessageImageContent>() ?: return null
|
||||
|
||||
return ImageContentRenderer.Data(
|
||||
eventId = eventId ?: "",
|
||||
filename = messageContent.body,
|
||||
url = messageContent.getFileUrl(),
|
||||
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
|
||||
height = messageContent.info?.height,
|
||||
maxHeight = itemSize,
|
||||
width = messageContent.info?.width,
|
||||
maxWidth = itemSize
|
||||
)
|
||||
}
|
||||
|
||||
private fun Event.toVideoContentRendererData(): VideoContentRenderer.Data? {
|
||||
val messageContent = getClearContent()?.toModel<MessageVideoContent>() ?: return null
|
||||
|
||||
val thumbnailData = ImageContentRenderer.Data(
|
||||
eventId = eventId ?: "",
|
||||
filename = messageContent.body,
|
||||
url = messageContent.videoInfo?.thumbnailFile?.url ?: messageContent.videoInfo?.thumbnailUrl,
|
||||
elementToDecrypt = messageContent.videoInfo?.thumbnailFile?.toElementToDecrypt(),
|
||||
height = messageContent.videoInfo?.height,
|
||||
maxHeight = itemSize,
|
||||
width = messageContent.videoInfo?.width,
|
||||
maxWidth = itemSize
|
||||
)
|
||||
|
||||
return VideoContentRenderer.Data(
|
||||
eventId = eventId ?: "",
|
||||
filename = messageContent.body,
|
||||
url = messageContent.getFileUrl(),
|
||||
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
|
||||
thumbnailMediaData = thumbnailData
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.roomprofile.uploads.media
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.riotx.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotx.features.media.ImageContentRenderer
|
||||
import im.vector.riotx.features.media.VideoContentRenderer
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_uploads_video)
|
||||
abstract class UploadsVideoItem : VectorEpoxyModel<UploadsVideoItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute lateinit var imageContentRenderer: ImageContentRenderer
|
||||
@EpoxyAttribute lateinit var data: VideoContentRenderer.Data
|
||||
|
||||
@EpoxyAttribute var listener: Listener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.view.setOnClickListener { listener?.onItemClicked(holder.imageView, data) }
|
||||
imageContentRenderer.render(data.thumbnailMediaData, holder.imageView, IMAGE_SIZE_DP)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val imageView by bind<ImageView>(R.id.uploadsVideoPreview)
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onItemClicked(view: View, data: VideoContentRenderer.Data)
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
android:elevation="4dp">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/matrixProfileToolbar"
|
||||
android:id="@+id/roomUploadsToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
@ -20,12 +20,12 @@
|
||||
app:layout_collapseMode="pin">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/matrixProfileToolbarContainer"
|
||||
android:id="@+id/roomUploadsToolbarContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/matrixProfileToolbarAvatarImageView"
|
||||
android:id="@+id/roomUploadsToolbarAvatarImageView"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="8dp"
|
||||
@ -36,17 +36,17 @@
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/matrixProfileDecorationToolbarAvatarImageView"
|
||||
android:id="@+id/roomUploadsDecorationToolbarAvatarImageView"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
app:layout_constraintCircle="@+id/matrixProfileToolbarAvatarImageView"
|
||||
app:layout_constraintCircle="@+id/roomUploadsToolbarAvatarImageView"
|
||||
app:layout_constraintCircleAngle="135"
|
||||
app:layout_constraintCircleRadius="20dp"
|
||||
tools:ignore="MissingConstraints"
|
||||
tools:src="@drawable/ic_shield_trusted" />
|
||||
|
||||
<im.vector.riotx.core.platform.EllipsizingTextView
|
||||
android:id="@+id/matrixProfileToolbarTitleView"
|
||||
android:id="@+id/roomUploadsToolbarTitleView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
@ -57,7 +57,7 @@
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/matrixProfileToolbarAvatarImageView"
|
||||
app:layout_constraintStart_toEndOf="@+id/roomUploadsToolbarAvatarImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@sample/matrix.json/data/roomName" />
|
||||
|
||||
@ -66,18 +66,18 @@
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:id="@+id/roomUploadsTabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/matrixProfileToolbarAvatarImageView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/roomUploadsToolbarAvatarImageView"
|
||||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/view_pager"
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/roomUploadsViewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
23
vector/src/main/res/layout/item_loading_square.xml
Normal file
23
vector/src/main/res/layout/item_loading_square.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
75
vector/src/main/res/layout/item_uploads_file.xml
Normal file
75
vector/src/main/res/layout/item_uploads_file.xml
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:minHeight="64dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/uploadsFileIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:src="@drawable/ic_file"
|
||||
android:tint="?riotx_text_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<im.vector.riotx.core.platform.EllipsizingTextView
|
||||
android:id="@+id/uploadsFileTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/uploadsFileSubtitle"
|
||||
app:layout_constraintEnd_toStartOf="@+id/uploadsFileActionDownload"
|
||||
app:layout_constraintStart_toEndOf="@+id/uploadsFileIcon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Filename.file" />
|
||||
|
||||
<im.vector.riotx.core.platform.EllipsizingTextView
|
||||
android:id="@+id/uploadsFileSubtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:ellipsize="end"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/uploadsFileTitle"
|
||||
app:layout_constraintStart_toStartOf="@+id/uploadsFileTitle"
|
||||
app:layout_constraintTop_toBottomOf="@+id/uploadsFileTitle"
|
||||
tools:text="Username at 12:00 on 01/01/01" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/uploadsFileActionDownload"
|
||||
android:layout_width="@dimen/layout_touch_size"
|
||||
android:layout_height="@dimen/layout_touch_size"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_download"
|
||||
android:tint="?colorAccent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/uploadsFileActionShare"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/uploadsFileActionShare"
|
||||
android:layout_width="@dimen/layout_touch_size"
|
||||
android:layout_height="@dimen/layout_touch_size"
|
||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_material_share"
|
||||
android:tint="?colorAccent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
22
vector/src/main/res/layout/item_uploads_image.xml
Normal file
22
vector/src/main/res/layout/item_uploads_image.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/uploadsImagePreview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="2dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@tools:sample/backgrounds/scenic" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
31
vector/src/main/res/layout/item_uploads_video.xml
Normal file
31
vector/src/main/res/layout/item_uploads_video.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/uploadsVideoPreview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="2dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@tools:sample/backgrounds/scenic" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_material_play_circle"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
x
Reference in New Issue
Block a user