forked from hardcoresushi/DroidFS
ImageViewer title & slideshow
This commit is contained in:
parent
f44a702647
commit
5c0f317a04
@ -11,6 +11,7 @@ class ConstValues {
|
||||
val fakeUri: Uri = Uri.parse("fakeuri://droidfs")
|
||||
const val MAX_KERNEL_WRITE = 128*1024
|
||||
const val wipe_passes = 2
|
||||
const val slideshow_delay: Long = 4000
|
||||
private val fileExtensions = mapOf(
|
||||
Pair("image", listOf("png", "jpg", "jpeg")),
|
||||
Pair("video", listOf("mp4", "webm")),
|
||||
|
@ -1,11 +1,13 @@
|
||||
package sushi.hardcore.droidfs.file_viewers
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Matrix
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Handler
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestBuilder
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
@ -16,6 +18,8 @@ import sushi.hardcore.droidfs.R
|
||||
import sushi.hardcore.droidfs.util.MiscUtils
|
||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||
import sushi.hardcore.droidfs.util.PathUtils
|
||||
import sushi.hardcore.droidfs.widgets.ZoomableImageView
|
||||
import java.io.File
|
||||
import java.security.MessageDigest
|
||||
import kotlin.math.abs
|
||||
|
||||
@ -30,60 +34,96 @@ class ImageViewer: FileViewerActivity() {
|
||||
private val mappedImages = mutableListOf<ExplorerElement>()
|
||||
private lateinit var sortOrder: String
|
||||
private var wasMapped = false
|
||||
private var slideshowActive = false
|
||||
private var currentMappedImageIndex = -1
|
||||
private var rotationAngle: Float = 0F
|
||||
private val handler = Handler()
|
||||
private val hideActionButtons = Runnable { action_buttons.visibility = View.GONE }
|
||||
private val hideUI = Runnable {
|
||||
action_buttons.visibility = View.GONE
|
||||
action_bar.visibility = View.GONE
|
||||
}
|
||||
override fun viewFile() {
|
||||
val imageBuff = loadWholeFile(filePath)
|
||||
if (imageBuff != null){
|
||||
setContentView(R.layout.activity_image_viewer)
|
||||
glideImage = Glide.with(this).load(imageBuff)
|
||||
glideImage.into(image_viewer)
|
||||
handler.postDelayed(hideActionButtons, hideDelay)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
|
||||
if (!image_viewer.isZoomed){
|
||||
when(event?.action){
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
x1 = event.x
|
||||
image_viewer.setOnInteractionListener(object : ZoomableImageView.OnInteractionListener{
|
||||
override fun onSingleTap(event: MotionEvent?) {
|
||||
handler.removeCallbacks(hideUI)
|
||||
if (action_buttons.visibility == View.GONE){
|
||||
action_buttons.visibility = View.VISIBLE
|
||||
action_bar.visibility = View.VISIBLE
|
||||
handler.postDelayed(hideUI, hideDelay)
|
||||
} else {
|
||||
hideUI.run()
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
x2 = event.x
|
||||
val deltaX = x2 - x1
|
||||
if (abs(deltaX) > MIN_SWIPE_DISTANCE){
|
||||
if (!wasMapped){
|
||||
for (e in gocryptfsVolume.recursiveMapFiles(PathUtils.getParentPath(filePath))){
|
||||
if (e.isRegularFile && ConstValues.isImage(e.name)){
|
||||
mappedImages.add(e)
|
||||
override fun onTouch(event: MotionEvent?) {
|
||||
if (!image_viewer.isZoomed){
|
||||
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){
|
||||
swipeImage(deltaX)
|
||||
}
|
||||
}
|
||||
sortOrder = intent.getStringExtra("sortOrder") ?: "name"
|
||||
ExplorerElement.sortBy(sortOrder, mappedImages)
|
||||
for ((i, e) in mappedImages.withIndex()){
|
||||
if (filePath == e.fullPath){
|
||||
currentMappedImageIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
wasMapped = true
|
||||
}
|
||||
currentMappedImageIndex = if (deltaX < 0){
|
||||
MiscUtils.incrementIndex(currentMappedImageIndex, mappedImages)
|
||||
} else {
|
||||
MiscUtils.decrementIndex(currentMappedImageIndex, mappedImages)
|
||||
}
|
||||
loadWholeFile(mappedImages[currentMappedImageIndex].fullPath)?.let {
|
||||
glideImage = Glide.with(this).load(it)
|
||||
glideImage.into(image_viewer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
glideImage = Glide.with(this).load(imageBuff)
|
||||
glideImage.into(image_viewer)
|
||||
text_filename.text = File(filePath).name
|
||||
handler.postDelayed(hideUI, hideDelay)
|
||||
}
|
||||
}
|
||||
|
||||
private fun swipeImage(deltaX: Float){
|
||||
if (!wasMapped){
|
||||
for (e in gocryptfsVolume.recursiveMapFiles(PathUtils.getParentPath(filePath))){
|
||||
if (e.isRegularFile && ConstValues.isImage(e.name)){
|
||||
mappedImages.add(e)
|
||||
}
|
||||
}
|
||||
sortOrder = intent.getStringExtra("sortOrder") ?: "name"
|
||||
ExplorerElement.sortBy(sortOrder, mappedImages)
|
||||
for ((i, e) in mappedImages.withIndex()){
|
||||
if (filePath == e.fullPath){
|
||||
currentMappedImageIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
wasMapped = true
|
||||
}
|
||||
currentMappedImageIndex = if (deltaX < 0){
|
||||
MiscUtils.incrementIndex(currentMappedImageIndex, mappedImages)
|
||||
} else {
|
||||
MiscUtils.decrementIndex(currentMappedImageIndex, mappedImages)
|
||||
}
|
||||
loadWholeFile(mappedImages[currentMappedImageIndex].fullPath)?.let {
|
||||
glideImage = Glide.with(applicationContext).load(it)
|
||||
glideImage.into(image_viewer)
|
||||
text_filename.text = File(mappedImages[currentMappedImageIndex].fullPath).name
|
||||
rotationAngle = 0F
|
||||
}
|
||||
}
|
||||
|
||||
fun onClickSlideshow(view: View) {
|
||||
if (!slideshowActive){
|
||||
slideshowActive = true
|
||||
Thread {
|
||||
while (slideshowActive){
|
||||
runOnUiThread { swipeImage(-1F) }
|
||||
Thread.sleep(ConstValues.slideshow_delay)
|
||||
}
|
||||
}.start()
|
||||
} else {
|
||||
slideshowActive = false
|
||||
Toast.makeText(this, R.string.slideshow_stopped, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return super.dispatchTouchEvent(event)
|
||||
}
|
||||
|
||||
class RotateTransformation(private val rotationAngle: Float): BitmapTransformation() {
|
||||
@ -112,16 +152,8 @@ class ImageViewer: FileViewerActivity() {
|
||||
rotateImage()
|
||||
}
|
||||
|
||||
override fun onUserInteraction() {
|
||||
super.onUserInteraction()
|
||||
action_buttons?.let {
|
||||
handler.removeCallbacks(hideActionButtons)
|
||||
if (it.visibility == View.GONE){
|
||||
it.visibility = View.VISIBLE
|
||||
handler.postDelayed(hideActionButtons, hideDelay)
|
||||
} else {
|
||||
it.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
image_viewer.restoreZoomNormal()
|
||||
}
|
||||
}
|
@ -72,10 +72,10 @@ class TextEditor: FileViewerActivity() {
|
||||
if (handleID != -1){
|
||||
val buff = ByteArrayInputStream(content)
|
||||
var offset: Long = 0
|
||||
val io_buffer = ByteArray(GocryptfsVolume.DefaultBS)
|
||||
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
|
||||
var length: Int
|
||||
while (buff.read(io_buffer).also { length = it } > 0) {
|
||||
val written = gocryptfsVolume.writeFile(handleID, offset, io_buffer, length).toLong()
|
||||
while (buff.read(ioBuffer).also { length = it } > 0) {
|
||||
val written = gocryptfsVolume.writeFile(handleID, offset, ioBuffer, length).toLong()
|
||||
if (written == length.toLong()) {
|
||||
offset += written
|
||||
} else {
|
||||
|
@ -35,6 +35,13 @@ public class ZoomableImageView extends androidx.appcompat.widget.AppCompatImageV
|
||||
|
||||
ScaleGestureDetector mScaleDetector;
|
||||
|
||||
public interface OnInteractionListener {
|
||||
void onTouch(MotionEvent event);
|
||||
void onSingleTap(MotionEvent event);
|
||||
}
|
||||
|
||||
OnInteractionListener onInteractionListener = null;
|
||||
|
||||
Context context;
|
||||
|
||||
public ZoomableImageView(Context context) {
|
||||
@ -124,15 +131,28 @@ public class ZoomableImageView extends androidx.appcompat.widget.AppCompatImageV
|
||||
fixTrans();
|
||||
}
|
||||
|
||||
public void setOnInteractionListener(OnInteractionListener listener){
|
||||
onInteractionListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
if (onInteractionListener != null){
|
||||
onInteractionListener.onSingleTap(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
// Double tap is detected
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
if (onInteractionListener != null){
|
||||
onInteractionListener.onTouch(event);
|
||||
}
|
||||
return super.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
if (saveScale >= maxScale) {
|
||||
restoreZoomNormal();
|
||||
} else {
|
||||
|
5
app/src/main/res/drawable/icon_slideshow.xml
Normal file
5
app/src/main/res/drawable/icon_slideshow.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M10,8v8l5,-4 -5,-4zM19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,5h14v14z"/>
|
||||
</vector>
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/root_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -11,6 +12,38 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/action_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_margin="20dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_filename"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textSize="20sp"
|
||||
android:layout_marginEnd="40dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/image_button_slideshow"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:background="#00000000"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/icon_slideshow"
|
||||
android:onClick="onClickSlideshow"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action_buttons"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -173,4 +173,5 @@
|
||||
<string name="open_cant_write_warning">DroidFS doesn\'t have write access to this path. You will only have read-only access to this volume.</string>
|
||||
<string name="change_pwd_cant_write_error_msg">DroidFS doesn\'t have write access to this path. You can try to move the volume to a writable location.</string>
|
||||
<string name="change_pwd_on_sdcard_error_msg">DroidFS can\'t write on removable SD cards, please move the volume to internal storage.</string>
|
||||
<string name="slideshow_stopped">Slideshow stopped</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user