@ -25,11 +25,15 @@ 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
import com.github.piasy.biv.view.GlideImageViewFactory
import com.keylesspalace.tusky.R
@ -42,9 +46,10 @@ import kotlinx.android.synthetic.main.fragment_view_image.*
import java.io.File
import java.lang.Exception
import kotlin.math.abs
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
class ViewImageFragment : ViewMediaFragment ( ) , ImageLoader . Callback , View . OnTouchListener {
class ViewImageFragment : ViewMediaFragment ( ) {
interface PhotoActionsListener {
fun onBringUp ( )
fun onDismiss ( )
@ -60,6 +65,10 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
@Volatile
private var startedTransition = false
private var uri = Uri . EMPTY
private var previewUri = Uri . EMPTY
private var showingPreview = false
override lateinit var descriptionView : TextView
override fun onAttach ( context : Context ) {
super . onAttach ( context )
@ -70,7 +79,11 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
descriptionView = mediaDescription
photoView . transitionName = url
startedTransition = false
loadImageFromNetwork ( url , previewUrl )
uri = Uri . parse ( url )
if ( previewUrl != null && ! previewUrl . equals ( url ) ) {
previewUri = Uri . parse ( previewUrl )
}
loadImageFromNetwork ( )
}
override fun onCreateView ( inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ? ) : View {
@ -78,51 +91,53 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
return inflater . inflate ( R . layout . fragment _view _image , container , false )
}
private var lastY = 0.0f
private var swipeStartedWithOneFinger = false
private lateinit var gestureDetector : GestureDetector
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 ) {
swipeStartedWithOneFinger = false
return false
}
private val imageOnTouchListener = object : View . OnTouchListener {
private var lastY = 0.0f
private var swipeStartedWithOneFinger = false
var result = false
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 )
when ( event . action ) {
MotionEvent . ACTION _DOWN -> {
swipeStartedWithOneFinger = true
lastY = event . rawY
}
MotionEvent . ACTION _UP , MotionEvent . ACTION _CANCEL -> {
if ( event . pointerCount != 1 ) {
onGestureEnd ( )
swipeStartedWithOneFinger = false
return false
}
MotionEvent . ACTION _MOVE -> {
if ( swipeStartedWithOneFinger && photoView . ssiv . scale <= photoView . ssiv . minScale ) {
val diff = event . rawY - lastY
// This code is to prevent transformations during page scrolling
// If we are already translating or we reached the threshold, then transform.
if ( photoView . translationY != 0f || abs ( diff ) > 40 ) {
photoView . translationY += ( diff )
val scale = ( - abs ( photoView . translationY ) / 720 + 1 ) . coerceAtLeast ( 0.5f )
photoView . scaleY = scale
photoView . scaleX = scale
lastY = event . rawY
when ( event . action ) {
MotionEvent . ACTION _DOWN -> {
swipeStartedWithOneFinger = true
lastY = event . rawY
}
MotionEvent . ACTION _UP , MotionEvent . ACTION _CANCEL -> {
onGestureEnd ( )
swipeStartedWithOneFinger = false
}
MotionEvent . ACTION _MOVE -> {
if ( swipeStartedWithOneFinger && photoView . ssiv . scale <= photoView . ssiv . minScale ) {
val diff = event . rawY - lastY
// This code is to prevent transformations during page scrolling
// If we are already translating or we reached the threshold, then transform.
if ( photoView . translationY != 0f || abs ( diff ) > 40 ) {
photoView . translationY += ( diff )
val scale = ( - abs ( photoView . translationY ) / 720 + 1 ) . coerceAtLeast ( 0.5f )
photoView . scaleY = scale
photoView . scaleX = scale
lastY = event . rawY
}
}
result = true
}
}
}
return result
return false
}
}
@SuppressLint ( " ClickableViewAccessibility " )
override fun onViewCreated ( view : View , savedInstanceState : Bundle ? ) {
super . onViewCreated ( view , savedInstanceState )
@ -135,7 +150,7 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
} )
// photoView.setOnTouchListener(this)
photoView . setImageLoaderCallback ( this )
photoView . setImageLoaderCallback ( imageLoaderCallback )
photoView . setImageViewFactory ( GlideImageViewFactory ( ) )
val arguments = this . requireArguments ( )
@ -190,33 +205,97 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
photoView . ssiv ?. recycle ( )
}
private fun loadImageFromNetwork ( url : String , previewUrl : String ? ) {
photoView . showImage ( Uri . parse ( previewUrl ) , Uri . parse ( url ) )
}
private inner class DummyCacheTarget ( val ctx : Context , val requestPreview : Boolean ) : CustomTarget < File > ( ) {
override fun onLoadCleared ( placeholder : Drawable ? ) { }
override fun onLoadFailed ( errorDrawable : Drawable ? ) {
if ( requestPreview ) {
// no preview, no full image in cache, load full image
// forget about fancy transition
showingPreview = false
photoView . showImage ( uri )
} else {
// let's start downloading full image that we supposedly don't have
BigImageViewer . prefetch ( uri )
// meanwhile poke cache about preview image
Glide . with ( ctx ) . asFile ( )
. load ( previewUri )
. dontAnimate ( )
. onlyRetrieveFromCache ( true )
. into ( DummyCacheTarget ( ctx , true ) )
}
}
override fun onSuccess ( image : File ? ) {
progressBar ?. hide ( ) // Always hide the progress bar on success
photoActionsListener . onBringUp ( )
photoView . ssiv ?. setOnTouchListener ( this )
override fun onResourceReady ( resource : File , transition : Transition < in File > ? ) {
showingPreview = requestPreview
if ( requestPreview ) {
// have preview cached but not full image
photoView . showImage ( previewUri , uri , true )
} else {
photoView . showImage ( uri )
}
}
}
override fun onFail ( error : Exception ? ) {
progressBar ?. hide ( )
photoActionsListener . onBringUp ( )
private fun loadImageFromNetwork ( ) {
if ( previewUri != Uri . EMPTY ) {
// check if we have full image in the cache, if yes, use it
// if not, look for preview in cache and use it if available
// if not, load full image anyway
Glide . with ( this ) . asFile ( )
. load ( uri )
. onlyRetrieveFromCache ( true )
. dontAnimate ( )
. into ( DummyCacheTarget ( context !! , false ) )
} else {
// no need in cache lookup, just load full image
showingPreview = false
photoView . showImage ( uri )
}
}
override fun onCacheHit ( imageType : Int , image : File ? ) {
override fun onTransitionEnd ( ) {
// if we had preview, load full image, as transition has ended
if ( showingPreview ) {
showingPreview = false
photoView . loadMainImageNow ( )
}
}
override fun onCacheMiss ( imageType : Int , image : File ? ) {
}
private val imageLoaderCallback = object : ImageLoader . Callback {
override fun onSuccess ( image : File ? ) {
if ( ! showingPreview ) {
progressBar ?. hide ( )
photoView . ssiv ?. let {
it . orientation = SubsamplingScaleImageView . ORIENTATION _USE _EXIF
it . setOnTouchListener ( imageOnTouchListener )
}
}
}
override fun onFinish ( ) {
}
override fun onFail ( error : Exception ? ) {
progressBar ?. hide ( )
}
override fun onProgress ( progress : Int ) {
}
override fun onCacheHit ( imageType : Int , image : File ? ) {
// image is here, bring up the activity!
photoActionsListener . onBringUp ( )
}
override fun onTransitionEnd ( ) {
override fun onStart ( ) {
// cache miss but image is downloading, bring up the activity
photoActionsListener . onBringUp ( )
}
override fun onCacheMiss ( imageType : Int , image : File ? ) {
// this callback is useless because it's called after
// image is downloaded or pulled from cache
// so in case of cache miss, onStart is used
}
override fun onFinish ( ) { }
override fun onProgress ( progress : Int ) {
// TODO: make use of it :)
}
}
}