* Use cached preview as thumbnail in ViewImageFragment, fix #1267 * Use cached preview as thumbnail in ViewImageFragment, fix #1267
This commit is contained in:
parent
70b3ce7487
commit
9805a985b2
7 changed files with 164 additions and 111 deletions
|
@ -30,14 +30,15 @@ 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.Target
|
||||
|
||||
import com.github.chrisbanes.photoview.PhotoViewAttacher
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import kotlinx.android.synthetic.main.activity_view_media.*
|
||||
import kotlinx.android.synthetic.main.fragment_view_image.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class ViewImageFragment : ViewMediaFragment() {
|
||||
interface PhotoActionsListener {
|
||||
|
@ -49,35 +50,35 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
private lateinit var attacher: PhotoViewAttacher
|
||||
private lateinit var photoActionsListener: PhotoActionsListener
|
||||
private lateinit var toolbar: View
|
||||
override lateinit var descriptionView: TextView
|
||||
private var transition = BehaviorSubject.create<Unit>()
|
||||
|
||||
override lateinit var descriptionView: TextView
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
photoActionsListener = context as PhotoActionsListener
|
||||
}
|
||||
|
||||
override fun setupMediaView(url: String) {
|
||||
override fun setupMediaView(url: String, previewUrl: String?) {
|
||||
descriptionView = mediaDescription
|
||||
photoView.transitionName = url
|
||||
attacher = PhotoViewAttacher(photoView)
|
||||
attacher = PhotoViewAttacher(photoView).apply {
|
||||
// Clicking outside the photo closes the viewer.
|
||||
setOnOutsidePhotoTapListener { photoActionsListener.onDismiss() }
|
||||
setOnClickListener { onMediaTap() }
|
||||
|
||||
// Clicking outside the photo closes the viewer.
|
||||
attacher.setOnOutsidePhotoTapListener { photoActionsListener.onDismiss() }
|
||||
|
||||
attacher.setOnClickListener { onMediaTap() }
|
||||
|
||||
/* A vertical swipe motion also closes the viewer. This is especially useful when the photo
|
||||
* mostly fills the screen so clicking outside is difficult. */
|
||||
attacher.setOnSingleFlingListener { _, _, velocityX, velocityY ->
|
||||
var result = false
|
||||
if (Math.abs(velocityY) > Math.abs(velocityX)) {
|
||||
photoActionsListener.onDismiss()
|
||||
result = true
|
||||
/* A vertical swipe motion also closes the viewer. This is especially useful when the photo
|
||||
* mostly fills the screen so clicking outside is difficult. */
|
||||
setOnSingleFlingListener { _, _, velocityX, velocityY ->
|
||||
var result = false
|
||||
if (abs(velocityY) > abs(velocityX)) {
|
||||
photoActionsListener.onDismiss()
|
||||
result = true
|
||||
}
|
||||
result
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
loadImageFromNetwork(url, photoView)
|
||||
loadImageFromNetwork(url, previewUrl, photoView)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
|
@ -103,7 +104,7 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
finalizeViewSetup(url, description)
|
||||
finalizeViewSetup(url, attachment?.previewUrl, description)
|
||||
}
|
||||
|
||||
private fun onMediaTap() {
|
||||
|
@ -131,49 +132,71 @@ class ViewImageFragment : ViewMediaFragment() {
|
|||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun loadImageFromNetwork(url: String, photoView: ImageView) =
|
||||
//Request image from the any cache
|
||||
Glide.with(this)
|
||||
.load(url)
|
||||
.dontAnimate()
|
||||
.onlyRetrieveFromCache(true)
|
||||
.error(
|
||||
//Request image from the network on fail load image from cache
|
||||
Glide.with(this)
|
||||
.load(url)
|
||||
.centerInside()
|
||||
.addListener(ImageRequestListener(false))
|
||||
)
|
||||
.centerInside()
|
||||
.addListener(ImageRequestListener(true))
|
||||
.into(photoView)
|
||||
|
||||
private fun loadImageFromNetwork(url: String, previewUrl: String?, photoView: ImageView) {
|
||||
val glide = Glide.with(this)
|
||||
// Request image from the any cache
|
||||
glide
|
||||
.load(url)
|
||||
.dontAnimate()
|
||||
.onlyRetrieveFromCache(true)
|
||||
.let {
|
||||
if (previewUrl != null)
|
||||
it.thumbnail(glide
|
||||
.load(previewUrl)
|
||||
.dontAnimate()
|
||||
.onlyRetrieveFromCache(true)
|
||||
.centerInside()
|
||||
.addListener(ImageRequestListener(true, isThumnailRequest = true)))
|
||||
else it
|
||||
}
|
||||
//Request image from the network on fail load image from cache
|
||||
.error(glide.load(url)
|
||||
.centerInside()
|
||||
.addListener(ImageRequestListener(false, isThumnailRequest = false))
|
||||
)
|
||||
.centerInside()
|
||||
.addListener(ImageRequestListener(true, isThumnailRequest = false))
|
||||
.into(photoView)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isCacheRequest - is this listener for request image from cache or from the network
|
||||
*/
|
||||
private inner class ImageRequestListener(private val isCacheRequest: Boolean) : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
|
||||
if (isCacheRequest) //Complete the transition on failed image from cache
|
||||
completeTransition()
|
||||
else
|
||||
progressBar?.hide() //Hide progress bar only on fail request from internet
|
||||
private inner class ImageRequestListener(
|
||||
private val isCacheRequest: Boolean,
|
||||
private val isThumnailRequest: Boolean) : RequestListener<Drawable> {
|
||||
|
||||
override fun onLoadFailed(e: GlideException?, model: Any, target: Target<Drawable>,
|
||||
isFirstResource: Boolean): Boolean {
|
||||
// If cache for full image failed, complete transition
|
||||
if (isCacheRequest && !isThumnailRequest) photoActionsListener.onBringUp()
|
||||
// Hide progress bar only on fail request from internet
|
||||
if (!isCacheRequest) progressBar?.hide()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
|
||||
progressBar?.hide() //Always hide the progress bar on success
|
||||
resource?.let {
|
||||
target?.onResourceReady(resource, null)
|
||||
if (isCacheRequest) completeTransition() //Complete transition on cache request only, because transition already completed on Network request
|
||||
override fun onResourceReady(resource: Drawable, model: Any, target: Target<Drawable>,
|
||||
dataSource: DataSource, isFirstResource: Boolean): Boolean {
|
||||
progressBar?.hide() // Always hide the progress bar on success
|
||||
if (isThumnailRequest) {
|
||||
photoView.post {
|
||||
target.onResourceReady(resource, null)
|
||||
photoActionsListener.onBringUp()
|
||||
}
|
||||
} else {
|
||||
transition
|
||||
.take(1)
|
||||
.subscribe {
|
||||
target.onResourceReady(resource, null)
|
||||
photoActionsListener.onBringUp()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun completeTransition() {
|
||||
attacher.update()
|
||||
photoActionsListener.onBringUp()
|
||||
override fun onTransitionEnd() {
|
||||
this.transition.onNext(Unit)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,25 +18,29 @@ package com.keylesspalace.tusky.fragment
|
|||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.widget.TextView
|
||||
import com.keylesspalace.tusky.SharedElementTransitionListener
|
||||
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
|
||||
abstract class ViewMediaFragment : BaseFragment() {
|
||||
abstract class ViewMediaFragment : BaseFragment(), SharedElementTransitionListener {
|
||||
private var toolbarVisibiltyDisposable: Function0<Boolean>? = null
|
||||
|
||||
abstract fun setupMediaView(url: String)
|
||||
abstract fun setupMediaView(url: String, previewUrl: String?)
|
||||
abstract fun onToolbarVisibilityChange(visible: Boolean)
|
||||
abstract val descriptionView : TextView
|
||||
abstract val descriptionView: TextView
|
||||
|
||||
protected var showingDescription = false
|
||||
protected var isDescriptionVisible = false
|
||||
|
||||
companion object {
|
||||
@JvmStatic protected val ARG_START_POSTPONED_TRANSITION = "startPostponedTransition"
|
||||
@JvmStatic protected val ARG_ATTACHMENT = "attach"
|
||||
@JvmStatic protected val ARG_AVATAR_URL = "avatarUrl"
|
||||
@JvmStatic
|
||||
protected val ARG_START_POSTPONED_TRANSITION = "startPostponedTransition"
|
||||
@JvmStatic
|
||||
protected val ARG_ATTACHMENT = "attach"
|
||||
@JvmStatic
|
||||
protected val ARG_AVATAR_URL = "avatarUrl"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(attachment: Attachment, shouldStartPostponedTransition: Boolean): ViewMediaFragment {
|
||||
|
@ -66,21 +70,20 @@ abstract class ViewMediaFragment : BaseFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
protected fun finalizeViewSetup(url: String, description: String?) {
|
||||
protected fun finalizeViewSetup(url: String, previewUrl: String?, description: String?) {
|
||||
val mediaActivity = activity as ViewMediaActivity
|
||||
setupMediaView(url)
|
||||
setupMediaView(url, previewUrl)
|
||||
|
||||
descriptionView.text = description ?: ""
|
||||
showingDescription = !TextUtils.isEmpty(description)
|
||||
isDescriptionVisible = showingDescription
|
||||
|
||||
descriptionView.visible(showingDescription && mediaActivity.isToolbarVisible())
|
||||
descriptionView.visible(showingDescription && mediaActivity.isToolbarVisible)
|
||||
|
||||
toolbarVisibiltyDisposable = (activity as ViewMediaActivity).addToolbarVisibilityListener(object: ViewMediaActivity.ToolbarVisibilityListener {
|
||||
override fun onToolbarVisiblityChanged(isVisible: Boolean) {
|
||||
onToolbarVisibilityChange(isVisible)
|
||||
}
|
||||
})
|
||||
toolbarVisibiltyDisposable = (activity as ViewMediaActivity)
|
||||
.addToolbarVisibilityListener { isVisible ->
|
||||
onToolbarVisibilityChange(isVisible)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
|
|
@ -26,7 +26,6 @@ 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
|
||||
|
@ -56,7 +55,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
|
||||
if (isVisibleToUser) {
|
||||
if (mediaActivity.isToolbarVisible()) {
|
||||
if (mediaActivity.isToolbarVisible) {
|
||||
handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS)
|
||||
}
|
||||
videoPlayer.start()
|
||||
|
@ -68,7 +67,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun setupMediaView(url: String) {
|
||||
override fun setupMediaView(url: String, previewUrl: String?) {
|
||||
descriptionView = mediaDescription
|
||||
val videoView = videoPlayer
|
||||
videoView.transitionName = url
|
||||
|
@ -114,7 +113,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
throw IllegalArgumentException("attachment has to be set")
|
||||
}
|
||||
url = attachment.url
|
||||
finalizeViewSetup(url, attachment.description)
|
||||
finalizeViewSetup(url, attachment.previewUrl, attachment.description)
|
||||
}
|
||||
|
||||
override fun onToolbarVisibilityChange(visible: Boolean) {
|
||||
|
@ -139,4 +138,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
handler.removeCallbacks(hideToolbar)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTransitionEnd() {
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue