Add support for uploading audio attachments (#1630)
* Add support for audio attachments. Partially addresses #1337 * Register Tusky as a target for audio sharing * Use icon with textColorTertiary for audio preview
This commit is contained in:
parent
a7c1345085
commit
9dccd06a06
7 changed files with 44 additions and 11 deletions
|
@ -89,6 +89,13 @@
|
||||||
|
|
||||||
<data android:mimeType="video/*" />
|
<data android:mimeType="video/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:mimeType="audio/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.service.chooser.chooser_target_service"
|
android:name="android.service.chooser.chooser_target_service"
|
||||||
|
|
|
@ -176,7 +176,7 @@ class ComposeActivity : BaseActivity(),
|
||||||
* instance state will be re-queued. */
|
* instance state will be re-queued. */
|
||||||
val type = intent.type
|
val type = intent.type
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
if (type.startsWith("image/") || type.startsWith("video/")) {
|
if (type.startsWith("image/") || type.startsWith("video/") || type.startsWith("audio/")) {
|
||||||
val uriList = ArrayList<Uri>()
|
val uriList = ArrayList<Uri>()
|
||||||
if (intent.action != null) {
|
if (intent.action != null) {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
|
@ -323,7 +323,7 @@ class ComposeActivity : BaseActivity(),
|
||||||
combineOptionalLiveData(viewModel.media, viewModel.poll) { media, poll ->
|
combineOptionalLiveData(viewModel.media, viewModel.poll) { media, poll ->
|
||||||
val active = poll == null
|
val active = poll == null
|
||||||
&& media!!.size != 4
|
&& media!!.size != 4
|
||||||
&& media.firstOrNull()?.type != QueuedMedia.Type.VIDEO
|
&& (media.isEmpty() || media.first().type == QueuedMedia.Type.IMAGE)
|
||||||
enableButton(composeAddMediaButton, active, active)
|
enableButton(composeAddMediaButton, active, active)
|
||||||
enablePollButton(media.isNullOrEmpty())
|
enablePollButton(media.isNullOrEmpty())
|
||||||
}.subscribe()
|
}.subscribe()
|
||||||
|
@ -813,7 +813,7 @@ class ComposeActivity : BaseActivity(),
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
|
||||||
val mimeTypes = arrayOf("image/*", "video/*")
|
val mimeTypes = arrayOf("image/*", "video/*", "audio/*")
|
||||||
intent.type = "*/*"
|
intent.type = "*/*"
|
||||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
||||||
startActivityForResult(intent, MEDIA_PICK_RESULT)
|
startActivityForResult(intent, MEDIA_PICK_RESULT)
|
||||||
|
@ -856,6 +856,9 @@ class ComposeActivity : BaseActivity(),
|
||||||
is VideoSizeException -> {
|
is VideoSizeException -> {
|
||||||
R.string.error_video_upload_size
|
R.string.error_video_upload_size
|
||||||
}
|
}
|
||||||
|
is AudioSizeException -> {
|
||||||
|
R.string.error_audio_upload_size
|
||||||
|
}
|
||||||
is VideoOrImageException -> {
|
is VideoOrImageException -> {
|
||||||
R.string.error_media_upload_image_or_video
|
R.string.error_media_upload_image_or_video
|
||||||
}
|
}
|
||||||
|
@ -980,7 +983,7 @@ class ComposeActivity : BaseActivity(),
|
||||||
val description: String? = null
|
val description: String? = null
|
||||||
) {
|
) {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
IMAGE, VIDEO;
|
IMAGE, VIDEO, AUDIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1036,7 +1039,7 @@ class ComposeActivity : BaseActivity(),
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun canHandleMimeType(mimeType: String?): Boolean {
|
fun canHandleMimeType(mimeType: String?): Boolean {
|
||||||
return mimeType != null && (mimeType.startsWith("image/") || mimeType.startsWith("video/") || mimeType == "text/plain")
|
return mimeType != null && (mimeType.startsWith("image/") || mimeType.startsWith("video/") || mimeType.startsWith("audio/") || mimeType == "text/plain")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ class ComposeViewModel
|
||||||
mediaUploader.prepareMedia(uri)
|
mediaUploader.prepareMedia(uri)
|
||||||
.map { (type, uri, size) ->
|
.map { (type, uri, size) ->
|
||||||
val mediaItems = media.value!!
|
val mediaItems = media.value!!
|
||||||
if (type == QueuedMedia.Type.VIDEO
|
if (type != QueuedMedia.Type.IMAGE
|
||||||
&& mediaItems.isNotEmpty()
|
&& mediaItems.isNotEmpty()
|
||||||
&& mediaItems[0].type == QueuedMedia.Type.IMAGE) {
|
&& mediaItems[0].type == QueuedMedia.Type.IMAGE) {
|
||||||
throw VideoOrImageException()
|
throw VideoOrImageException()
|
||||||
|
@ -388,6 +388,7 @@ class ComposeViewModel
|
||||||
val mediaType = when (a.type) {
|
val mediaType = when (a.type) {
|
||||||
Attachment.Type.VIDEO, Attachment.Type.GIFV -> QueuedMedia.Type.VIDEO
|
Attachment.Type.VIDEO, Attachment.Type.GIFV -> QueuedMedia.Type.VIDEO
|
||||||
Attachment.Type.UNKNOWN, Attachment.Type.IMAGE -> QueuedMedia.Type.IMAGE
|
Attachment.Type.UNKNOWN, Attachment.Type.IMAGE -> QueuedMedia.Type.IMAGE
|
||||||
|
Attachment.Type.AUDIO -> QueuedMedia.Type.AUDIO
|
||||||
else -> QueuedMedia.Type.IMAGE
|
else -> QueuedMedia.Type.IMAGE
|
||||||
}
|
}
|
||||||
addUploadedMedia(a.id, mediaType, a.url.toUri(), a.description)
|
addUploadedMedia(a.id, mediaType, a.url.toUri(), a.description)
|
||||||
|
|
|
@ -69,11 +69,16 @@ class MediaPreviewAdapter(
|
||||||
val item = differ.currentList[position]
|
val item = differ.currentList[position]
|
||||||
holder.progressImageView.setChecked(!item.description.isNullOrEmpty())
|
holder.progressImageView.setChecked(!item.description.isNullOrEmpty())
|
||||||
holder.progressImageView.setProgress(item.uploadPercent)
|
holder.progressImageView.setProgress(item.uploadPercent)
|
||||||
Glide.with(holder.itemView.context)
|
if (item.type == ComposeActivity.QueuedMedia.Type.AUDIO) {
|
||||||
.load(item.uri)
|
// TODO: Fancy waveform display?
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
holder.progressImageView.setImageResource(R.drawable.ic_music_box_preview_24dp)
|
||||||
.dontAnimate()
|
} else {
|
||||||
.into(holder.progressImageView)
|
Glide.with(holder.itemView.context)
|
||||||
|
.load(item.uri)
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
|
.dontAnimate()
|
||||||
|
.into(holder.progressImageView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val differ = AsyncListDiffer(this, object : DiffUtil.ItemCallback<ComposeActivity.QueuedMedia>() {
|
private val differ = AsyncListDiffer(this, object : DiffUtil.ItemCallback<ComposeActivity.QueuedMedia>() {
|
||||||
|
|
|
@ -63,6 +63,7 @@ interface MediaUploader {
|
||||||
fun uploadMedia(media: QueuedMedia): Observable<UploadEvent>
|
fun uploadMedia(media: QueuedMedia): Observable<UploadEvent>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AudioSizeException : Exception()
|
||||||
class VideoSizeException : Exception()
|
class VideoSizeException : Exception()
|
||||||
class MediaTypeException : Exception()
|
class MediaTypeException : Exception()
|
||||||
class CouldNotOpenFileException : Exception()
|
class CouldNotOpenFileException : Exception()
|
||||||
|
@ -128,6 +129,12 @@ class MediaUploaderImpl(
|
||||||
"image" -> {
|
"image" -> {
|
||||||
PreparedMedia(QueuedMedia.Type.IMAGE, uri, mediaSize)
|
PreparedMedia(QueuedMedia.Type.IMAGE, uri, mediaSize)
|
||||||
}
|
}
|
||||||
|
"audio" -> {
|
||||||
|
if (mediaSize > STATUS_AUDIO_SIZE_LIMIT) {
|
||||||
|
throw AudioSizeException()
|
||||||
|
}
|
||||||
|
PreparedMedia(QueuedMedia.Type.AUDIO, uri, mediaSize)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
throw MediaTypeException()
|
throw MediaTypeException()
|
||||||
}
|
}
|
||||||
|
@ -196,6 +203,7 @@ class MediaUploaderImpl(
|
||||||
private companion object {
|
private companion object {
|
||||||
private const val TAG = "MediaUploaderImpl"
|
private const val TAG = "MediaUploaderImpl"
|
||||||
private const val STATUS_VIDEO_SIZE_LIMIT = 41943040 // 40MiB
|
private const val STATUS_VIDEO_SIZE_LIMIT = 41943040 // 40MiB
|
||||||
|
private const val STATUS_AUDIO_SIZE_LIMIT = 41943040 // 40MiB
|
||||||
private const val STATUS_IMAGE_SIZE_LIMIT = 8388608 // 8MiB
|
private const val STATUS_IMAGE_SIZE_LIMIT = 8388608 // 8MiB
|
||||||
private const val STATUS_IMAGE_PIXEL_SIZE_LIMIT = 16777216 // 4096^2 Pixels
|
private const val STATUS_IMAGE_PIXEL_SIZE_LIMIT = 16777216 // 4096^2 Pixels
|
||||||
|
|
||||||
|
|
8
app/src/main/res/drawable/ic_music_box_preview_24dp.xml
Normal file
8
app/src/main/res/drawable/ic_music_box_preview_24dp.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path android:fillColor="?android:textColorTertiary" android:pathData="M16,9H13V14.5A2.5,2.5 0 0,1 10.5,17A2.5,2.5 0 0,1 8,14.5A2.5,2.5 0 0,1 10.5,12C11.07,12 11.58,12.19 12,12.5V7H16M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3Z" />
|
||||||
|
</vector>
|
|
@ -12,6 +12,7 @@
|
||||||
<string name="error_compose_character_limit">The status is too long!</string>
|
<string name="error_compose_character_limit">The status is too long!</string>
|
||||||
<string name="error_image_upload_size">The file must be less than 8MB.</string>
|
<string name="error_image_upload_size">The file must be less than 8MB.</string>
|
||||||
<string name="error_video_upload_size">Video files must be less than 40MB.</string>
|
<string name="error_video_upload_size">Video files must be less than 40MB.</string>
|
||||||
|
<string name="error_audio_upload_size">Audio files must be less than 40MB.</string>
|
||||||
<string name="error_media_upload_type">That type of file cannot be uploaded.</string>
|
<string name="error_media_upload_type">That type of file cannot be uploaded.</string>
|
||||||
<string name="error_media_upload_opening">That file could not be opened.</string>
|
<string name="error_media_upload_opening">That file could not be opened.</string>
|
||||||
<string name="error_media_upload_permission">Permission to read media is required.</string>
|
<string name="error_media_upload_permission">Permission to read media is required.</string>
|
||||||
|
|
Loading…
Reference in a new issue