Uploads: create extension

This commit is contained in:
Benoit Marty 2020-05-19 19:26:44 +02:00
parent 919225bdfd
commit f7de2f0f13
8 changed files with 133 additions and 20 deletions

View file

@ -256,7 +256,11 @@ fun shareMedia(context: Context, file: File, mediaMimeType: String?) {
sendIntent.type = mediaMimeType
sendIntent.putExtra(Intent.EXTRA_STREAM, mediaUri)
context.startActivity(sendIntent)
try {
context.startActivity(sendIntent)
} catch (activityNotFoundException: ActivityNotFoundException) {
context.toast(R.string.error_no_external_application_found)
}
}
}

View file

@ -17,11 +17,12 @@
package im.vector.riotx.features.roomprofile.uploads
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.message.MessageWithAttachmentContent
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class RoomUploadsAction : VectorViewModelAction {
data class Download(val event: Event) : RoomUploadsAction()
data class Share(val event: Event) : RoomUploadsAction()
data class Download(val event: Event, val messageContent: MessageWithAttachmentContent) : RoomUploadsAction()
data class Share(val event: Event, val messageContent: MessageWithAttachmentContent) : RoomUploadsAction()
object Retry : RoomUploadsAction()
object LoadMore : RoomUploadsAction()

View file

@ -18,14 +18,20 @@ package im.vector.riotx.features.roomprofile.uploads
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.net.toUri
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.extensions.exhaustive
import im.vector.riotx.core.intent.getMimeTypeFromUri
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.utils.saveMedia
import im.vector.riotx.core.utils.shareMedia
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.roomprofile.RoomProfileArgs
import kotlinx.android.synthetic.main.fragment_room_uploads.*
@ -58,7 +64,27 @@ class RoomUploadsFragment @Inject constructor(
setupToolbar(roomUploadsToolbar)
// Initialize your view, subscribe to viewModel...
viewModel.observeViewEvents {
when (it) {
is RoomUploadsViewEvents.FileReadyForSharing -> {
shareMedia(requireContext(), it.file, getMimeTypeFromUri(requireContext(), it.file.toUri()))
}
is RoomUploadsViewEvents.FileReadyForSaving -> {
val saved = saveMedia(
context = requireContext(),
file = it.file,
title = it.title,
mediaMimeType = getMimeTypeFromUri(requireContext(), it.file.toUri())
)
if (saved) {
Toast.makeText(requireContext(), R.string.media_file_added_to_gallery, Toast.LENGTH_LONG).show()
} else {
Toast.makeText(requireContext(), R.string.error_adding_media_file_to_gallery, Toast.LENGTH_LONG).show()
}
}
is RoomUploadsViewEvents.Failure -> showFailure(it.throwable)
}.exhaustive
}
}
/*

View file

@ -0,0 +1,27 @@
/*
* 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
import im.vector.riotx.core.platform.VectorViewEvents
import java.io.File
sealed class RoomUploadsViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : RoomUploadsViewEvents()
data class FileReadyForSharing(val file: File) : RoomUploadsViewEvents()
data class FileReadyForSaving(val file: File, val title: String) : RoomUploadsViewEvents()
}

View file

@ -30,20 +30,23 @@ 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.file.FileService
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
import im.vector.matrix.android.api.session.room.uploads.GetUploadsResult
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
import im.vector.matrix.android.internal.util.awaitCallback
import im.vector.matrix.rx.rx
import im.vector.matrix.rx.unwrap
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel
import kotlinx.coroutines.launch
import java.io.File
class RoomUploadsViewModel @AssistedInject constructor(
@Assisted initialState: RoomUploadsViewState,
private val session: Session
) : VectorViewModel<RoomUploadsViewState, RoomUploadsAction, EmptyViewEvents>(initialState) {
) : VectorViewModel<RoomUploadsViewState, RoomUploadsAction, RoomUploadsViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -123,10 +126,50 @@ class RoomUploadsViewModel @AssistedInject constructor(
override fun handle(action: RoomUploadsAction) {
when (action) {
is RoomUploadsAction.Download -> TODO()
is RoomUploadsAction.Share -> TODO()
is RoomUploadsAction.Download -> handleDownload(action)
is RoomUploadsAction.Share -> handleShare(action)
RoomUploadsAction.Retry -> handleLoadMore()
RoomUploadsAction.LoadMore -> handleLoadMore()
}.exhaustive
}
private fun handleShare(action: RoomUploadsAction.Share) {
viewModelScope.launch {
try {
val file = awaitCallback<File> {
session.downloadFile(
FileService.DownloadMode.FOR_EXTERNAL_SHARE,
action.event.eventId ?: "",
action.messageContent.body,
action.messageContent.getFileUrl(),
action.messageContent.encryptedFileInfo?.toElementToDecrypt(),
it
)
}
_viewEvents.post(RoomUploadsViewEvents.FileReadyForSharing(file))
} catch (failure: Throwable) {
_viewEvents.post(RoomUploadsViewEvents.Failure(failure))
}
}
}
private fun handleDownload(action: RoomUploadsAction.Download) {
viewModelScope.launch {
try {
val file = awaitCallback<File> {
session.downloadFile(
FileService.DownloadMode.FOR_EXTERNAL_SHARE,
action.event.eventId ?: "",
action.messageContent.body,
action.messageContent.getFileUrl(),
action.messageContent.encryptedFileInfo?.toElementToDecrypt(),
it)
}
_viewEvents.post(RoomUploadsViewEvents.FileReadyForSaving(file, action.messageContent.body))
} catch (failure: Throwable) {
_viewEvents.post(RoomUploadsViewEvents.Failure(failure))
}
}
}
}

View file

@ -21,6 +21,7 @@ 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.matrix.android.api.session.room.model.message.MessageWithAttachmentContent
import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
@ -54,7 +55,7 @@ class RoomUploadsFilesFragment @Inject constructor(
}
override fun onOpenClicked(event: Event) {
// TODO
TODO()
}
override fun onRetry() {
@ -65,12 +66,12 @@ class RoomUploadsFilesFragment @Inject constructor(
uploadsViewModel.handle(RoomUploadsAction.LoadMore)
}
override fun onDownloadClicked(event: Event) {
uploadsViewModel.handle(RoomUploadsAction.Download(event))
override fun onDownloadClicked(event: Event, messageWithAttachmentContent: MessageWithAttachmentContent) {
uploadsViewModel.handle(RoomUploadsAction.Download(event, messageWithAttachmentContent))
}
override fun onShareClicked(event: Event) {
uploadsViewModel.handle(RoomUploadsAction.Share(event))
override fun onShareClicked(event: Event, messageWithAttachmentContent: MessageWithAttachmentContent) {
uploadsViewModel.handle(RoomUploadsAction.Share(event, messageWithAttachmentContent))
}
override fun invalidate() = withState(uploadsViewModel) { state ->

View file

@ -22,7 +22,11 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import im.vector.matrix.android.api.session.events.model.Event
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.model.message.MessageWithAttachmentContent
import im.vector.riotx.R
import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.epoxy.errorWithRetryItem
import im.vector.riotx.core.epoxy.loadingItem
import im.vector.riotx.core.epoxy.noResultItem
@ -33,15 +37,16 @@ import javax.inject.Inject
class UploadsFileController @Inject constructor(
private val errorFormatter: ErrorFormatter,
private val stringProvider: StringProvider
private val stringProvider: StringProvider,
private val dateFormatter: VectorDateFormatter
) : TypedEpoxyController<RoomUploadsViewState>() {
interface Listener {
fun onRetry()
fun loadMore()
fun onOpenClicked(event: Event)
fun onDownloadClicked(event: Event)
fun onShareClicked(event: Event)
fun onDownloadClicked(event: Event, messageWithAttachmentContent: MessageWithAttachmentContent)
fun onShareClicked(event: Event, messageWithAttachmentContent: MessageWithAttachmentContent)
}
var listener: Listener? = null
@ -100,21 +105,25 @@ class UploadsFileController @Inject constructor(
private fun buildFileItems(fileEvents: List<Event>) {
fileEvents.forEach {
val messageContent = it.getClearContent()?.toModel<MessageContent>() ?: return@forEach
val messageWithAttachmentContent = (messageContent as? MessageWithAttachmentContent) ?: return@forEach
uploadsFileItem {
id(it.eventId ?: "")
title(it.getClearType())
subtitle(it.getSenderKey())
title(messageWithAttachmentContent.body)
// TODO Resolve user displayName
subtitle(stringProvider.getString(R.string.uploads_files_subtitle, it.senderId, dateFormatter.formatRelativeDateTime(it.originServerTs)))
listener(object : UploadsFileItem.Listener {
override fun onItemClicked() {
listener?.onOpenClicked(it)
}
override fun onDownloadClicked() {
listener?.onDownloadClicked(it)
listener?.onDownloadClicked(it, messageWithAttachmentContent)
}
override fun onShareClicked() {
listener?.onShareClicked(it)
listener?.onShareClicked(it, messageWithAttachmentContent)
}
})
}

View file

@ -1791,6 +1791,8 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
<string name="uploads_media_title">MEDIA</string>
<string name="uploads_media_no_result">There is no media in this room</string>
<string name="uploads_files_title">FILES</string>
<!-- First parameter is a username and second is a date Example: "Matthew at 12:00 on 01/01/01" -->
<string name="uploads_files_subtitle">%1$s at %2$s</string>
<string name="uploads_files_no_result">There is no files in this room</string>
<string name="report_content_spam">"It's spam"</string>