From 44ed189b3c314fd61edcd10695df7ec1caf5500c Mon Sep 17 00:00:00 2001 From: Ivan Kupalov Date: Sat, 1 Aug 2020 21:48:51 +0200 Subject: [PATCH] Description improvements (#1846) * Increase character limit for media descriptions to 1500 It was increased in Mastodon 3.0.0 which was released in October 2019. * Improve image description view Since media descriptions can be longer now, we need to adjust the UI. It is a common problem that description takes up the whole screen, it's hard for readers and also discourages people from adding descriptions. This commit uses bottom sheet to hide most of the description. Since we know how much screen space it will cover, we can use darker background which makes reading text easier. * Adjust description handle * Fix unable to dismiss image caption --- .../tusky/adapter/StatusBaseViewHolder.java | 2 +- .../compose/dialog/CaptionDialog.kt | 5 +- .../tusky/fragment/ViewImageFragment.kt | 31 +++------ .../tusky/fragment/ViewMediaFragment.kt | 18 +++--- .../tusky/fragment/ViewVideoFragment.kt | 20 +++--- .../res/drawable/description_bg_expanded.xml | 10 +++ .../drawable/ic_drag_indicator_horiz_24dp.xml | 9 +++ .../main/res/layout/fragment_view_image.xml | 64 +++++++++++++++---- 8 files changed, 103 insertions(+), 56 deletions(-) create mode 100644 app/src/main/res/drawable/description_bg_expanded.xml create mode 100644 app/src/main/res/drawable/ic_drag_indicator_horiz_24dp.xml diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 26bd6024..64a59bfe 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -805,7 +805,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { itemView.setAccessibilityDelegate(null); } else { if (payloads instanceof List) - for (Object item : (List) payloads) { + for (Object item : (List) payloads) { if (Key.KEY_CREATED.equals(item)) { setCreatedAt(status.getCreatedAt(), statusDisplayOptions); } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/CaptionDialog.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/CaptionDialog.kt index 7dc237ca..442212be 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/CaptionDialog.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/CaptionDialog.kt @@ -41,9 +41,8 @@ import com.keylesspalace.tusky.R import com.keylesspalace.tusky.util.withLifecycleContext import java.io.File -// https://github.com/tootsuite/mastodon/blob/1656663/app/models/media_attachment.rb#L94 -private const val MEDIA_DESCRIPTION_CHARACTER_LIMIT = 420 - +// https://github.com/tootsuite/mastodon/blob/c6904c0d3766a2ea8a81ab025c127169ecb51373/app/models/media_attachment.rb#L32 +private const val MEDIA_DESCRIPTION_CHARACTER_LIMIT = 1500 fun T.makeCaptionDialog(existingDescription: String?, previewUri: Uri, diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt index 4a3c62af..563ca39a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt @@ -22,16 +22,9 @@ import android.content.Context import android.graphics.drawable.Drawable import android.net.Uri import android.os.Bundle -import android.util.Log import android.view.* -import android.widget.TextView -import androidx.exifinterface.media.ExifInterface import com.bumptech.glide.Glide -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.CustomTarget -import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.transition.Transition import com.github.piasy.biv.BigImageViewer import com.github.piasy.biv.loader.ImageLoader @@ -69,15 +62,19 @@ class ViewImageFragment : ViewMediaFragment() { private var previewUri = Uri.EMPTY private var showingPreview = false - override lateinit var descriptionView: TextView override fun onAttach(context: Context) { super.onAttach(context) photoActionsListener = context as PhotoActionsListener } - override fun setupMediaView(url: String, previewUrl: String?) { - descriptionView = mediaDescription + override fun setupMediaView(url: String, + previewUrl: String?, + description: String?, + showingDescription: Boolean) { photoView.transitionName = url + mediaDescription.text = description + captionSheet.visible(showingDescription) + startedTransition = false uri = Uri.parse(url) if(previewUrl != null && !previewUrl.equals(url)) { @@ -91,8 +88,6 @@ class ViewImageFragment : ViewMediaFragment() { return inflater.inflate(R.layout.fragment_view_image, container, false) } - private lateinit var gestureDetector : GestureDetector - private val imageOnTouchListener = object : View.OnTouchListener { private var lastY = 0.0f private var swipeStartedWithOneFinger = false @@ -100,7 +95,6 @@ class ViewImageFragment : ViewMediaFragment() { override fun onTouch(v: View, event: MotionEvent): Boolean { // This part is for scaling/translating on vertical move. // We use raw coordinates to get the correct ones during scaling - gestureDetector.onTouchEvent(event) if(event.pointerCount != 1) { onGestureEnd() @@ -143,13 +137,6 @@ class ViewImageFragment : ViewMediaFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - gestureDetector = GestureDetector(requireContext(), object : GestureDetector.SimpleOnGestureListener() { - override fun onSingleTapConfirmed(e: MotionEvent?): Boolean { - onMediaTap() - return true - } - }) - photoView.setImageLoaderCallback(imageLoaderCallback) photoView.setImageViewFactory(GlideImageViewFactory()) @@ -190,10 +177,10 @@ class ViewImageFragment : ViewMediaFragment() { } isDescriptionVisible = showingDescription && visible val alpha = if (isDescriptionVisible) 1.0f else 0.0f - descriptionView.animate().alpha(alpha) + captionSheet.animate().alpha(alpha) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { - descriptionView.visible(isDescriptionVisible) + captionSheet.visible(isDescriptionVisible) animation.removeListener(this) } }) diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt index 5a50fd18..9b51f285 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt @@ -17,18 +17,20 @@ package com.keylesspalace.tusky.fragment import android.os.Bundle import android.text.TextUtils -import android.widget.TextView - import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.entity.Attachment -import com.keylesspalace.tusky.util.visible abstract class ViewMediaFragment : BaseFragment() { private var toolbarVisibiltyDisposable: Function0? = null - abstract fun setupMediaView(url: String, previewUrl: String?) + abstract fun setupMediaView( + url: String, + previewUrl: String?, + description: String?, + showingDescription: Boolean + ) + abstract fun onToolbarVisibilityChange(visible: Boolean) - abstract val descriptionView: TextView protected var showingDescription = false protected var isDescriptionVisible = false @@ -36,6 +38,7 @@ abstract class ViewMediaFragment : BaseFragment() { companion object { @JvmStatic protected val ARG_START_POSTPONED_TRANSITION = "startPostponedTransition" + @JvmStatic protected val ARG_ATTACHMENT = "attach" @JvmStatic @@ -74,13 +77,10 @@ abstract class ViewMediaFragment : BaseFragment() { protected fun finalizeViewSetup(url: String, previewUrl: String?, description: String?) { val mediaActivity = activity as ViewMediaActivity - setupMediaView(url, previewUrl) - descriptionView.text = description ?: "" showingDescription = !TextUtils.isEmpty(description) isDescriptionVisible = showingDescription - - descriptionView.visible(showingDescription && mediaActivity.isToolbarVisible) + setupMediaView(url, previewUrl, description, showingDescription && mediaActivity.isToolbarVisible) toolbarVisibiltyDisposable = (activity as ViewMediaActivity) .addToolbarVisibilityListener { isVisible -> diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt index 0b3a23a7..6c0c73d3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt @@ -26,7 +26,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.MediaController -import android.widget.TextView import com.keylesspalace.tusky.R import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.entity.Attachment @@ -47,7 +46,6 @@ class ViewVideoFragment : ViewMediaFragment() { } private lateinit var mediaActivity: ViewMediaActivity private val TOOLBAR_HIDE_DELAY_MS = 3000L - override lateinit var descriptionView : TextView private lateinit var mediaController : MediaController private var isAudio = false @@ -71,8 +69,14 @@ class ViewVideoFragment : ViewMediaFragment() { } @SuppressLint("ClickableViewAccessibility") - override fun setupMediaView(url: String, previewUrl: String?) { - descriptionView = mediaDescription + override fun setupMediaView( + url: String, + previewUrl: String?, + description: String?, + showingDescription: Boolean + ) { + mediaDescription.text = description + mediaDescription.visible(showingDescription) videoView.transitionName = url videoView.setVideoPath(url) @@ -178,14 +182,14 @@ class ViewVideoFragment : ViewMediaFragment() { val alpha = if (isDescriptionVisible) 1.0f else 0.0f if (isDescriptionVisible) { // If to be visible, need to make visible immediately and animate alpha - descriptionView.alpha = 0.0f - descriptionView.visible(isDescriptionVisible) + mediaDescription.alpha = 0.0f + mediaDescription.visible(isDescriptionVisible) } - descriptionView.animate().alpha(alpha) + mediaDescription.animate().alpha(alpha) .setListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { - descriptionView.visible(isDescriptionVisible) + mediaDescription.visible(isDescriptionVisible) animation.removeListener(this) } }) diff --git a/app/src/main/res/drawable/description_bg_expanded.xml b/app/src/main/res/drawable/description_bg_expanded.xml new file mode 100644 index 00000000..db2eebde --- /dev/null +++ b/app/src/main/res/drawable/description_bg_expanded.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_drag_indicator_horiz_24dp.xml b/app/src/main/res/drawable/ic_drag_indicator_horiz_24dp.xml new file mode 100644 index 00000000..eb611224 --- /dev/null +++ b/app/src/main/res/drawable/ic_drag_indicator_horiz_24dp.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_view_image.xml b/app/src/main/res/layout/fragment_view_image.xml index 1144cb70..caec9eec 100644 --- a/app/src/main/res/layout/fragment_view_image.xml +++ b/app/src/main/res/layout/fragment_view_image.xml @@ -26,18 +26,56 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + + + + + + + + + + + + + \ No newline at end of file