use Picasso as a decoder for fullscreen SubsamplingScaleImageView images
This commit is contained in:
parent
4ea5e34b0f
commit
23dd5baaa4
7 changed files with 80 additions and 58 deletions
|
@ -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"
|
||||||
|
|
6
app/proguard-rules.pro
vendored
6
app/proguard-rules.pro
vendored
|
@ -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
|
||||||
|
|
|
@ -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?) {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue