2020-07-17 16:35:39 +02:00
|
|
|
package sushi.hardcore.droidfs.file_viewers
|
|
|
|
|
2020-08-25 15:43:47 +02:00
|
|
|
import android.content.res.Configuration
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.graphics.Bitmap
|
2021-10-23 19:40:10 +02:00
|
|
|
import android.graphics.BitmapFactory
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.graphics.Matrix
|
2021-12-20 20:13:39 +01:00
|
|
|
import android.graphics.Point
|
|
|
|
import android.graphics.drawable.Drawable
|
|
|
|
import android.os.Build
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.os.Handler
|
2021-12-20 20:13:39 +01:00
|
|
|
import android.util.Size
|
2020-08-01 16:43:48 +02:00
|
|
|
import android.view.MotionEvent
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.view.View
|
2021-12-20 20:13:39 +01:00
|
|
|
import android.view.WindowInsets
|
2020-09-29 11:14:30 +02:00
|
|
|
import android.view.WindowManager
|
2020-08-25 15:43:47 +02:00
|
|
|
import android.widget.Toast
|
2020-07-18 18:44:53 +02:00
|
|
|
import com.bumptech.glide.Glide
|
2021-12-20 20:13:39 +01:00
|
|
|
import com.bumptech.glide.RequestBuilder
|
2020-07-18 18:44:53 +02:00
|
|
|
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
|
|
|
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
2020-08-01 16:43:48 +02:00
|
|
|
import sushi.hardcore.droidfs.ConstValues
|
2020-07-17 16:35:39 +02:00
|
|
|
import sushi.hardcore.droidfs.R
|
2021-06-11 20:23:54 +02:00
|
|
|
import sushi.hardcore.droidfs.databinding.ActivityImageViewerBinding
|
2021-11-09 11:12:09 +01:00
|
|
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
2020-08-25 15:43:47 +02:00
|
|
|
import sushi.hardcore.droidfs.widgets.ZoomableImageView
|
2020-09-30 16:19:16 +02:00
|
|
|
import java.io.ByteArrayInputStream
|
|
|
|
import java.io.ByteArrayOutputStream
|
2020-08-25 15:43:47 +02:00
|
|
|
import java.io.File
|
2020-07-18 18:44:53 +02:00
|
|
|
import java.security.MessageDigest
|
2020-08-01 16:43:48 +02:00
|
|
|
import kotlin.math.abs
|
2020-07-17 16:35:39 +02:00
|
|
|
|
|
|
|
class ImageViewer: FileViewerActivity() {
|
|
|
|
companion object {
|
|
|
|
private const val hideDelay: Long = 3000
|
2020-08-01 16:43:48 +02:00
|
|
|
private const val MIN_SWIPE_DISTANCE = 150
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
|
2020-09-04 13:30:12 +02:00
|
|
|
private lateinit var fileName: String
|
2021-09-01 20:15:11 +02:00
|
|
|
private lateinit var handler: Handler
|
2021-12-20 20:13:39 +01:00
|
|
|
private var bitmap: Bitmap? = null
|
|
|
|
private var requestBuilder: RequestBuilder<Drawable>? = null
|
2020-08-01 16:43:48 +02:00
|
|
|
private var x1 = 0F
|
|
|
|
private var x2 = 0F
|
2020-08-25 15:43:47 +02:00
|
|
|
private var slideshowActive = false
|
2020-07-18 18:44:53 +02:00
|
|
|
private var rotationAngle: Float = 0F
|
2020-09-30 16:19:16 +02:00
|
|
|
private var rotatedBitmap: Bitmap? = null
|
2020-08-25 15:43:47 +02:00
|
|
|
private val hideUI = Runnable {
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.actionButtons.visibility = View.GONE
|
|
|
|
binding.actionBar.visibility = View.GONE
|
2020-08-25 15:43:47 +02:00
|
|
|
}
|
2020-11-03 13:34:40 +01:00
|
|
|
private val slideshowNext = Runnable {
|
|
|
|
if (slideshowActive){
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageViewer.resetZoomFactor()
|
2020-11-03 13:34:40 +01:00
|
|
|
swipeImage(-1F, true)
|
2020-09-30 16:19:16 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
private lateinit var binding: ActivityImageViewerBinding
|
2021-03-17 21:11:14 +01:00
|
|
|
|
|
|
|
override fun getFileType(): String {
|
|
|
|
return "image"
|
|
|
|
}
|
|
|
|
|
2020-07-17 16:35:39 +02:00
|
|
|
override fun viewFile() {
|
2021-06-11 20:23:54 +02:00
|
|
|
binding = ActivityImageViewerBinding.inflate(layoutInflater)
|
|
|
|
setContentView(binding.root)
|
2021-09-01 20:15:11 +02:00
|
|
|
handler = Handler(mainLooper)
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageViewer.setOnInteractionListener(object : ZoomableImageView.OnInteractionListener {
|
2020-09-04 13:30:12 +02:00
|
|
|
override fun onSingleTap(event: MotionEvent?) {
|
|
|
|
handler.removeCallbacks(hideUI)
|
2021-06-11 20:23:54 +02:00
|
|
|
if (binding.actionButtons.visibility == View.GONE) {
|
|
|
|
binding.actionButtons.visibility = View.VISIBLE
|
|
|
|
binding.actionBar.visibility = View.VISIBLE
|
2020-09-04 13:30:12 +02:00
|
|
|
handler.postDelayed(hideUI, hideDelay)
|
|
|
|
} else {
|
|
|
|
hideUI.run()
|
2020-08-01 16:43:48 +02:00
|
|
|
}
|
2020-09-04 13:30:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onTouch(event: MotionEvent?) {
|
2021-06-11 20:23:54 +02:00
|
|
|
if (!binding.imageViewer.isZoomed) {
|
2020-09-04 13:30:12 +02:00
|
|
|
when (event?.action) {
|
|
|
|
MotionEvent.ACTION_DOWN -> {
|
|
|
|
x1 = event.x
|
|
|
|
}
|
|
|
|
MotionEvent.ACTION_UP -> {
|
|
|
|
x2 = event.x
|
|
|
|
val deltaX = x2 - x1
|
|
|
|
if (abs(deltaX) > MIN_SWIPE_DISTANCE) {
|
2020-09-30 16:19:16 +02:00
|
|
|
askSaveRotation { swipeImage(deltaX) }
|
2020-08-01 16:43:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-04 13:30:12 +02:00
|
|
|
}
|
|
|
|
})
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageDelete.setOnClickListener {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2021-06-07 16:34:50 +02:00
|
|
|
.keepFullScreen()
|
|
|
|
.setTitle(R.string.warning)
|
|
|
|
.setPositiveButton(R.string.ok) { _, _ ->
|
|
|
|
createPlaylist() //be sure the playlist is created before deleting if there is only one image
|
|
|
|
if (gocryptfsVolume.removeFile(filePath)) {
|
|
|
|
playlistNext(true)
|
|
|
|
refreshPlaylist()
|
|
|
|
if (mappedPlaylist.size == 0) { //deleted all images of the playlist
|
|
|
|
goBackToExplorer()
|
|
|
|
} else {
|
|
|
|
loadImage()
|
|
|
|
}
|
|
|
|
} else {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2021-06-07 16:34:50 +02:00
|
|
|
.keepFullScreen()
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(getString(R.string.remove_failed, fileName))
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.setMessage(getString(R.string.single_delete_confirm, fileName))
|
|
|
|
.show()
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageButtonSlideshow.setOnClickListener {
|
2021-06-07 16:34:50 +02:00
|
|
|
if (!slideshowActive){
|
|
|
|
slideshowActive = true
|
|
|
|
handler.postDelayed(slideshowNext, ConstValues.slideshow_delay)
|
|
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
|
|
|
hideUI.run()
|
|
|
|
Toast.makeText(this, R.string.slideshow_started, Toast.LENGTH_SHORT).show()
|
|
|
|
} else {
|
|
|
|
stopSlideshow()
|
|
|
|
}
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imagePrevious.setOnClickListener {
|
2021-06-07 16:34:50 +02:00
|
|
|
askSaveRotation {
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageViewer.resetZoomFactor()
|
2021-06-07 16:34:50 +02:00
|
|
|
swipeImage(1F)
|
|
|
|
}
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageNext.setOnClickListener {
|
2021-06-07 16:34:50 +02:00
|
|
|
askSaveRotation {
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageViewer.resetZoomFactor()
|
2021-06-07 16:34:50 +02:00
|
|
|
swipeImage(-1F)
|
|
|
|
}
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageRotateRight.setOnClickListener {
|
2021-06-07 16:34:50 +02:00
|
|
|
rotationAngle += 90
|
|
|
|
rotateImage()
|
|
|
|
}
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageRotateLeft.setOnClickListener {
|
2021-06-07 16:34:50 +02:00
|
|
|
rotationAngle -= 90
|
|
|
|
rotateImage()
|
|
|
|
}
|
2020-09-04 13:30:12 +02:00
|
|
|
loadImage()
|
|
|
|
handler.postDelayed(hideUI, hideDelay)
|
|
|
|
}
|
|
|
|
|
2021-12-20 20:13:39 +01:00
|
|
|
private fun getDisplaySize(): Size {
|
|
|
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
|
|
val insets = windowManager.currentWindowMetrics.windowInsets.getInsetsIgnoringVisibility(
|
|
|
|
WindowInsets.Type.navigationBars() or WindowInsets.Type.displayCutout()
|
|
|
|
)
|
|
|
|
Size(insets.right + insets.left, insets.top + insets.bottom)
|
|
|
|
} else {
|
|
|
|
val point = Point()
|
|
|
|
@Suppress("Deprecation")
|
|
|
|
windowManager.defaultDisplay.getSize(point)
|
|
|
|
Size(point.x, point.y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 13:30:12 +02:00
|
|
|
private fun loadImage(){
|
2021-12-25 16:13:34 +01:00
|
|
|
bitmap = null
|
|
|
|
requestBuilder = null
|
2020-09-04 13:30:12 +02:00
|
|
|
loadWholeFile(filePath)?.let {
|
2021-11-07 12:42:52 +01:00
|
|
|
val displayWithGlide = if (it.size < 5_000_000) {
|
|
|
|
true
|
|
|
|
} else {
|
2021-12-20 20:13:39 +01:00
|
|
|
bitmap = BitmapFactory.decodeByteArray(it, 0, it.size)
|
|
|
|
if (bitmap == null) {
|
2021-11-07 12:42:52 +01:00
|
|
|
true
|
|
|
|
} else {
|
2021-12-20 20:13:39 +01:00
|
|
|
val displaySize = getDisplaySize()
|
|
|
|
if (displaySize.width < bitmap!!.width || displaySize.height < bitmap!!.height) {
|
|
|
|
val newWidth: Int
|
|
|
|
val newHeight: Int
|
|
|
|
if (displaySize.width > displaySize.height) {
|
|
|
|
newWidth = displaySize.width
|
|
|
|
newHeight = bitmap!!.height*displaySize.width/bitmap!!.width
|
|
|
|
} else {
|
|
|
|
newHeight = displaySize.height
|
|
|
|
newWidth = bitmap!!.width*displaySize.height/bitmap!!.height
|
|
|
|
}
|
|
|
|
bitmap = Bitmap.createScaledBitmap(bitmap!!, newWidth, newHeight, false)
|
|
|
|
}
|
|
|
|
Glide.with(this).load(bitmap).into(binding.imageViewer)
|
2021-11-07 12:42:52 +01:00
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (displayWithGlide) {
|
2021-12-20 20:13:39 +01:00
|
|
|
requestBuilder = Glide.with(this).load(it)
|
|
|
|
requestBuilder?.into(binding.imageViewer)
|
2021-11-07 12:42:52 +01:00
|
|
|
}
|
2020-09-04 13:30:12 +02:00
|
|
|
fileName = File(filePath).name
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.textFilename.text = fileName
|
2020-09-04 13:30:12 +02:00
|
|
|
rotationAngle = 0F
|
2020-08-25 15:43:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 13:30:12 +02:00
|
|
|
override fun onUserInteraction() {
|
|
|
|
super.onUserInteraction()
|
|
|
|
handler.removeCallbacks(hideUI)
|
|
|
|
handler.postDelayed(hideUI, hideDelay)
|
|
|
|
}
|
|
|
|
|
2020-11-03 13:34:40 +01:00
|
|
|
private fun swipeImage(deltaX: Float, slideshowSwipe: Boolean = false){
|
2021-03-17 21:11:14 +01:00
|
|
|
playlistNext(deltaX < 0)
|
|
|
|
loadImage()
|
|
|
|
if (slideshowActive){
|
|
|
|
if (!slideshowSwipe) { //reset slideshow delay if user swipes
|
|
|
|
handler.removeCallbacks(slideshowNext)
|
2020-11-03 13:34:40 +01:00
|
|
|
}
|
2021-03-17 21:11:14 +01:00
|
|
|
handler.postDelayed(slideshowNext, ConstValues.slideshow_delay)
|
2020-08-25 15:43:47 +02:00
|
|
|
}
|
2020-09-04 13:30:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun stopSlideshow(){
|
|
|
|
slideshowActive = false
|
2020-09-29 11:14:30 +02:00
|
|
|
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
2020-09-04 13:30:12 +02:00
|
|
|
Toast.makeText(this, R.string.slideshow_stopped, Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onBackPressed() {
|
|
|
|
if (slideshowActive){
|
|
|
|
stopSlideshow()
|
2020-08-25 15:43:47 +02:00
|
|
|
} else {
|
2020-09-30 16:19:16 +02:00
|
|
|
askSaveRotation { super.onBackPressed() }
|
2020-08-01 16:43:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-30 16:19:16 +02:00
|
|
|
class RotateTransformation(private val imageViewer: ImageViewer): BitmapTransformation() {
|
2020-07-18 18:44:53 +02:00
|
|
|
|
2020-09-30 16:19:16 +02:00
|
|
|
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap? {
|
2020-07-18 18:44:53 +02:00
|
|
|
val matrix = Matrix()
|
2020-09-30 16:19:16 +02:00
|
|
|
matrix.postRotate(imageViewer.rotationAngle)
|
|
|
|
imageViewer.rotatedBitmap = Bitmap.createBitmap(toTransform, 0, 0, toTransform.width, toTransform.height, matrix, true)
|
|
|
|
return imageViewer.rotatedBitmap
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
2020-07-18 18:44:53 +02:00
|
|
|
|
|
|
|
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
|
2020-09-30 16:19:16 +02:00
|
|
|
messageDigest.update("rotate${imageViewer.rotationAngle}".toByteArray())
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-18 18:44:53 +02:00
|
|
|
private fun rotateImage(){
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageViewer.restoreZoomNormal()
|
2021-12-20 20:13:39 +01:00
|
|
|
(requestBuilder ?: Glide.with(this).load(bitmap))
|
|
|
|
.transform(RotateTransformation(this)).into(binding.imageViewer)
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
2020-09-30 16:19:16 +02:00
|
|
|
|
|
|
|
private fun askSaveRotation(callback: () -> Unit){
|
2020-11-03 13:34:40 +01:00
|
|
|
if (rotationAngle%360 != 0f && !slideshowActive){
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2020-09-30 16:19:16 +02:00
|
|
|
.keepFullScreen()
|
|
|
|
.setTitle(R.string.warning)
|
|
|
|
.setMessage(R.string.ask_save_img_rotated)
|
|
|
|
.setNegativeButton(R.string.no) { _, _ -> callback() }
|
2020-11-03 13:34:40 +01:00
|
|
|
.setNeutralButton(R.string.cancel, null)
|
2020-09-30 16:19:16 +02:00
|
|
|
.setPositiveButton(R.string.yes) { _, _ ->
|
|
|
|
val outputStream = ByteArrayOutputStream()
|
|
|
|
if (rotatedBitmap?.compress(
|
|
|
|
if (fileName.endsWith("png", true)){
|
|
|
|
Bitmap.CompressFormat.PNG
|
|
|
|
} else {
|
|
|
|
Bitmap.CompressFormat.JPEG
|
|
|
|
}, 100, outputStream) == true
|
|
|
|
){
|
|
|
|
if (gocryptfsVolume.importFile(ByteArrayInputStream(outputStream.toByteArray()), filePath)){
|
|
|
|
Toast.makeText(this, R.string.image_saved_successfully, Toast.LENGTH_SHORT).show()
|
|
|
|
callback()
|
|
|
|
} else {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2020-09-30 16:19:16 +02:00
|
|
|
.keepFullScreen()
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.file_write_failed)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
} else {
|
2021-11-09 11:12:09 +01:00
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
2020-09-30 16:19:16 +02:00
|
|
|
.keepFullScreen()
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.bitmap_compress_failed)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.show()
|
|
|
|
} else {
|
|
|
|
callback()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-25 15:43:47 +02:00
|
|
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
|
|
super.onConfigurationChanged(newConfig)
|
2021-06-11 20:23:54 +02:00
|
|
|
binding.imageViewer.restoreZoomNormal()
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
}
|