ComposeActivity: restore ability to upload any type of file

main
Alibek Omarov 5 years ago
parent 3e5e4329de
commit d447b683cc
  1. 51
      app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt
  2. 23
      app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt
  3. 86
      app/src/main/java/com/keylesspalace/tusky/components/compose/MediaPreviewAdapter.kt
  4. 23
      app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt

@ -23,6 +23,7 @@ import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.ContentResolver
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.net.Uri
@ -31,6 +32,7 @@ import android.os.Bundle
import android.os.Parcelable
import androidx.preference.PreferenceManager
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.text.TextUtils
import android.util.Log
import android.view.KeyEvent
@ -171,6 +173,35 @@ class ComposeActivity : BaseActivity(),
composeEditField.requestFocus()
}
private fun uriToFilename(uri: Uri): String {
var result: String = "unknown"
if(uri.scheme.equals("content")) {
val cursor = contentResolver.query(uri, null, null, null, null)
if(cursor != null) {
try {
if(cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
}
}
finally {
cursor.close()
}
}
}
if(result.equals("unknown")) {
val path = uri.getPath()
if(path != null) {
result = path
val cut = result.lastIndexOf('/')
if (cut != -1) {
result = result.substring(cut + 1)
}
}
}
return result
}
private fun applyShareIntent(intent: Intent?, savedInstanceState: Bundle?) {
if (intent != null && savedInstanceState == null) {
@ -831,9 +862,11 @@ class ComposeActivity : BaseActivity(),
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
val mimeTypes = arrayOf("image/*", "video/*")
if(!hasNoAttachmentLimits) {
val mimeTypes = arrayOf("image/*", "video/*")
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
}
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
startActivityForResult(intent, MEDIA_PICK_RESULT)
}
@ -867,8 +900,8 @@ class ComposeActivity : BaseActivity(),
}
private fun pickMedia(uri: Uri) {
withLifecycleContext {
viewModel.pickMedia(uri).observe { exceptionOrItem ->
withLifecycleContext {
viewModel.pickMedia(uri, uriToFilename(uri)).observe { exceptionOrItem ->
exceptionOrItem.asLeftOrNull()?.let {
val errorId = when (it) {
is VideoSizeException -> {
@ -991,14 +1024,18 @@ class ComposeActivity : BaseActivity(),
data class QueuedMedia(
val localId: Long,
val uri: Uri,
val type: Type,
val type: Int,
val mediaSize: Long,
val originalFileName: String,
val noChanges: Boolean = false,
val uploadPercent: Int = 0,
val id: String? = null,
val description: String? = null
) {
enum class Type {
IMAGE, VIDEO;
companion object Type {
public const val IMAGE: Int = 0
public const val VIDEO: Int = 1
public const val UNKNOWN: Int = 2
}
}

@ -121,19 +121,20 @@ class ComposeViewModel
.autoDispose()
}
fun pickMedia(uri: Uri): LiveData<Either<Throwable, QueuedMedia>> {
fun pickMedia(uri: Uri, filename: String?): LiveData<Either<Throwable, QueuedMedia>> {
// We are not calling .toLiveData() here because we don't want to stop the process when
// the Activity goes away temporarily (like on screen rotation).
val liveData = MutableLiveData<Either<Throwable, QueuedMedia>>()
mediaUploader.prepareMedia(uri)
mediaUploader.prepareMedia(uri, instanceParams.value!!.hasNoAttachmentLimits)
.map { (type, uri, size) ->
val mediaItems = media.value!!
if (type == QueuedMedia.Type.VIDEO
if (!instanceParams.value!!.hasNoAttachmentLimits
&& type == QueuedMedia.Type.VIDEO
&& mediaItems.isNotEmpty()
&& mediaItems[0].type == QueuedMedia.Type.IMAGE) {
throw VideoOrImageException()
} else {
addMediaToQueue(type, uri, size)
addMediaToQueue(type, uri, size, if(filename != null) filename else "unknown")
}
}
.subscribe({ queuedMedia ->
@ -144,9 +145,10 @@ class ComposeViewModel
.autoDispose()
return liveData
}
private fun addMediaToQueue(type: QueuedMedia.Type, uri: Uri, mediaSize: Long): QueuedMedia {
val mediaItem = QueuedMedia(System.currentTimeMillis(), uri, type, mediaSize)
private fun addMediaToQueue(type: Int, uri: Uri, mediaSize: Long, filename: String): QueuedMedia {
val mediaItem = QueuedMedia(System.currentTimeMillis(), uri, type, mediaSize, filename,
instanceParams.value!!.hasNoAttachmentLimits)
media.value = media.value!! + mediaItem
mediaToDisposable[mediaItem.localId] = mediaUploader
.uploadMedia(mediaItem)
@ -175,8 +177,9 @@ class ComposeViewModel
return mediaItem
}
private fun addUploadedMedia(id: String, type: QueuedMedia.Type, uri: Uri, description: String?) {
val mediaItem = QueuedMedia(System.currentTimeMillis(), uri, type, 0, -1, id, description)
private fun addUploadedMedia(id: String, type: Int, uri: Uri, description: String?) {
val mediaItem = QueuedMedia(System.currentTimeMillis(), uri, type, 0, "unknown",
instanceParams.value!!.hasNoAttachmentLimits, -1, id, description)
media.value = media.value!! + mediaItem
}
@ -379,7 +382,7 @@ class ComposeViewModel
if (loadedDraftMediaUris != null && loadedDraftMediaDescriptions != null) {
loadedDraftMediaUris.zip(loadedDraftMediaDescriptions)
.forEach { (uri, description) ->
pickMedia(uri.toUri()).observeForever { errorOrItem ->
pickMedia(uri.toUri(), null).observeForever { errorOrItem ->
if (errorOrItem.isRight() && description != null) {
updateDescription(errorOrItem.asRight().localId, description)
}

@ -20,6 +20,8 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.PopupMenu
import android.view.Gravity
import android.text.TextUtils
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
@ -27,13 +29,14 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.compose.view.ProgressTextView
import com.keylesspalace.tusky.components.compose.view.ProgressImageView
class MediaPreviewAdapter(
context: Context,
private val onAddCaption: (ComposeActivity.QueuedMedia) -> Unit,
private val onRemove: (ComposeActivity.QueuedMedia) -> Unit
) : RecyclerView.Adapter<MediaPreviewAdapter.PreviewViewHolder>() {
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
fun submitList(list: List<ComposeActivity.QueuedMedia>) {
this.differ.submitList(list)
@ -60,20 +63,43 @@ class MediaPreviewAdapter(
context.resources.getDimensionPixelSize(R.dimen.compose_media_preview_size)
override fun getItemCount(): Int = differ.currentList.size
override fun getItemViewType(position: Int): Int {
val item = differ.currentList[position]
return item.type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PreviewViewHolder {
return PreviewViewHolder(ProgressImageView(parent.context))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when(viewType) {
ComposeActivity.QueuedMedia.Type.UNKNOWN -> {
return TextViewHolder(ProgressTextView(parent.context))
}
else -> {
return PreviewViewHolder(ProgressImageView(parent.context))
}
}
}
override fun onBindViewHolder(holder: PreviewViewHolder, position: Int) {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = differ.currentList[position]
holder.progressImageView.setChecked(!item.description.isNullOrEmpty())
holder.progressImageView.setProgress(item.uploadPercent)
Glide.with(holder.itemView.context)
.load(item.uri)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.dontAnimate()
.into(holder.progressImageView)
when(item.type) {
ComposeActivity.QueuedMedia.Type.UNKNOWN -> {
(holder as TextViewHolder).view.setText(item.originalFileName)
holder.view.setChecked(!item.description.isNullOrEmpty())
holder.view.setProgress(item.uploadPercent)
}
else -> {
(holder as PreviewViewHolder).view.setChecked(!item.description.isNullOrEmpty())
holder.view.setProgress(item.uploadPercent)
Glide.with(holder.itemView.context)
.load(item.uri)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.dontAnimate()
.into(holder.view)
}
}
}
private val differ = AsyncListDiffer(this, object : DiffUtil.ItemCallback<ComposeActivity.QueuedMedia>() {
@ -85,9 +111,33 @@ class MediaPreviewAdapter(
return oldItem == newItem
}
})
inner class TextViewHolder(val view: ProgressTextView)
: RecyclerView.ViewHolder(view) {
init {
val layoutParams = ConstraintLayout.LayoutParams(thumbnailViewSize, thumbnailViewSize)
val margin = itemView.context.resources
.getDimensionPixelSize(R.dimen.compose_media_preview_margin)
val marginBottom = itemView.context.resources
.getDimensionPixelSize(R.dimen.compose_media_preview_margin_bottom)
layoutParams.setMargins(margin, 0, margin, marginBottom)
view.layoutParams = layoutParams
view.gravity = Gravity.CENTER
view.setHorizontallyScrolling(true)
view.ellipsize = TextUtils.TruncateAt.MARQUEE
view.marqueeRepeatLimit = -1
view.setSingleLine()
view.setSelected(true)
view.maxLines = 1
view.textSize = 16.0f
view.setOnClickListener {
onMediaClick(adapterPosition, view)
}
}
}
inner class PreviewViewHolder(val progressImageView: ProgressImageView)
: RecyclerView.ViewHolder(progressImageView) {
inner class PreviewViewHolder(val view: ProgressImageView)
: RecyclerView.ViewHolder(view) {
init {
val layoutParams = ConstraintLayout.LayoutParams(thumbnailViewSize, thumbnailViewSize)
val margin = itemView.context.resources
@ -95,11 +145,11 @@ class MediaPreviewAdapter(
val marginBottom = itemView.context.resources
.getDimensionPixelSize(R.dimen.compose_media_preview_margin_bottom)
layoutParams.setMargins(margin, 0, margin, marginBottom)
progressImageView.layoutParams = layoutParams
progressImageView.scaleType = ImageView.ScaleType.CENTER_CROP
progressImageView.setOnClickListener {
onMediaClick(adapterPosition, progressImageView)
view.layoutParams = layoutParams
view.scaleType = ImageView.ScaleType.CENTER_CROP
view.setOnClickListener {
onMediaClick(adapterPosition, view)
}
}
}
}
}

@ -56,10 +56,10 @@ fun createNewImageFile(context: Context): File {
)
}
data class PreparedMedia(val type: QueuedMedia.Type, val uri: Uri, val size: Long)
data class PreparedMedia(val type: Int, val uri: Uri, val size: Long)
interface MediaUploader {
fun prepareMedia(inUri: Uri): Single<PreparedMedia>
fun prepareMedia(inUri: Uri, hasNoLimits: Boolean): Single<PreparedMedia>
fun uploadMedia(media: QueuedMedia): Observable<UploadEvent>
}
@ -83,7 +83,7 @@ class MediaUploaderImpl(
.subscribeOn(Schedulers.io())
}
override fun prepareMedia(inUri: Uri): Single<PreparedMedia> {
override fun prepareMedia(inUri: Uri, hasNoLimits: Boolean): Single<PreparedMedia> {
return Single.fromCallable {
var mediaSize = getMediaSize(contentResolver, inUri)
var uri = inUri
@ -120,7 +120,7 @@ class MediaUploaderImpl(
val topLevelType = mimeType.substring(0, mimeType.indexOf('/'))
when (topLevelType) {
"video" -> {
if (mediaSize > STATUS_VIDEO_SIZE_LIMIT) {
if (!hasNoLimits && mediaSize > STATUS_VIDEO_SIZE_LIMIT) {
throw VideoSizeException()
}
PreparedMedia(QueuedMedia.Type.VIDEO, uri, mediaSize)
@ -129,7 +129,8 @@ class MediaUploaderImpl(
PreparedMedia(QueuedMedia.Type.IMAGE, uri, mediaSize)
}
else -> {
throw MediaTypeException()
PreparedMedia(QueuedMedia.Type.UNKNOWN, uri, mediaSize)
// throw MediaTypeException()
}
}
} else {
@ -139,17 +140,17 @@ class MediaUploaderImpl(
}
private val contentResolver = context.contentResolver
private fun upload(media: QueuedMedia): Observable<UploadEvent> {
return Observable.create { emitter ->
var mimeType = contentResolver.getType(media.uri)
val map = MimeTypeMap.getSingleton()
val fileExtension = map.getExtensionFromMimeType(mimeType)
val filename = String.format("%s_%s_%s.%s",
context.getString(R.string.app_name),
Date().time.toString(),
randomAlphanumericString(10),
fileExtension)
context.getString(R.string.app_name),
Date().time.toString(),
randomAlphanumericString(10),
fileExtension)
val stream = contentResolver.openInputStream(media.uri)
@ -188,7 +189,7 @@ class MediaUploaderImpl(
}
private fun shouldResizeMedia(media: QueuedMedia): Boolean {
return media.type == QueuedMedia.Type.IMAGE
return !media.noChanges && media.type == QueuedMedia.Type.IMAGE
&& (media.mediaSize > STATUS_IMAGE_SIZE_LIMIT
|| getImageSquarePixels(context.contentResolver, media.uri) > STATUS_IMAGE_PIXEL_SIZE_LIMIT)
}

Loading…
Cancel
Save