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 'com.google.vr:sdk-panowidget:1.150.0'
implementation 'org.apache.sanselan:sanselan:0.97-incubator' implementation 'org.apache.sanselan:sanselan:0.97-incubator'
implementation 'info.androidhive:imagefilters:1.0.7' implementation 'info.androidhive:imagefilters:1.0.7'
implementation 'com.squareup.picasso:picasso:2.71828'
kapt "android.arch.persistence.room:compiler:1.1.1" kapt "android.arch.persistence.room:compiler:1.1.1"
implementation "android.arch.persistence.room:runtime:1.1.1" implementation "android.arch.persistence.room:runtime:1.1.1"

View file

@ -1,3 +1,9 @@
-keep class com.simplemobiletools.** { *; } -keep class com.simplemobiletools.** { *; }
-dontwarn com.simplemobiletools.** -dontwarn com.simplemobiletools.**
-dontwarn org.apache.** -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 { companion object {
var screenWidth = 0 var screenWidth = 0
var screenHeight = 0 var screenHeight = 0
var wasDecodedByGlide = false
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

View file

@ -61,7 +61,7 @@ fun Activity.launchCamera() {
fun SimpleActivity.launchAbout() { fun SimpleActivity.launchAbout() {
val licenses = LICENSE_GLIDE or LICENSE_CROPPER or LICENSE_MULTISELECT or LICENSE_RTL or LICENSE_SUBSAMPLING or LICENSE_PATTERN or 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( val faqItems = arrayListOf(
FAQItem(R.string.faq_5_title_commons, R.string.faq_5_text_commons), 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.graphics.drawable.ColorDrawable
import android.media.ExifInterface.* import android.media.ExifInterface.*
import android.net.Uri import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
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 com.bumptech.glide.Glide 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.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.RequestOptions
import com.bumptech.glide.request.target.Target
import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.simplemobiletools.commons.extensions.* 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.PhotoActivity
import com.simplemobiletools.gallery.activities.ViewPagerActivity import com.simplemobiletools.gallery.activities.ViewPagerActivity
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.GlideRotateTransformation import com.simplemobiletools.gallery.helpers.*
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.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.squareup.picasso.Callback
import com.squareup.picasso.Picasso
import it.sephiroth.android.library.exif2.ExifInterface import it.sephiroth.android.library.exif2.ExifInterface
import kotlinx.android.synthetic.main.pager_photo_item.view.* import kotlinx.android.synthetic.main.pager_photo_item.view.*
import org.apache.sanselan.common.byteSources.ByteSourceInputStream import org.apache.sanselan.common.byteSources.ByteSourceInputStream
@ -48,12 +41,11 @@ import java.io.FileOutputStream
class PhotoFragment : ViewPagerFragment() { class PhotoFragment : ViewPagerFragment() {
private val DEFAULT_DOUBLE_TAP_ZOOM = 2f 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 isFragmentVisible = false
private var isFullscreen = false private var isFullscreen = false
private var wasInit = false private var wasInit = false
private var useHalfResolution = false
private var isPanorama = false private var isPanorama = false
private var imageOrientation = -1 private var imageOrientation = -1
private var gifDrawable: GifDrawable? = null private var gifDrawable: GifDrawable? = null
@ -245,48 +237,19 @@ class PhotoFragment : ViewPagerFragment() {
private fun loadBitmap(degrees: Int = 0) { private fun loadBitmap(degrees: Int = 0) {
if (degrees == 0) { if (degrees == 0) {
var targetWidth = if (ViewPagerActivity.screenWidth == 0) Target.SIZE_ORIGINAL else ViewPagerActivity.screenWidth Picasso.get()
var targetHeight = if (ViewPagerActivity.screenHeight == 0) Target.SIZE_ORIGINAL else ViewPagerActivity.screenHeight .load(File(medium.path))
if (useHalfResolution) { .centerInside()
targetWidth /= 2 .fit()
targetHeight /= 2 .into(view.photo_view, object : Callback {
} override fun onSuccess() {
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 {
if (isFragmentVisible) { if (isFragmentVisible) {
scheduleZoomableView() scheduleZoomableView()
} }
return false
} }
}).into(view.photo_view)
override fun onError(e: Exception) {}
})
} else { } else {
val options = RequestOptions() val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
@ -311,23 +274,24 @@ class PhotoFragment : ViewPagerFragment() {
private fun scheduleZoomableView() { private fun scheduleZoomableView() {
loadZoomableViewHandler.removeCallbacksAndMessages(null) loadZoomableViewHandler.removeCallbacksAndMessages(null)
loadZoomableViewHandler.postDelayed({ loadZoomableViewHandler.postDelayed({
if (isFragmentVisible && !context!!.config.replaceZoomableImages && medium.isImage() && view.subsampling_view.isGone()) { if (isFragmentVisible && medium.isImage() && view.subsampling_view.isGone()) {
addZoomableView() addZoomableView()
} }
}, ZOOMABLE_VIEW_LOAD_DELAY) }, ZOOMABLE_VIEW_LOAD_DELAY)
} }
private fun addZoomableView() { private fun addZoomableView() {
ViewPagerActivity.wasDecodedByGlide = false val rotation = degreesForRotation(imageOrientation)
view.subsampling_view.apply { view.subsampling_view.apply {
setBitmapDecoderFactory { PicassoDecoder(medium.path, Picasso.get(), rotation) }
setRegionDecoderFactory { PicassoRegionDecoder() }
maxScale = 10f maxScale = 10f
beVisible() beVisible()
isQuickScaleEnabled = context.config.oneFingerZoom isQuickScaleEnabled = context.config.oneFingerZoom
setResetScaleOnSizeChange(context.config.screenRotation != ROTATE_BY_ASPECT_RATIO) setResetScaleOnSizeChange(context.config.screenRotation != ROTATE_BY_ASPECT_RATIO)
setImage(ImageSource.uri(getPathToLoad(medium))) setImage(ImageSource.uri(getPathToLoad(medium)))
orientation = if (imageOrientation == -1) SubsamplingScaleImageView.ORIENTATION_USE_EXIF else degreesForRotation(imageOrientation) orientation = rotation
setEagerLoadingEnabled(false) setEagerLoadingEnabled(false)
setExecutor(AsyncTask.SERIAL_EXECUTOR)
setOnImageEventListener(object : SubsamplingScaleImageView.OnImageEventListener { setOnImageEventListener(object : SubsamplingScaleImageView.OnImageEventListener {
override fun onImageLoaded() { override fun onImageLoaded() {
} }
@ -400,8 +364,6 @@ class PhotoFragment : ViewPagerFragment() {
return if (context == null || bitmapAspectRatio == screenAspectRatio) { return if (context == null || bitmapAspectRatio == screenAspectRatio) {
DEFAULT_DOUBLE_TAP_ZOOM DEFAULT_DOUBLE_TAP_ZOOM
} else if (ViewPagerActivity.wasDecodedByGlide) {
1f
} else if (context!!.portrait && bitmapAspectRatio <= screenAspectRatio) { } else if (context!!.portrait && bitmapAspectRatio <= screenAspectRatio) {
ViewPagerActivity.screenHeight / height.toFloat() ViewPagerActivity.screenHeight / height.toFloat()
} else if (context!!.portrait && bitmapAspectRatio > screenAspectRatio) { } 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()
}
}