@ -25,11 +25,15 @@ import android.os.Bundle
import android.util.Log
import android.util.Log
import android.view.*
import android.view.*
import android.widget.TextView
import android.widget.TextView
import androidx.exifinterface.media.ExifInterface
import com.bumptech.glide.Glide
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
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.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.loader.ImageLoader
import com.github.piasy.biv.view.GlideImageViewFactory
import com.github.piasy.biv.view.GlideImageViewFactory
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.R
@ -42,9 +46,10 @@ import kotlinx.android.synthetic.main.fragment_view_image.*
import java.io.File
import java.io.File
import java.lang.Exception
import java.lang.Exception
import kotlin.math.abs
import kotlin.math.abs
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
class ViewImageFragment : ViewMediaFragment ( ) , ImageLoader . Callback , View . OnTouchListener {
class ViewImageFragment : ViewMediaFragment ( ) {
interface PhotoActionsListener {
interface PhotoActionsListener {
fun onBringUp ( )
fun onBringUp ( )
fun onDismiss ( )
fun onDismiss ( )
@ -60,6 +65,10 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
@Volatile
@Volatile
private var startedTransition = false
private var startedTransition = false
private var uri = Uri . EMPTY
private var previewUri = Uri . EMPTY
private var showingPreview = false
override lateinit var descriptionView : TextView
override lateinit var descriptionView : TextView
override fun onAttach ( context : Context ) {
override fun onAttach ( context : Context ) {
super . onAttach ( context )
super . onAttach ( context )
@ -70,7 +79,11 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
descriptionView = mediaDescription
descriptionView = mediaDescription
photoView . transitionName = url
photoView . transitionName = url
startedTransition = false
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 {
override fun onCreateView ( inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ? ) : View {
@ -78,9 +91,11 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
return inflater . inflate ( R . layout . fragment _view _image , container , false )
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 lastY = 0.0f
private var swipeStartedWithOneFinger = false
private var swipeStartedWithOneFinger = false
private lateinit var gestureDetector : GestureDetector
override fun onTouch ( v : View , event : MotionEvent ) : Boolean {
override fun onTouch ( v : View , event : MotionEvent ) : Boolean {
// This part is for scaling/translating on vertical move.
// This part is for scaling/translating on vertical move.
@ -88,12 +103,11 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
gestureDetector . onTouchEvent ( event )
gestureDetector . onTouchEvent ( event )
if ( event . pointerCount != 1 ) {
if ( event . pointerCount != 1 ) {
onGestureEnd ( )
swipeStartedWithOneFinger = false
swipeStartedWithOneFinger = false
return false
return false
}
}
var result = false
when ( event . action ) {
when ( event . action ) {
MotionEvent . ACTION _DOWN -> {
MotionEvent . ACTION _DOWN -> {
swipeStartedWithOneFinger = true
swipeStartedWithOneFinger = true
@ -115,14 +129,15 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
photoView . scaleX = scale
photoView . scaleX = scale
lastY = event . rawY
lastY = event . rawY
}
}
result = true
}
}
}
}
}
}
return result
return false
}
}
}
@SuppressLint ( " ClickableViewAccessibility " )
@SuppressLint ( " ClickableViewAccessibility " )
override fun onViewCreated ( view : View , savedInstanceState : Bundle ? ) {
override fun onViewCreated ( view : View , savedInstanceState : Bundle ? ) {
super . onViewCreated ( view , savedInstanceState )
super . onViewCreated ( view , savedInstanceState )
@ -135,7 +150,7 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
} )
} )
// photoView.setOnTouchListener(this)
// photoView.setOnTouchListener(this)
photoView . setImageLoaderCallback ( this )
photoView . setImageLoaderCallback ( imageLoaderCallback )
photoView . setImageViewFactory ( GlideImageViewFactory ( ) )
photoView . setImageViewFactory ( GlideImageViewFactory ( ) )
val arguments = this . requireArguments ( )
val arguments = this . requireArguments ( )
@ -190,33 +205,97 @@ class ViewImageFragment : ViewMediaFragment(), ImageLoader.Callback, View.OnTouc
photoView . ssiv ?. recycle ( )
photoView . ssiv ?. recycle ( )
}
}
private fun loadImageFromNetwork ( url : String , previewUrl : String ? ) {
private inner class DummyCacheTarget ( val ctx : Context , val requestPreview : Boolean ) : CustomTarget < File > ( ) {
photoView . showImage ( Uri . parse ( previewUrl ) , Uri . parse ( url ) )
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 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 )
}
}
}
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 onTransitionEnd ( ) {
// if we had preview, load full image, as transition has ended
if ( showingPreview ) {
showingPreview = false
photoView . loadMainImageNow ( )
}
}
private val imageLoaderCallback = object : ImageLoader . Callback {
override fun onSuccess ( image : File ? ) {
override fun onSuccess ( image : File ? ) {
progressBar ?. hide ( ) // Always hide the progress bar on success
if ( ! showingPreview ) {
photoActionsListener . onBringUp ( )
progressBar ?. hide ( )
photoView . ssiv ?. setOnTouchListener ( this )
photoView . ssiv ?. let {
it . orientation = SubsamplingScaleImageView . ORIENTATION _USE _EXIF
it . setOnTouchListener ( imageOnTouchListener )
}
}
}
}
override fun onFail ( error : Exception ? ) {
override fun onFail ( error : Exception ? ) {
progressBar ?. hide ( )
progressBar ?. hide ( )
photoActionsListener . onBringUp ( )
}
}
override fun onCacheHit ( imageType : Int , image : File ? ) {
override fun onCacheHit ( imageType : Int , image : File ? ) {
// image is here, bring up the activity!
photoActionsListener . onBringUp ( )
}
}
override fun onCacheMiss ( imageType : Int , image : File ? ) {
override fun onStart ( ) {
// cache miss but image is downloading, bring up the activity
photoActionsListener . onBringUp ( )
}
}
override fun onFinish ( ) {
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 ) {
override fun onProgress ( progress : Int ) {
// TODO: make use of it :)
}
}
override fun onTransitionEnd ( ) {
}
}
}
}