forked from hardcoresushi/DroidFS
Image rotation saving feature & navigation buttons
This commit is contained in:
parent
0ca5bbcbb4
commit
646a194dd7
@ -4,7 +4,7 @@ apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "30.0.0"
|
||||
buildToolsVersion "30.0.2"
|
||||
|
||||
compileOptions {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
@ -14,8 +14,8 @@ android {
|
||||
applicationId "sushi.hardcore.droidfs"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 2
|
||||
versionName "1.1.7"
|
||||
versionCode 3
|
||||
versionName "1.1.8"
|
||||
|
||||
ndk {
|
||||
abiFilters 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
|
@ -13,9 +13,9 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.android.synthetic.main.activity_change_password.*
|
||||
import kotlinx.android.synthetic.main.activity_change_password.checkbox_remember_path
|
||||
import kotlinx.android.synthetic.main.activity_change_password.checkbox_save_password
|
||||
import kotlinx.android.synthetic.main.activity_change_password.edit_volume_path
|
||||
import kotlinx.android.synthetic.main.activity_change_password.saved_path_listview
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import kotlinx.android.synthetic.main.volume_path_section.*
|
||||
import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
|
||||
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
|
||||
import sushi.hardcore.droidfs.util.*
|
||||
|
@ -9,6 +9,7 @@ import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.android.synthetic.main.activity_create.*
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import kotlinx.android.synthetic.main.volume_path_section.*
|
||||
import sushi.hardcore.droidfs.explorers.ExplorerActivity
|
||||
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
|
||||
import sushi.hardcore.droidfs.util.*
|
||||
@ -16,7 +17,6 @@ import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
||||
class CreateActivity : BaseActivity() {
|
||||
companion object {
|
||||
private const val PICK_DIRECTORY_REQUEST_CODE = 1
|
||||
|
@ -9,15 +9,9 @@ import android.view.View
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.android.synthetic.main.activity_change_password.*
|
||||
import kotlinx.android.synthetic.main.activity_create.*
|
||||
import kotlinx.android.synthetic.main.activity_open.*
|
||||
import kotlinx.android.synthetic.main.activity_open.checkbox_remember_path
|
||||
import kotlinx.android.synthetic.main.activity_open.checkbox_save_password
|
||||
import kotlinx.android.synthetic.main.activity_open.edit_password
|
||||
import kotlinx.android.synthetic.main.activity_open.edit_volume_path
|
||||
import kotlinx.android.synthetic.main.activity_open.saved_path_listview
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import kotlinx.android.synthetic.main.volume_path_section.*
|
||||
import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
|
||||
import sushi.hardcore.droidfs.explorers.ExplorerActivity
|
||||
import sushi.hardcore.droidfs.explorers.ExplorerActivityDrop
|
||||
|
@ -287,7 +287,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
||||
dialogEditText.selectAll()
|
||||
val dialog = ColoredAlertDialogBuilder(this)
|
||||
.setView(dialogEditTextView)
|
||||
.setTitle(getString(R.string.enter_new_name))
|
||||
.setTitle(R.string.enter_new_name)
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
handler.sendMessage(Message().apply { obj = checkPathOverwrite(PathUtils.path_join(PathUtils.getParentPath(path), dialogEditText.text.toString()), isDirectory) })
|
||||
}
|
||||
@ -302,7 +302,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
||||
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
dialog.show()
|
||||
}
|
||||
.setPositiveButton(R.string.yes) {_, _ -> handler.sendMessage(Message().apply { obj = path }) }
|
||||
.setPositiveButton(R.string.yes) { _, _ -> handler.sendMessage(Message().apply { obj = path }) }
|
||||
.create()
|
||||
dialog.setOnCancelListener { handler.sendMessage(Message().apply { obj = null }) }
|
||||
dialog.show()
|
||||
|
@ -22,8 +22,8 @@ abstract class FileViewerActivity: BaseActivity() {
|
||||
}
|
||||
open fun hideSystemUi(){
|
||||
window.decorView.systemUiVisibility =
|
||||
View.SYSTEM_UI_FLAG_LOW_PROFILE or
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN/* or
|
||||
View.SYSTEM_UI_FLAG_LOW_PROFILE or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
|
||||
|
@ -21,6 +21,8 @@ import sushi.hardcore.droidfs.util.MiscUtils
|
||||
import sushi.hardcore.droidfs.util.PathUtils
|
||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||
import sushi.hardcore.droidfs.widgets.ZoomableImageView
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.security.MessageDigest
|
||||
import kotlin.math.abs
|
||||
@ -40,11 +42,20 @@ class ImageViewer: FileViewerActivity() {
|
||||
private var slideshowActive = false
|
||||
private var currentMappedImageIndex = -1
|
||||
private var rotationAngle: Float = 0F
|
||||
private var rotatedBitmap: Bitmap? = null
|
||||
private val handler = Handler()
|
||||
private val hideUI = Runnable {
|
||||
action_buttons.visibility = View.GONE
|
||||
action_bar.visibility = View.GONE
|
||||
}
|
||||
private val slideshowLoop = object : Runnable {
|
||||
override fun run() {
|
||||
if (slideshowActive){
|
||||
swipeImage(-1F)
|
||||
handler.postDelayed(this, ConstValues.slideshow_delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun viewFile() {
|
||||
setContentView(R.layout.activity_image_viewer)
|
||||
image_viewer.setOnInteractionListener(object : ZoomableImageView.OnInteractionListener {
|
||||
@ -69,7 +80,7 @@ class ImageViewer: FileViewerActivity() {
|
||||
x2 = event.x
|
||||
val deltaX = x2 - x1
|
||||
if (abs(deltaX) > MIN_SWIPE_DISTANCE) {
|
||||
swipeImage(deltaX)
|
||||
askSaveRotation { swipeImage(deltaX) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,13 +160,7 @@ class ImageViewer: FileViewerActivity() {
|
||||
fun onClickSlideshow(view: View) {
|
||||
if (!slideshowActive){
|
||||
slideshowActive = true
|
||||
Thread {
|
||||
Thread.sleep(ConstValues.slideshow_delay)
|
||||
while (slideshowActive){
|
||||
runOnUiThread { swipeImage(-1F) }
|
||||
Thread.sleep(ConstValues.slideshow_delay)
|
||||
}
|
||||
}.start()
|
||||
handler.postDelayed(slideshowLoop, ConstValues.slideshow_delay)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
hideUI.run()
|
||||
Toast.makeText(this, R.string.slideshow_started, Toast.LENGTH_SHORT).show()
|
||||
@ -174,26 +179,27 @@ class ImageViewer: FileViewerActivity() {
|
||||
if (slideshowActive){
|
||||
stopSlideshow()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
askSaveRotation { super.onBackPressed() }
|
||||
}
|
||||
}
|
||||
|
||||
class RotateTransformation(private val rotationAngle: Float): BitmapTransformation() {
|
||||
class RotateTransformation(private val imageViewer: ImageViewer): BitmapTransformation() {
|
||||
|
||||
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
|
||||
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap? {
|
||||
val matrix = Matrix()
|
||||
matrix.postRotate(rotationAngle)
|
||||
return Bitmap.createBitmap(toTransform, 0, 0, toTransform.width, toTransform.height, matrix, true)
|
||||
matrix.postRotate(imageViewer.rotationAngle)
|
||||
imageViewer.rotatedBitmap = Bitmap.createBitmap(toTransform, 0, 0, toTransform.width, toTransform.height, matrix, true)
|
||||
return imageViewer.rotatedBitmap
|
||||
}
|
||||
|
||||
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
|
||||
messageDigest.update("rotate$rotationAngle".toByteArray())
|
||||
messageDigest.update("rotate${imageViewer.rotationAngle}".toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
private fun rotateImage(){
|
||||
image_viewer.restoreZoomNormal()
|
||||
glideImage.transform(RotateTransformation(rotationAngle)).into(image_viewer)
|
||||
glideImage.transform(RotateTransformation(this)).into(image_viewer)
|
||||
}
|
||||
fun onCLickRotateRight(view: View){
|
||||
rotationAngle += 90
|
||||
@ -204,6 +210,55 @@ class ImageViewer: FileViewerActivity() {
|
||||
rotateImage()
|
||||
}
|
||||
|
||||
fun onClickPrevious(view: View){
|
||||
askSaveRotation { swipeImage(1F) }
|
||||
}
|
||||
fun onClickNext(view: View){
|
||||
askSaveRotation { swipeImage(-1F) }
|
||||
}
|
||||
|
||||
private fun askSaveRotation(callback: () -> Unit){
|
||||
if (rotationAngle%360 != 0f){
|
||||
ColoredAlertDialogBuilder(this)
|
||||
.keepFullScreen()
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(R.string.ask_save_img_rotated)
|
||||
.setNegativeButton(R.string.no) { _, _ -> callback() }
|
||||
.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 {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
.keepFullScreen()
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.file_write_failed)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
.keepFullScreen()
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.bitmap_compress_failed)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
.show()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
image_viewer.restoreZoomNormal()
|
||||
|
@ -3,7 +3,9 @@ package sushi.hardcore.droidfs.util
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||
import java.io.*
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
class GocryptfsVolume(var sessionID: Int) {
|
||||
private external fun native_close(sessionID: Int)
|
||||
|
@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.widgets
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
|
||||
class ColoredAlertDialogBuilder: AlertDialog.Builder {
|
||||
@ -27,6 +28,10 @@ class ColoredAlertDialogBuilder: AlertDialog.Builder {
|
||||
override fun show(): AlertDialog {
|
||||
val dialog = create()
|
||||
dialog.show()
|
||||
if (keepFullScreen){
|
||||
dialog.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
dialog.window?.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
|
||||
}
|
||||
return dialog
|
||||
}
|
||||
|
||||
|
@ -16,34 +16,7 @@
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="@dimen/change_password_activity_label_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/volume_path"
|
||||
android:textSize="@dimen/edit_text_label_text_size" />
|
||||
|
||||
<sushi.hardcore.droidfs.widgets.ColoredEditText
|
||||
android:id="@+id/edit_volume_path"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="0.5"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"/>
|
||||
|
||||
<sushi.hardcore.droidfs.widgets.ColoredImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:onClick="pickDirectory"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:src="@drawable/icon_folder" />
|
||||
|
||||
</LinearLayout>
|
||||
<include layout="@layout/volume_path_section"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -16,34 +16,7 @@
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="@dimen/create_activity_label_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/volume_path"
|
||||
android:textSize="@dimen/edit_text_label_text_size" />
|
||||
|
||||
<sushi.hardcore.droidfs.widgets.ColoredEditText
|
||||
android:id="@+id/edit_volume_path"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="0.5"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"/>
|
||||
|
||||
<sushi.hardcore.droidfs.widgets.ColoredImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:onClick="pickDirectory"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:src="@drawable/icon_folder" />
|
||||
|
||||
</LinearLayout>
|
||||
<include layout="@layout/volume_path_section"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -65,8 +65,17 @@
|
||||
android:gravity="center">
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_width="@dimen/image_button_size"
|
||||
android:layout_height="@dimen/image_button_size"
|
||||
android:layout_margin="10dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:src="@drawable/exo_icon_previous"
|
||||
android:onClick="onClickPrevious"/>
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="@dimen/image_button_size"
|
||||
android:layout_height="@dimen/image_button_size"
|
||||
android:layout_margin="10dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
@ -74,14 +83,23 @@
|
||||
android:onClick="onClickRotateLeft"/>
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_width="@dimen/image_button_size"
|
||||
android:layout_height="@dimen/image_button_size"
|
||||
android:layout_margin="10dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:src="@drawable/icon_rotate_right"
|
||||
android:onClick="onCLickRotateRight"/>
|
||||
|
||||
<ImageButton
|
||||
android:layout_width="@dimen/image_button_size"
|
||||
android:layout_height="@dimen/image_button_size"
|
||||
android:layout_margin="10dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:src="@drawable/exo_icon_next"
|
||||
android:onClick="onClickNext"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -16,34 +16,7 @@
|
||||
android:padding="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="@dimen/open_activity_label_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/volume_path"
|
||||
android:textSize="@dimen/edit_text_label_text_size" />
|
||||
|
||||
<sushi.hardcore.droidfs.widgets.ColoredEditText
|
||||
android:id="@+id/edit_volume_path"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="0.5"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"/>
|
||||
|
||||
<sushi.hardcore.droidfs.widgets.ColoredImageButton
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:onClick="pickDirectory"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:src="@drawable/icon_folder"/>
|
||||
|
||||
</LinearLayout>
|
||||
<include layout="@layout/volume_path_section"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
29
app/src/main/res/layout/volume_path_section.xml
Normal file
29
app/src/main/res/layout/volume_path_section.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="@dimen/create_activity_label_width"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/volume_path"
|
||||
android:textSize="@dimen/edit_text_label_text_size" />
|
||||
|
||||
<sushi.hardcore.droidfs.widgets.ColoredEditText
|
||||
android:id="@+id/edit_volume_path"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="0.5"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"/>
|
||||
|
||||
<sushi.hardcore.droidfs.widgets.ColoredImageButton
|
||||
android:layout_width="@dimen/image_button_size"
|
||||
android:layout_height="@dimen/image_button_size"
|
||||
android:onClick="pickDirectory"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="#00000000"
|
||||
android:src="@drawable/icon_folder" />
|
||||
|
||||
</LinearLayout>
|
@ -14,4 +14,5 @@
|
||||
<dimen name="warning_msg_padding">20dp</dimen>
|
||||
<dimen name="action_activity_button_margin_bottom">20dp</dimen>
|
||||
<dimen name="adapter_text_size">18sp</dimen>
|
||||
<dimen name="image_button_size">40dp</dimen>
|
||||
</resources>
|
@ -173,4 +173,8 @@
|
||||
<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>
|
||||
<string name="slideshow_started">Slideshow started</string>
|
||||
<string name="ask_save_img_rotated">The image has been rotated. Do you want to save these changes and overwrite the original image ?</string>
|
||||
<string name="image_saved_successfully">Image changes successfully saved.</string>
|
||||
<string name="bitmap_compress_failed">Failed to compress the bitmap.</string>
|
||||
<string name="file_write_failed">Failed to write the file.</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user