Add menu action to download image and video on *ViewerActivity

This commit is contained in:
ganfra 2019-08-07 19:29:41 +02:00
parent 7fef063e15
commit 4a754166df
8 changed files with 183 additions and 6 deletions

View File

@ -208,6 +208,7 @@ class MessageItemFactory @Inject constructor(


val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize() val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
val data = ImageContentRenderer.Data( val data = ImageContentRenderer.Data(
eventId = informationData.eventId,
filename = messageContent.body, filename = messageContent.body,
url = messageContent.getFileUrl(), url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(), elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
@ -251,6 +252,7 @@ class MessageItemFactory @Inject constructor(


val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize() val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
val thumbnailData = ImageContentRenderer.Data( val thumbnailData = ImageContentRenderer.Data(
eventId = informationData.eventId,
filename = messageContent.body, filename = messageContent.body,
url = messageContent.videoInfo?.thumbnailFile?.url url = messageContent.videoInfo?.thumbnailFile?.url
?: messageContent.videoInfo?.thumbnailUrl, ?: messageContent.videoInfo?.thumbnailUrl,

View File

@ -42,6 +42,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:


@Parcelize @Parcelize
data class Data( data class Data(
val eventId: String,
val filename: String, val filename: String,
val url: String?, val url: String?,
val elementToDecrypt: ElementToDecrypt?, val elementToDecrypt: ElementToDecrypt?,

View File

@ -21,6 +21,8 @@ import android.content.Intent
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
@ -36,6 +38,7 @@ import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.target.Target
import com.github.piasy.biv.indicator.progresspie.ProgressPieIndicator import com.github.piasy.biv.indicator.progresspie.ProgressPieIndicator
import com.github.piasy.biv.view.GlideImageViewFactory import com.github.piasy.biv.view.GlideImageViewFactory
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.glide.GlideApp
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
@ -47,17 +50,20 @@ import javax.inject.Inject
class ImageMediaViewerActivity : VectorBaseActivity() { class ImageMediaViewerActivity : VectorBaseActivity() {


@Inject lateinit var imageContentRenderer: ImageContentRenderer @Inject lateinit var imageContentRenderer: ImageContentRenderer
@Inject lateinit var mediaDownloadHelper: MediaDownloadHelper


lateinit var mediaData: ImageContentRenderer.Data lateinit var mediaData: ImageContentRenderer.Data


override fun getMenuRes() = R.menu.image_media_viewer

override fun injectWith(injector: ScreenComponent) { override fun injectWith(injector: ScreenComponent) {
injector.inject(this) injector.inject(this)
} }


override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(im.vector.riotx.R.layout.activity_image_media_viewer) setContentView(R.layout.activity_image_media_viewer)
mediaData = intent.getParcelableExtra<ImageContentRenderer.Data>(EXTRA_MEDIA_DATA) mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA)
intent.extras.getString(EXTRA_SHARED_TRANSITION_NAME)?.let { intent.extras.getString(EXTRA_SHARED_TRANSITION_NAME)?.let {
ViewCompat.setTransitionName(imageTransitionView, it) ViewCompat.setTransitionName(imageTransitionView, it)
} }
@ -105,6 +111,29 @@ class ImageMediaViewerActivity : VectorBaseActivity() {
} }
} }


override fun onPrepareOptionsMenu(menu: Menu): Boolean {
val downloadItem = menu.findItem(R.id.download_image)
downloadItem.isVisible = !mediaData.isLocalFile()
return super.onPrepareOptionsMenu(menu)
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.download_image -> mediaDownloadHelper.checkPermissionAndDownload(
mediaData.eventId,
mediaData.filename,
mediaData.url,
mediaData.elementToDecrypt
)
}
return super.onOptionsItemSelected(item)
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
mediaDownloadHelper.onRequestPermissionsResult(requestCode, permissions, grantResults)
}


private fun configureToolbar(toolbar: Toolbar, mediaData: ImageContentRenderer.Data) { private fun configureToolbar(toolbar: Toolbar, mediaData: ImageContentRenderer.Data) {
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.apply { supportActionBar?.apply {

View File

@ -0,0 +1,84 @@
/*

* Copyright 2019 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.media

import androidx.appcompat.app.AppCompatActivity
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.internal.crypto.attachments.ElementToDecrypt
import im.vector.riotx.R
import im.vector.riotx.core.error.ErrorFormatter
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.utils.*
import java.io.File
import javax.inject.Inject

class MediaDownloadHelper @Inject constructor(private val activity: AppCompatActivity,
private val session: Session,
private val stringProvider: StringProvider,
private val errorFormatter: ErrorFormatter) {

private data class PendingData(
val id: String,
val filename: String,
val url: String,
val elementToDecrypt: ElementToDecrypt?
)

private var pendingData: PendingData? = null

fun checkPermissionAndDownload(id: String, filename: String, url: String?, elementToDecrypt: ElementToDecrypt?) {
if (url.isNullOrEmpty()) {
activity.toast(stringProvider.getString(R.string.unexpected_error))
} else if (checkPermissions(PERMISSIONS_FOR_WRITING_FILES, activity, PERMISSION_REQUEST_CODE_DOWNLOAD_FILE)) {
downloadFile(id, filename, url, elementToDecrypt)
} else {
pendingData = PendingData(id, filename, url, elementToDecrypt)
}
}

fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
if (allGranted(grantResults) && requestCode == PERMISSION_REQUEST_CODE_DOWNLOAD_FILE) {
pendingData?.also {
downloadFile(it.id, it.filename, it.url, it.elementToDecrypt)
}
}
}

private fun downloadFile(id: String, filename: String, url: String, elementToDecrypt: ElementToDecrypt?) {
session.downloadFile(
FileService.DownloadMode.TO_EXPORT,
id,
filename,
url,
elementToDecrypt,
object : MatrixCallback<File> {
override fun onSuccess(data: File) {
activity.toast(stringProvider.getString(R.string.downloaded_file, data.path))
}

override fun onFailure(failure: Throwable) {
activity.toast(errorFormatter.toHumanReadable(failure))
}
})

}


}

View File

@ -43,7 +43,13 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
val url: String?, val url: String?,
val elementToDecrypt: ElementToDecrypt?, val elementToDecrypt: ElementToDecrypt?,
val thumbnailMediaData: ImageContentRenderer.Data val thumbnailMediaData: ImageContentRenderer.Data
) : Parcelable ) : Parcelable {

fun isLocalFile(): Boolean {
return url != null && File(url).exists()
}

}


fun render(data: Data, fun render(data: Data,
thumbnailView: ImageView, thumbnailView: ImageView,

View File

@ -19,7 +19,10 @@ package im.vector.riotx.features.media
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.core.platform.VectorBaseActivity
import kotlinx.android.synthetic.main.activity_video_media_viewer.* import kotlinx.android.synthetic.main.activity_video_media_viewer.*
@ -30,6 +33,10 @@ class VideoMediaViewerActivity : VectorBaseActivity() {


@Inject lateinit var imageContentRenderer: ImageContentRenderer @Inject lateinit var imageContentRenderer: ImageContentRenderer
@Inject lateinit var videoContentRenderer: VideoContentRenderer @Inject lateinit var videoContentRenderer: VideoContentRenderer
@Inject lateinit var mediaDownloadHelper: MediaDownloadHelper
lateinit var mediaData: VideoContentRenderer.Data

override fun getMenuRes() = R.menu.video_media_viewer


override fun injectWith(injector: ScreenComponent) { override fun injectWith(injector: ScreenComponent) {
injector.inject(this) injector.inject(this)
@ -37,9 +44,12 @@ class VideoMediaViewerActivity : VectorBaseActivity() {


override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(im.vector.riotx.R.layout.activity_video_media_viewer) setContentView(R.layout.activity_video_media_viewer)
val mediaData = intent.getParcelableExtra<VideoContentRenderer.Data>(EXTRA_MEDIA_DATA) mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA)

if (mediaData.url.isNullOrEmpty()) {
finish()
return
}
configureToolbar(videoMediaViewerToolbar, mediaData) configureToolbar(videoMediaViewerToolbar, mediaData)
imageContentRenderer.render(mediaData.thumbnailMediaData, ImageContentRenderer.Mode.FULL_SIZE, videoMediaViewerThumbnailView) imageContentRenderer.render(mediaData.thumbnailMediaData, ImageContentRenderer.Mode.FULL_SIZE, videoMediaViewerThumbnailView)
videoContentRenderer.render(mediaData, videoMediaViewerThumbnailView, videoMediaViewerLoading, videoMediaViewerVideoView, videoMediaViewerErrorView) videoContentRenderer.render(mediaData, videoMediaViewerThumbnailView, videoMediaViewerLoading, videoMediaViewerVideoView, videoMediaViewerErrorView)
@ -54,6 +64,28 @@ class VideoMediaViewerActivity : VectorBaseActivity() {
} }
} }


override fun onPrepareOptionsMenu(menu: Menu): Boolean {
val downloadItem = menu.findItem(R.id.download_video)
downloadItem.isVisible = !mediaData.isLocalFile()
return super.onPrepareOptionsMenu(menu)
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.download_video -> mediaDownloadHelper.checkPermissionAndDownload(
mediaData.eventId,
mediaData.filename,
mediaData.url,
mediaData.elementToDecrypt
)
}
return super.onOptionsItemSelected(item)
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
mediaDownloadHelper.onRequestPermissionsResult(requestCode, permissions, grantResults)
}

companion object { companion object {


private const val EXTRA_MEDIA_DATA = "EXTRA_MEDIA_DATA" private const val EXTRA_MEDIA_DATA = "EXTRA_MEDIA_DATA"

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<item
android:id="@+id/download_image"
android:title="@string/download"
android:visible="true"
app:showAsAction="never" />

</menu>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<item
android:id="@+id/download_video"
android:title="@string/download"
android:visible="true"
app:showAsAction="never" />


</menu>