use Picasso as a decoder for fullscreen SubsamplingScaleImageView images

This commit is contained in:
tibbi 2018-08-06 23:59:49 +02:00
parent 4ea5e34b0f
commit 23dd5baaa4
7 changed files with 80 additions and 58 deletions

View file

@ -58,6 +58,7 @@ dependencies {
implementation 'com.google.vr:sdk-panowidget:1.150.0'
implementation 'org.apache.sanselan:sanselan:0.97-incubator'
implementation 'info.androidhive:imagefilters:1.0.7'
implementation 'com.squareup.picasso:picasso:2.71828'
kapt "android.arch.persistence.room:compiler:1.1.1"
implementation "android.arch.persistence.room:runtime:1.1.1"

View file

@ -1,3 +1,9 @@
-keep class com.simplemobiletools.** { *; }
-dontwarn com.simplemobiletools.**
-dontwarn org.apache.**
# Picasso
-dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
-dontwarn org.codehaus.mojo.animal_sniffer.*
-dontwarn okhttp3.internal.platform.ConscryptPlatform

View file

@ -77,7 +77,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
companion object {
var screenWidth = 0
var screenHeight = 0
var wasDecodedByGlide = false
}
override fun onCreate(savedInstanceState: Bundle?) {

View file

@ -61,7 +61,7 @@ fun Activity.launchCamera() {
fun SimpleActivity.launchAbout() {
val licenses = LICENSE_GLIDE or LICENSE_CROPPER or LICENSE_MULTISELECT or LICENSE_RTL or LICENSE_SUBSAMPLING or LICENSE_PATTERN or
LICENSE_REPRINT or LICENSE_GIF_DRAWABLE or LICENSE_PHOTOVIEW or LICENSE_EXOPLAYER or LICENSE_PANORAMA_VIEW or LICENSE_SANSELAN or LICENSE_FILTERS
LICENSE_REPRINT or LICENSE_GIF_DRAWABLE or LICENSE_PHOTOVIEW or LICENSE_PICASSO or LICENSE_EXOPLAYER or LICENSE_PANORAMA_VIEW or LICENSE_SANSELAN or LICENSE_FILTERS
val faqItems = arrayListOf(
FAQItem(R.string.faq_5_title_commons, R.string.faq_5_text_commons),

View file

@ -9,20 +9,14 @@ import android.graphics.Matrix
import android.graphics.drawable.ColorDrawable
import android.media.ExifInterface.*
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.simplemobiletools.commons.extensions.*
@ -33,11 +27,10 @@ import com.simplemobiletools.gallery.activities.PanoramaActivity
import com.simplemobiletools.gallery.activities.PhotoActivity
import com.simplemobiletools.gallery.activities.ViewPagerActivity
import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.GlideRotateTransformation
import com.simplemobiletools.gallery.helpers.MEDIUM
import com.simplemobiletools.gallery.helpers.PATH
import com.simplemobiletools.gallery.helpers.ROTATE_BY_ASPECT_RATIO
import com.simplemobiletools.gallery.helpers.*
import com.simplemobiletools.gallery.models.Medium
import com.squareup.picasso.Callback
import com.squareup.picasso.Picasso
import it.sephiroth.android.library.exif2.ExifInterface
import kotlinx.android.synthetic.main.pager_photo_item.view.*
import org.apache.sanselan.common.byteSources.ByteSourceInputStream
@ -48,12 +41,11 @@ import java.io.FileOutputStream
class PhotoFragment : ViewPagerFragment() {
private val DEFAULT_DOUBLE_TAP_ZOOM = 2f
private val ZOOMABLE_VIEW_LOAD_DELAY = 1000L
private val ZOOMABLE_VIEW_LOAD_DELAY = 500L
private var isFragmentVisible = false
private var isFullscreen = false
private var wasInit = false
private var useHalfResolution = false
private var isPanorama = false
private var imageOrientation = -1
private var gifDrawable: GifDrawable? = null
@ -245,48 +237,19 @@ class PhotoFragment : ViewPagerFragment() {
private fun loadBitmap(degrees: Int = 0) {
if (degrees == 0) {
var targetWidth = if (ViewPagerActivity.screenWidth == 0) Target.SIZE_ORIGINAL else ViewPagerActivity.screenWidth
var targetHeight = if (ViewPagerActivity.screenHeight == 0) Target.SIZE_ORIGINAL else ViewPagerActivity.screenHeight
if (useHalfResolution) {
targetWidth /= 2
targetHeight /= 2
}
if (imageOrientation == ORIENTATION_ROTATE_90) {
targetWidth = targetHeight
targetHeight = Target.SIZE_ORIGINAL
}
val options = RequestOptions()
.signature(medium.path.getFileSignature())
.format(DecodeFormat.PREFER_ARGB_8888)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.override(targetWidth, targetHeight)
Glide.with(this)
.asBitmap()
.load(getPathToLoad(medium))
.apply(options)
.listener(object : RequestListener<Bitmap> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Bitmap>?, isFirstResource: Boolean): Boolean {
if (!useHalfResolution && e?.rootCauses?.firstOrNull() is OutOfMemoryError) {
useHalfResolution = true
Handler().post {
if (activity?.isActivityDestroyed() == false) {
loadBitmap(degrees)
}
}
}
return false
}
override fun onResourceReady(resource: Bitmap?, model: Any?, target: Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
Picasso.get()
.load(File(medium.path))
.centerInside()
.fit()
.into(view.photo_view, object : Callback {
override fun onSuccess() {
if (isFragmentVisible) {
scheduleZoomableView()
}
return false
}
}).into(view.photo_view)
override fun onError(e: Exception) {}
})
} else {
val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.NONE)
@ -311,23 +274,24 @@ class PhotoFragment : ViewPagerFragment() {
private fun scheduleZoomableView() {
loadZoomableViewHandler.removeCallbacksAndMessages(null)
loadZoomableViewHandler.postDelayed({
if (isFragmentVisible && !context!!.config.replaceZoomableImages && medium.isImage() && view.subsampling_view.isGone()) {
if (isFragmentVisible && medium.isImage() && view.subsampling_view.isGone()) {
addZoomableView()
}
}, ZOOMABLE_VIEW_LOAD_DELAY)
}
private fun addZoomableView() {
ViewPagerActivity.wasDecodedByGlide = false
val rotation = degreesForRotation(imageOrientation)
view.subsampling_view.apply {
setBitmapDecoderFactory { PicassoDecoder(medium.path, Picasso.get(), rotation) }
setRegionDecoderFactory { PicassoRegionDecoder() }
maxScale = 10f
beVisible()
isQuickScaleEnabled = context.config.oneFingerZoom
setResetScaleOnSizeChange(context.config.screenRotation != ROTATE_BY_ASPECT_RATIO)
setImage(ImageSource.uri(getPathToLoad(medium)))
orientation = if (imageOrientation == -1) SubsamplingScaleImageView.ORIENTATION_USE_EXIF else degreesForRotation(imageOrientation)
orientation = rotation
setEagerLoadingEnabled(false)
setExecutor(AsyncTask.SERIAL_EXECUTOR)
setOnImageEventListener(object : SubsamplingScaleImageView.OnImageEventListener {
override fun onImageLoaded() {
}
@ -400,8 +364,6 @@ class PhotoFragment : ViewPagerFragment() {
return if (context == null || bitmapAspectRatio == screenAspectRatio) {
DEFAULT_DOUBLE_TAP_ZOOM
} else if (ViewPagerActivity.wasDecodedByGlide) {
1f
} else if (context!!.portrait && bitmapAspectRatio <= screenAspectRatio) {
ViewPagerActivity.screenHeight / height.toFloat()
} else if (context!!.portrait && bitmapAspectRatio > screenAspectRatio) {

View file

@ -0,0 +1,21 @@
package com.simplemobiletools.gallery.helpers
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder
import com.squareup.picasso.MemoryPolicy
import com.squareup.picasso.Picasso
class PicassoDecoder(val tag: String, val picasso: Picasso, val degrees: Int) : ImageDecoder {
override fun decode(context: Context, uri: Uri): Bitmap {
return picasso
.load(uri)
.tag(tag)
.config(Bitmap.Config.ARGB_8888)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.rotate(-degrees.toFloat())
.get()
}
}

View file

@ -0,0 +1,33 @@
package com.simplemobiletools.gallery.helpers
import android.content.Context
import android.graphics.*
import android.net.Uri
import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder
class PicassoRegionDecoder : ImageRegionDecoder {
private var decoder: BitmapRegionDecoder? = null
private val decoderLock = Any()
override fun init(context: Context, uri: Uri): Point {
val inputStream = context.contentResolver.openInputStream(uri)
this.decoder = BitmapRegionDecoder.newInstance(inputStream, false)
return Point(this.decoder!!.width, this.decoder!!.height)
}
override fun decodeRegion(rect: Rect, sampleSize: Int): Bitmap {
synchronized(this.decoderLock) {
val options = BitmapFactory.Options()
options.inSampleSize = sampleSize
options.inPreferredConfig = Bitmap.Config.ARGB_8888
val bitmap = this.decoder!!.decodeRegion(rect, options)
return bitmap ?: throw RuntimeException("Region decoder returned null bitmap - image format may not be supported")
}
}
override fun isReady() = this.decoder != null && !this.decoder!!.isRecycled
override fun recycle() {
this.decoder!!.recycle()
}
}