Sent video does not contains duration (#3272)

When using the file picker (and not the media picker).
Now when using the file picker, we detect the mime type and we send the correct event
Also some code duplication
This commit is contained in:
Benoit Marty 2021-05-03 19:44:11 +02:00 committed by Benoit Marty
parent d9ffce7e0d
commit f7949100a7
11 changed files with 237 additions and 311 deletions

View file

@ -22,6 +22,7 @@ Bugfix 🐛:
- Fix wording issue (#3242)
- Fix missing sender information after edits (#3184)
- Fix read marker not updating automatically (#3267)
- Sent video does not contains duration (#3272)
Translations 🗣:
-

View file

@ -18,9 +18,8 @@ package im.vector.lib.multipicker
import android.content.Context
import android.content.Intent
import android.media.MediaMetadataRetriever
import android.provider.MediaStore
import im.vector.lib.multipicker.entity.MultiPickerAudioType
import im.vector.lib.multipicker.utils.toMultiPickerAudioType
/**
* Audio file picker implementation
@ -32,48 +31,9 @@ class AudioPicker : Picker<MultiPickerAudioType>() {
* Returns selected audio files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerAudioType> {
val audioList = mutableListOf<MultiPickerAudioType>()
getSelectedUriList(data).forEach { selectedUri ->
val projection = arrayOf(
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.SIZE
)
context.contentResolver.query(
selectedUri,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Audio.Media.SIZE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
var duration = 0L
context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd ->
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
}
audioList.add(
MultiPickerAudioType(
name,
size,
context.contentResolver.getType(selectedUri),
selectedUri,
duration
)
)
}
}
return getSelectedUriList(data).mapNotNull { selectedUri ->
selectedUri.toMultiPickerAudioType(context)
}
return audioList
}
override fun createIntent(): Intent {

View file

@ -23,7 +23,7 @@ import android.provider.MediaStore
import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.FileProvider
import im.vector.lib.multipicker.entity.MultiPickerImageType
import im.vector.lib.multipicker.utils.ImageUtils
import im.vector.lib.multipicker.utils.toMultiPickerImageType
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
@ -54,40 +54,7 @@ class CameraPicker {
* or user cancelled the operation.
*/
fun getTakenPhoto(context: Context, photoUri: Uri): MultiPickerImageType? {
val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE
)
context.contentResolver.query(
photoUri,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
val bitmap = ImageUtils.getBitmap(context, photoUri)
val orientation = ImageUtils.getOrientation(context, photoUri)
return MultiPickerImageType(
name,
size,
context.contentResolver.getType(photoUri),
photoUri,
bitmap?.width ?: 0,
bitmap?.height ?: 0,
orientation
)
}
}
return null
return photoUri.toMultiPickerImageType(context)
}
private fun createIntent(): Intent {

View file

@ -18,12 +18,12 @@ package im.vector.lib.multipicker
import android.content.Context
import android.content.Intent
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.provider.MediaStore
import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.FileProvider
import im.vector.lib.multipicker.entity.MultiPickerVideoType
import im.vector.lib.multipicker.utils.toMultiPickerVideoType
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
@ -54,52 +54,7 @@ class CameraVideoPicker {
* or user cancelled the operation.
*/
fun getTakenVideo(context: Context, videoUri: Uri): MultiPickerVideoType? {
val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.MIME_TYPE
)
context.contentResolver.query(
videoUri,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
var duration = 0L
var width = 0
var height = 0
var orientation = 0
context.contentResolver.openFileDescriptor(videoUri, "r")?.use { pfd ->
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toInt() ?: 0
height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toInt() ?: 0
orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)?.toInt() ?: 0
}
return MultiPickerVideoType(
name,
size,
context.contentResolver.getType(videoUri),
videoUri,
width,
height,
orientation,
duration
)
}
}
return null
return videoUri.toMultiPickerVideoType(context)
}
private fun createIntent(): Intent {

View file

@ -19,41 +19,55 @@ package im.vector.lib.multipicker
import android.content.Context
import android.content.Intent
import android.provider.OpenableColumns
import im.vector.lib.multipicker.entity.MultiPickerBaseType
import im.vector.lib.multipicker.entity.MultiPickerFileType
import im.vector.lib.multipicker.utils.isMimeTypeAudio
import im.vector.lib.multipicker.utils.isMimeTypeImage
import im.vector.lib.multipicker.utils.isMimeTypeVideo
import im.vector.lib.multipicker.utils.toMultiPickerAudioType
import im.vector.lib.multipicker.utils.toMultiPickerImageType
import im.vector.lib.multipicker.utils.toMultiPickerVideoType
/**
* Implementation of selecting any type of files
*/
class FilePicker : Picker<MultiPickerFileType>() {
class FilePicker : Picker<MultiPickerBaseType>() {
/**
* Call this function from onActivityResult(int, int, Intent).
* Returns selected files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerFileType> {
val fileList = mutableListOf<MultiPickerFileType>()
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseType> {
return getSelectedUriList(data).mapNotNull { selectedUri ->
val type = context.contentResolver.getType(selectedUri)
getSelectedUriList(data).forEach { selectedUri ->
context.contentResolver.query(selectedUri, null, null, null, null)
?.use { cursor ->
val nameColumn = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(OpenableColumns.SIZE)
if (cursor.moveToFirst()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
when {
type.isMimeTypeVideo() -> selectedUri.toMultiPickerVideoType(context)
type.isMimeTypeImage() -> selectedUri.toMultiPickerImageType(context)
type.isMimeTypeAudio() -> selectedUri.toMultiPickerAudioType(context)
else -> {
// Other files
context.contentResolver.query(selectedUri, null, null, null, null)
?.use { cursor ->
val nameColumn = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(OpenableColumns.SIZE)
if (cursor.moveToFirst()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
fileList.add(
MultiPickerFileType(
name,
size,
context.contentResolver.getType(selectedUri),
selectedUri
)
)
}
}
} else {
null
}
}
}
}
}
return fileList
}
override fun createIntent(): Intent {

View file

@ -18,9 +18,8 @@ package im.vector.lib.multipicker
import android.content.Context
import android.content.Intent
import android.provider.MediaStore
import im.vector.lib.multipicker.entity.MultiPickerImageType
import im.vector.lib.multipicker.utils.ImageUtils
import im.vector.lib.multipicker.utils.toMultiPickerImageType
/**
* Image Picker implementation
@ -32,46 +31,9 @@ class ImagePicker : Picker<MultiPickerImageType>() {
* Returns selected image files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerImageType> {
val imageList = mutableListOf<MultiPickerImageType>()
getSelectedUriList(data).forEach { selectedUri ->
val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE
)
context.contentResolver.query(
selectedUri,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
val bitmap = ImageUtils.getBitmap(context, selectedUri)
val orientation = ImageUtils.getOrientation(context, selectedUri)
imageList.add(
MultiPickerImageType(
name,
size,
context.contentResolver.getType(selectedUri),
selectedUri,
bitmap?.width ?: 0,
bitmap?.height ?: 0,
orientation
)
)
}
}
return getSelectedUriList(data).mapNotNull { selectedUri ->
selectedUri.toMultiPickerImageType(context)
}
return imageList
}
override fun createIntent(): Intent {

View file

@ -18,12 +18,10 @@ package im.vector.lib.multipicker
import android.content.Context
import android.content.Intent
import android.media.MediaMetadataRetriever
import android.provider.MediaStore
import im.vector.lib.multipicker.entity.MultiPickerBaseMediaType
import im.vector.lib.multipicker.entity.MultiPickerImageType
import im.vector.lib.multipicker.entity.MultiPickerVideoType
import im.vector.lib.multipicker.utils.ImageUtils
import im.vector.lib.multipicker.utils.isMimeTypeVideo
import im.vector.lib.multipicker.utils.toMultiPickerImageType
import im.vector.lib.multipicker.utils.toMultiPickerVideoType
/**
* Image/Video Picker implementation
@ -35,79 +33,16 @@ class MediaPicker : Picker<MultiPickerBaseMediaType>() {
* Returns selected image/video files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerBaseMediaType> {
val mediaList = mutableListOf<MultiPickerBaseMediaType>()
return getSelectedUriList(data).mapNotNull { selectedUri ->
val mimeType = context.contentResolver.getType(selectedUri)
getSelectedUriList(data).forEach { selectedUri ->
val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.MIME_TYPE
)
context.contentResolver.query(
selectedUri,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE)
val mimeTypeColumn = cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
val mimeType = cursor.getString(mimeTypeColumn)
if (mimeType.isMimeTypeVideo()) {
var duration = 0L
var width = 0
var height = 0
var orientation = 0
context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd ->
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toInt() ?: 0
height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toInt() ?: 0
orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)?.toInt() ?: 0
}
mediaList.add(
MultiPickerVideoType(
name,
size,
context.contentResolver.getType(selectedUri),
selectedUri,
width,
height,
orientation,
duration
)
)
} else {
// Assume it's an image
val bitmap = ImageUtils.getBitmap(context, selectedUri)
val orientation = ImageUtils.getOrientation(context, selectedUri)
mediaList.add(
MultiPickerImageType(
name,
size,
context.contentResolver.getType(selectedUri),
selectedUri,
bitmap?.width ?: 0,
bitmap?.height ?: 0,
orientation
)
)
}
}
if (mimeType.isMimeTypeVideo()) {
selectedUri.toMultiPickerVideoType(context)
} else {
// Assume it's an image
selectedUri.toMultiPickerImageType(context)
}
}
return mediaList
}
override fun createIntent(): Intent {
@ -119,6 +54,4 @@ class MediaPicker : Picker<MultiPickerBaseMediaType>() {
putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
}
}
private fun String?.isMimeTypeVideo() = this?.startsWith("video/") == true
}

View file

@ -18,9 +18,8 @@ package im.vector.lib.multipicker
import android.content.Context
import android.content.Intent
import android.media.MediaMetadataRetriever
import android.provider.MediaStore
import im.vector.lib.multipicker.entity.MultiPickerVideoType
import im.vector.lib.multipicker.utils.toMultiPickerVideoType
/**
* Video Picker implementation
@ -32,57 +31,9 @@ class VideoPicker : Picker<MultiPickerVideoType>() {
* Returns selected video files or empty list if user did not select any files.
*/
override fun getSelectedFiles(context: Context, data: Intent?): List<MultiPickerVideoType> {
val videoList = mutableListOf<MultiPickerVideoType>()
getSelectedUriList(data).forEach { selectedUri ->
val projection = arrayOf(
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.SIZE
)
context.contentResolver.query(
selectedUri,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Video.Media.SIZE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
var duration = 0L
var width = 0
var height = 0
var orientation = 0
context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd ->
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toInt() ?: 0
height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toInt() ?: 0
orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)?.toInt() ?: 0
}
videoList.add(
MultiPickerVideoType(
name,
size,
context.contentResolver.getType(selectedUri),
selectedUri,
width,
height,
orientation,
duration
)
)
}
}
return getSelectedUriList(data).mapNotNull { selectedUri ->
selectedUri.toMultiPickerVideoType(context)
}
return videoList
}
override fun createIntent(): Intent {

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2021 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.lib.multipicker.utils
import android.content.Context
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.provider.MediaStore
import im.vector.lib.multipicker.entity.MultiPickerAudioType
import im.vector.lib.multipicker.entity.MultiPickerImageType
import im.vector.lib.multipicker.entity.MultiPickerVideoType
internal fun Uri.toMultiPickerImageType(context: Context): MultiPickerImageType? {
val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE
)
return context.contentResolver.query(
this,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
val bitmap = ImageUtils.getBitmap(context, this)
val orientation = ImageUtils.getOrientation(context, this)
MultiPickerImageType(
name,
size,
context.contentResolver.getType(this),
this,
bitmap?.width ?: 0,
bitmap?.height ?: 0,
orientation
)
} else {
null
}
}
}
internal fun Uri.toMultiPickerVideoType(context: Context): MultiPickerVideoType? {
val projection = arrayOf(
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.SIZE
)
return context.contentResolver.query(
this,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Video.Media.SIZE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
var duration = 0L
var width = 0
var height = 0
var orientation = 0
context.contentResolver.openFileDescriptor(this, "r")?.use { pfd ->
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toInt() ?: 0
height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toInt() ?: 0
orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)?.toInt() ?: 0
}
MultiPickerVideoType(
name,
size,
context.contentResolver.getType(this),
this,
width,
height,
orientation,
duration
)
} else {
null
}
}
}
internal fun Uri.toMultiPickerAudioType(context: Context): MultiPickerAudioType? {
val projection = arrayOf(
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.SIZE
)
return context.contentResolver.query(
this,
projection,
null,
null,
null
)?.use { cursor ->
val nameColumn = cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)
val sizeColumn = cursor.getColumnIndex(MediaStore.Audio.Media.SIZE)
if (cursor.moveToNext()) {
val name = cursor.getString(nameColumn)
val size = cursor.getLong(sizeColumn)
var duration = 0L
context.contentResolver.openFileDescriptor(this, "r")?.use { pfd ->
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
}
MultiPickerAudioType(
name,
size,
context.contentResolver.getType(this),
this,
duration
)
} else {
null
}
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2021 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.lib.multipicker.utils
internal fun String?.isMimeTypeImage() = this?.startsWith("image/") == true
internal fun String?.isMimeTypeVideo() = this?.startsWith("video/") == true
internal fun String?.isMimeTypeAudio() = this?.startsWith("audio/") == true

View file

@ -70,6 +70,16 @@ private fun MultiPickerBaseType.mapType(): ContentAttachmentData.Type {
}
}
fun MultiPickerBaseType.toContentAttachmentData(): ContentAttachmentData {
return when (this) {
is MultiPickerImageType -> toContentAttachmentData()
is MultiPickerVideoType -> toContentAttachmentData()
is MultiPickerAudioType -> toContentAttachmentData()
is MultiPickerFileType -> toContentAttachmentData()
else -> throw IllegalStateException("Unknown file type")
}
}
fun MultiPickerBaseMediaType.toContentAttachmentData(): ContentAttachmentData {
return when (this) {
is MultiPickerImageType -> toContentAttachmentData()