Fix media transitions, reduce code duplication between media fragments (#894)

* Fix media transitions, reduce code duplication between media fragments

* Remove redundant helper

* Fix occasional crash when swiping between mixed media

* Hide controls when swiping between media
main
Levi Bard 6 years ago committed by Konrad Pozniak
parent d3bed81a82
commit c05eaae317
  1. 44
      app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt
  2. 18
      app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt
  3. 54
      app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt
  4. 2
      app/src/main/res/layout/fragment_view_image.xml

@ -20,18 +20,17 @@ import android.animation.AnimatorListenerAdapter
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.support.v4.view.ViewCompat import android.support.v4.view.ViewCompat
import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import com.github.chrisbanes.photoview.PhotoViewAttacher import com.github.chrisbanes.photoview.PhotoViewAttacher
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.visible
import com.squareup.picasso.Callback import com.squareup.picasso.Callback
import com.squareup.picasso.NetworkPolicy import com.squareup.picasso.NetworkPolicy
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
@ -48,9 +47,7 @@ class ViewImageFragment : ViewMediaFragment() {
private lateinit var attacher: PhotoViewAttacher private lateinit var attacher: PhotoViewAttacher
private lateinit var photoActionsListener: PhotoActionsListener private lateinit var photoActionsListener: PhotoActionsListener
private lateinit var toolbar: View private lateinit var toolbar: View
override lateinit var descriptionView : TextView
private var showingDescription = false
private var isDescriptionVisible = false
companion object { companion object {
private const val TAG = "ViewImageFragment" private const val TAG = "ViewImageFragment"
@ -62,6 +59,8 @@ class ViewImageFragment : ViewMediaFragment() {
} }
override fun setupMediaView(url: String) { override fun setupMediaView(url: String) {
descriptionView = mediaDescription
ViewCompat.setTransitionName(photoView, url)
attacher = PhotoViewAttacher(photoView) attacher = PhotoViewAttacher(photoView)
// Clicking outside the photo closes the viewer. // Clicking outside the photo closes the viewer.
@ -132,38 +131,21 @@ class ViewImageFragment : ViewMediaFragment() {
val arguments = this.arguments!! val arguments = this.arguments!!
val attachment = arguments.getParcelable<Attachment>(ARG_ATTACHMENT) val attachment = arguments.getParcelable<Attachment>(ARG_ATTACHMENT)
val url: String? val url: String?
var description : String? = null
if (attachment != null) { if (attachment != null) {
url = attachment.url url = attachment.url
description = attachment.description
val description = attachment.description
descriptionView.text = description
showingDescription = !TextUtils.isEmpty(description)
isDescriptionVisible = showingDescription
} else { } else {
url = arguments.getString(ARG_AVATAR_URL) url = arguments.getString(ARG_AVATAR_URL)
if (url == null) { if (url == null) {
throw IllegalArgumentException("attachment or avatar url has to be set") throw IllegalArgumentException("attachment or avatar url has to be set")
} }
showingDescription = false
isDescriptionVisible = false
} }
// Setting visibility without animations so it looks nice when you scroll images finalizeViewSetup(url, description)
if (showingDescription && (activity as ViewMediaActivity).isToolbarVisible()) {
descriptionView.show()
} else {
descriptionView.hide()
} }
setupMediaView(url)
setupToolbarVisibilityListener()
}
private fun onMediaTap() { private fun onMediaTap() {
photoActionsListener.onPhotoTap() photoActionsListener.onPhotoTap()
} }
@ -177,11 +159,7 @@ class ViewImageFragment : ViewMediaFragment() {
descriptionView.animate().alpha(alpha) descriptionView.animate().alpha(alpha)
.setListener(object : AnimatorListenerAdapter() { .setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
if (isDescriptionVisible) { descriptionView.visible(isDescriptionVisible)
descriptionView.show()
} else {
descriptionView.hide()
}
animation.removeListener(this) animation.removeListener(this)
} }
}) })
@ -204,13 +182,13 @@ class ViewImageFragment : ViewMediaFragment() {
} }
override fun onError() { override fun onError() {
progressBar.hide() progressBar?.hide()
} }
}) })
} }
private fun finishLoadingSuccessfully() { private fun finishLoadingSuccessfully() {
progressBar.hide() progressBar?.hide()
attacher.update() attacher.update()
photoActionsListener.onBringUp() photoActionsListener.onBringUp()
} }

@ -16,15 +16,22 @@
package com.keylesspalace.tusky.fragment package com.keylesspalace.tusky.fragment
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils
import android.widget.TextView
import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.visible
abstract class ViewMediaFragment : BaseFragment() { abstract class ViewMediaFragment : BaseFragment() {
private var toolbarVisibiltyDisposable: Function0<Boolean>? = null private var toolbarVisibiltyDisposable: Function0<Boolean>? = null
abstract fun setupMediaView(url: String) abstract fun setupMediaView(url: String)
abstract fun onToolbarVisibilityChange(visible: Boolean) abstract fun onToolbarVisibilityChange(visible: Boolean)
abstract val descriptionView : TextView
protected var showingDescription = false
protected var isDescriptionVisible = false
companion object { companion object {
@JvmStatic protected val ARG_START_POSTPONED_TRANSITION = "startPostponedTransition" @JvmStatic protected val ARG_START_POSTPONED_TRANSITION = "startPostponedTransition"
@ -60,7 +67,16 @@ abstract class ViewMediaFragment : BaseFragment() {
} }
} }
protected fun setupToolbarVisibilityListener() { protected fun finalizeViewSetup(url: String, description: String?) {
val mediaActivity = activity as ViewMediaActivity
setupMediaView(url)
descriptionView.text = description ?: ""
showingDescription = !TextUtils.isEmpty(description)
isDescriptionVisible = showingDescription
descriptionView.visible(showingDescription && mediaActivity.isToolbarVisible())
toolbarVisibiltyDisposable = (activity as ViewMediaActivity).addToolbarVisibilityListener(object: ViewMediaActivity.ToolbarVisibilityListener { toolbarVisibiltyDisposable = (activity as ViewMediaActivity).addToolbarVisibilityListener(object: ViewMediaActivity.ToolbarVisibilityListener {
override fun onToolbarVisiblityChanged(isVisible: Boolean) { override fun onToolbarVisiblityChanged(isVisible: Boolean) {
onToolbarVisibilityChange(isVisible) onToolbarVisibilityChange(isVisible)

@ -22,17 +22,17 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.support.v4.view.ViewCompat import android.support.v4.view.ViewCompat
import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.MediaController import android.widget.MediaController
import android.widget.TextView
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.visible
import kotlinx.android.synthetic.main.activity_view_media.* import kotlinx.android.synthetic.main.activity_view_media.*
import kotlinx.android.synthetic.main.fragment_view_video.* import kotlinx.android.synthetic.main.fragment_view_video.*
@ -46,9 +46,8 @@ class ViewVideoFragment : ViewMediaFragment() {
} }
private lateinit var mediaActivity: ViewMediaActivity private lateinit var mediaActivity: ViewMediaActivity
private val TOOLBAR_HIDE_DELAY_MS = 3000L private val TOOLBAR_HIDE_DELAY_MS = 3000L
override lateinit var descriptionView : TextView
private var showingDescription = false private lateinit var mediaController : MediaController
private var isDescriptionVisible = false
companion object { companion object {
private const val TAG = "ViewVideoFragment" private const val TAG = "ViewVideoFragment"
@ -65,20 +64,23 @@ class ViewVideoFragment : ViewMediaFragment() {
if (mediaActivity.isToolbarVisible()) { if (mediaActivity.isToolbarVisible()) {
handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS) handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS)
} }
videoPlayer?.start() videoPlayer.start()
} else { } else {
handler.removeCallbacks(hideToolbar) handler.removeCallbacks(hideToolbar)
videoPlayer?.pause() videoPlayer.pause()
mediaController.hide()
} }
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun setupMediaView(url: String) { override fun setupMediaView(url: String) {
descriptionView = mediaDescription
val videoView = videoPlayer val videoView = videoPlayer
ViewCompat.setTransitionName(videoView, url)
videoView.setVideoPath(url) videoView.setVideoPath(url)
val controller = MediaController(mediaActivity) mediaController = MediaController(mediaActivity)
controller.setMediaPlayer(videoView) mediaController.setMediaPlayer(videoPlayer)
videoView.setMediaController(controller) videoPlayer.setMediaController(mediaController)
videoView.requestFocus() videoView.requestFocus()
videoView.setOnTouchListener { _, _ -> videoView.setOnTouchListener { _, _ ->
mediaActivity.onPhotoTap() mediaActivity.onPhotoTap()
@ -110,34 +112,14 @@ class ViewVideoFragment : ViewMediaFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val attachment = arguments?.getParcelable<Attachment>(ViewMediaFragment.ARG_ATTACHMENT)
val arguments = this.arguments!!
val attachment = arguments.getParcelable<Attachment>(ViewMediaFragment.ARG_ATTACHMENT)
val url: String val url: String
if (attachment == null) { if (attachment == null) {
throw IllegalArgumentException("attachment has to be set") throw IllegalArgumentException("attachment has to be set")
} }
url = attachment.url url = attachment.url
val description = attachment.description finalizeViewSetup(url, attachment.description)
mediaDescription.text = description
showingDescription = !TextUtils.isEmpty(description)
isDescriptionVisible = showingDescription
// Setting visibility without animations so it looks nice when you scroll media
//noinspection ConstantConditions
if (showingDescription && mediaActivity.isToolbarVisible()) {
mediaDescription.show()
} else {
mediaDescription.hide()
}
ViewCompat.setTransitionName(videoPlayer!!, url)
setupMediaView(url)
setupToolbarVisibilityListener()
} }
override fun onToolbarVisibilityChange(visible: Boolean) { override fun onToolbarVisibilityChange(visible: Boolean) {
@ -147,14 +129,10 @@ class ViewVideoFragment : ViewMediaFragment() {
isDescriptionVisible = showingDescription && visible isDescriptionVisible = showingDescription && visible
val alpha = if (isDescriptionVisible) 1.0f else 0.0f val alpha = if (isDescriptionVisible) 1.0f else 0.0f
mediaDescription.animate().alpha(alpha) descriptionView.animate().alpha(alpha)
.setListener(object : AnimatorListenerAdapter() { .setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
if (isDescriptionVisible) { descriptionView.visible(isDescriptionVisible)
mediaDescription.show()
} else {
mediaDescription.hide()
}
animation.removeListener(this) animation.removeListener(this)
} }
}) })

@ -26,7 +26,7 @@
android:layout_gravity="center" /> android:layout_gravity="center" />
<TextView <TextView
android:id="@+id/descriptionView" android:id="@+id/mediaDescription"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

Loading…
Cancel
Save