Compare commits

...

1 Commits

Author SHA1 Message Date
ganfra
4a754166df Add menu action to download image and video on *ViewerActivity 2019-08-07 19:29:41 +02:00
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 data = ImageContentRenderer.Data(
eventId = informationData.eventId,
filename = messageContent.body,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
@ -251,6 +252,7 @@ class MessageItemFactory @Inject constructor(

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

View File

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

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

View File

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

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

lateinit var mediaData: ImageContentRenderer.Data

override fun getMenuRes() = R.menu.image_media_viewer

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

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(im.vector.riotx.R.layout.activity_image_media_viewer)
mediaData = intent.getParcelableExtra<ImageContentRenderer.Data>(EXTRA_MEDIA_DATA)
setContentView(R.layout.activity_image_media_viewer)
mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA)
intent.extras.getString(EXTRA_SHARED_TRANSITION_NAME)?.let {
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) {
setSupportActionBar(toolbar)
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 elementToDecrypt: ElementToDecrypt?,
val thumbnailMediaData: ImageContentRenderer.Data
) : Parcelable
) : Parcelable {

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

}

fun render(data: Data,
thumbnailView: ImageView,

View File

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

@Inject lateinit var imageContentRenderer: ImageContentRenderer
@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) {
injector.inject(this)
@ -37,9 +44,12 @@ class VideoMediaViewerActivity : VectorBaseActivity() {

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

setContentView(R.layout.activity_video_media_viewer)
mediaData = intent.getParcelableExtra(EXTRA_MEDIA_DATA)
if (mediaData.url.isNullOrEmpty()) {
finish()
return
}
configureToolbar(videoMediaViewerToolbar, mediaData)
imageContentRenderer.render(mediaData.thumbnailMediaData, ImageContentRenderer.Mode.FULL_SIZE, videoMediaViewerThumbnailView)
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 {

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>