diff --git a/app/build.gradle b/app/build.gradle index 20b225e..1522041 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt index 5d8bc79..9bb36a4 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt @@ -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.* diff --git a/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt index d9b9081..3eeac35 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt @@ -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 diff --git a/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt index ea1b912..cd820df 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt @@ -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 diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt index ad2c22d..50ac5e0 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt @@ -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() diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt index 04c311c..bffc007 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/FileViewerActivity.kt @@ -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 diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt index 9ec2ebc..5da3bc4 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/ImageViewer.kt @@ -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() diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/GocryptfsVolume.kt b/app/src/main/java/sushi/hardcore/droidfs/util/GocryptfsVolume.kt index bbbe792..922ebb0 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/util/GocryptfsVolume.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/util/GocryptfsVolume.kt @@ -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) diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialogBuilder.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialogBuilder.kt index eb5a008..0f17156 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialogBuilder.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredAlertDialogBuilder.kt @@ -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 } diff --git a/app/src/main/res/layout/activity_change_password.xml b/app/src/main/res/layout/activity_change_password.xml index d9655b0..bce890f 100644 --- a/app/src/main/res/layout/activity_change_password.xml +++ b/app/src/main/res/layout/activity_change_password.xml @@ -16,34 +16,7 @@ android:orientation="vertical" android:padding="10dp"> - - - - - - - - - + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_open.xml b/app/src/main/res/layout/activity_open.xml index edb1336..ad1b65a 100644 --- a/app/src/main/res/layout/activity_open.xml +++ b/app/src/main/res/layout/activity_open.xml @@ -16,34 +16,7 @@ android:padding="10dp" android:orientation="vertical"> - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index f87fa9a..4bdc481 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -14,4 +14,5 @@ 20dp 20dp 18sp + 40dp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7980935..3585899 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -173,4 +173,8 @@ DroidFS can\'t write on removable SD cards, please move the volume to internal storage. Slideshow stopped Slideshow started + The image has been rotated. Do you want to save these changes and overwrite the original image ? + Image changes successfully saved. + Failed to compress the bitmap. + Failed to write the file.