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 7c97caf..b6d46fc 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt @@ -2,6 +2,9 @@ package sushi.hardcore.droidfs.explorers import android.content.Intent import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.os.Message import android.view.Menu import android.view.MenuItem import android.view.View @@ -34,6 +37,7 @@ import sushi.hardcore.droidfs.util.ExternalProvider import sushi.hardcore.droidfs.util.GocryptfsVolume import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder +import java.io.File open class BaseExplorerActivity : BaseActivity() { private lateinit var sortOrderEntries: Array @@ -253,6 +257,53 @@ open class BaseExplorerActivity : BaseActivity() { dialog.show() } + protected fun checkFileOverwrite(path: String): String? { + var outputPath: String? = null + if (gocryptfsVolume.pathExists(path)){ + val fileName = File(path).name + val handler = Handler{ msg -> + outputPath = msg.obj as String? + throw RuntimeException() + } + runOnUiThread { + val dialog = ColoredAlertDialogBuilder(this) + .setTitle(R.string.warning) + .setMessage(getString(R.string.file_overwrite_question, fileName)) + .setNegativeButton(R.string.no) { _, _ -> + val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null) + val dialogEditText = dialogEditTextView.findViewById(R.id.dialog_edit_text) + dialogEditText.setText(fileName) + dialogEditText.selectAll() + val dialog = ColoredAlertDialogBuilder(this) + .setView(dialogEditTextView) + .setTitle(getString(R.string.enter_new_filename)) + .setPositiveButton(R.string.ok) { _, _ -> + handler.sendMessage(Message().apply { obj = checkFileOverwrite(PathUtils.path_join(PathUtils.getParentPath(path), dialogEditText.text.toString())) }) + } + .setNegativeButton(R.string.cancel) { _, _ -> handler.sendMessage(Message().apply { obj = null }) } + .create() + dialogEditText.setOnEditorActionListener { _, _, _ -> + dialog.dismiss() + handler.sendMessage(Message().apply { obj = checkFileOverwrite(PathUtils.path_join(PathUtils.getParentPath(path), dialogEditText.text.toString())) }) + true + } + dialog.setOnCancelListener { handler.sendMessage(Message().apply { obj = null }) } + dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) + dialog.show() + } + .setPositiveButton(R.string.yes) {_, _ -> handler.sendMessage(Message().apply { obj = path }) } + .create() + dialog.setOnCancelListener { handler.sendMessage(Message().apply { obj = null }) } + dialog.show() + } + try { Looper.loop() } + catch (e: RuntimeException) {} + } else { + outputPath = path + } + return outputPath + } + protected fun rename(old_name: String, new_name: String){ if (new_name.isEmpty()) { Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt index e3b8a58..0374f5f 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt @@ -3,6 +3,7 @@ package sushi.hardcore.droidfs.explorers import android.app.Activity import android.content.Intent import android.net.Uri +import android.os.Looper import android.view.Menu import android.view.MenuItem import android.view.View @@ -43,17 +44,19 @@ class ExplorerActivity : BaseExplorerActivity() { if (fileName.isEmpty()) { Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() } else { - val handleID = gocryptfsVolume.openWriteMode(PathUtils.path_join(currentDirectoryPath, fileName)) - if (handleID == -1) { - ColoredAlertDialogBuilder(this) - .setTitle(R.string.error) - .setMessage(R.string.file_creation_failed) - .setPositiveButton(R.string.ok, null) - .show() - } else { - gocryptfsVolume.closeFile(handleID) - setCurrentPath(currentDirectoryPath) - invalidateOptionsMenu() + checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, fileName))?.let { + val handleID = gocryptfsVolume.openWriteMode(it) + if (handleID == -1) { + ColoredAlertDialogBuilder(this) + .setTitle(R.string.error) + .setMessage(R.string.file_creation_failed) + .setPositiveButton(R.string.ok, null) + .show() + } else { + gocryptfsVolume.closeFile(handleID) + setCurrentPath(currentDirectoryPath) + invalidateOptionsMenu() + } } } } @@ -143,21 +146,26 @@ class ExplorerActivity : BaseExplorerActivity() { } else { uris.add(singleUri) } - var success = true + Looper.prepare() + var success = false for (uri in uris) { - val dstPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)) - contentResolver.openInputStream(uri)?.let { - success = gocryptfsVolume.importFile(it, dstPath) - } - if (!success) { - stopTask { - ColoredAlertDialogBuilder(activity) - .setTitle(R.string.error) - .setMessage(getString(R.string.import_failed, uri)) - .setPositiveButton(R.string.ok, null) - .show() - } + val dstPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri))) + if (dstPath == null){ break + } else { + contentResolver.openInputStream(uri)?.let { + success = gocryptfsVolume.importFile(it, dstPath) + } + if (!success) { + stopTask { + ColoredAlertDialogBuilder(activity) + .setTitle(R.string.error) + .setMessage(getString(R.string.import_failed, uri)) + .setPositiveButton(R.string.ok, null) + .show() + } + break + } } } if (success) { @@ -257,6 +265,7 @@ class ExplorerActivity : BaseExplorerActivity() { val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID) val path = data.getStringExtra("path") var failedItem: String? = null + Looper.prepare() if (path == null) { val paths = data.getStringArrayListExtra("paths") val types = data.getIntegerArrayListExtra("types") @@ -265,7 +274,7 @@ class ExplorerActivity : BaseExplorerActivity() { failedItem = if (types[i] == 0) { //directory recursiveImportDirectoryFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath) } else { - if (importFileFromOtherVolume(remoteGocryptfsVolume, paths[i], PathUtils.path_join(currentDirectoryPath, File(paths[i]).name))) null else paths[i] + safeImportFileFromOtherVolume(remoteGocryptfsVolume, paths[i], PathUtils.path_join(currentDirectoryPath, File(paths[i]).name)) } if (failedItem != null) { break @@ -273,7 +282,7 @@ class ExplorerActivity : BaseExplorerActivity() { } } } else { - failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, PathUtils.path_join(currentDirectoryPath, File(path).name))) null else path + failedItem = safeImportFileFromOtherVolume(remoteGocryptfsVolume, path, PathUtils.path_join(currentDirectoryPath, File(path).name)) } if (failedItem == null) { stopTask { @@ -283,7 +292,7 @@ class ExplorerActivity : BaseExplorerActivity() { .setPositiveButton(R.string.ok, null) .show() } - } else { + } else if (failedItem!!.isNotEmpty()){ stopTask { ColoredAlertDialogBuilder(activity) .setTitle(R.string.error) @@ -356,13 +365,19 @@ class ExplorerActivity : BaseExplorerActivity() { object : LoadingTask(this, R.string.loading_msg_copy){ override fun doTask(activity: AppCompatActivity) { var failedItem: String? = null + Looper.prepare() for (element in filesToCopy) { failedItem = if (element.isDirectory) { recursiveCopyDirectory(element.fullPath, currentDirectoryPath) } else { - if (copyFile(element.fullPath, PathUtils.path_join(currentDirectoryPath, element.name))) null else element.fullPath + val dstPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, element.name)) + if (dstPath == null){ + "" + } else { + if (copyFile(element.fullPath, dstPath)) null else element.fullPath + } } - if (failedItem != null) { + if (failedItem != null && failedItem.isNotEmpty()) { stopTask { ColoredAlertDialogBuilder(activity) .setTitle(R.string.error) @@ -483,25 +498,33 @@ class ExplorerActivity : BaseExplorerActivity() { return e.fullPath } } else { - if (!copyFile(e.fullPath, dstPath)) { - return e.fullPath + val checkedDstPath = checkFileOverwrite(dstPath) + if (checkedDstPath == null){ + return "" + } else { + if (!copyFile(e.fullPath, dstPath)) { + return e.fullPath + } } } } return null } - private fun importFileFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): Boolean { + private fun importFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): Boolean { var success = true - val srcHandleID = remote_gocryptfsVolume.openReadMode(srcPath) + val srcHandleID = remoteGocryptfsVolume.openReadMode(srcPath) if (srcHandleID != -1) { val dstHandleID = gocryptfsVolume.openWriteMode(dstPath) if (dstHandleID != -1) { var length: Int val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS) var offset: Long = 0 - while (remote_gocryptfsVolume.readFile(srcHandleID, offset, ioBuffer).also { length = it } > 0){ - val written = gocryptfsVolume.writeFile(dstHandleID, offset, ioBuffer, length).toLong() + while (remoteGocryptfsVolume.readFile(srcHandleID, offset, ioBuffer) + .also { length = it } > 0 + ) { + val written = + gocryptfsVolume.writeFile(dstHandleID, offset, ioBuffer, length).toLong() if (written == length.toLong()) { offset += length.toLong() } else { @@ -511,11 +534,20 @@ class ExplorerActivity : BaseExplorerActivity() { } gocryptfsVolume.closeFile(dstHandleID) } - remote_gocryptfsVolume.closeFile(srcHandleID) + remoteGocryptfsVolume.closeFile(srcHandleID) } return success } + private fun safeImportFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): String? { + val checkedDstPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, File(dstPath).name)) + return if (checkedDstPath == null){ + "" + } else { + if (importFileFromOtherVolume(remoteGocryptfsVolume, srcPath, checkedDstPath)) null else dstPath + } + } + private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, outputPath: String): String? { val mappedElements = gocryptfsVolume.recursiveMapFiles(remote_directory_path) val dstDirectoryPath = PathUtils.path_join(outputPath, File(remote_directory_path).name) @@ -531,8 +563,13 @@ class ExplorerActivity : BaseExplorerActivity() { return e.fullPath } } else { - if (!importFileFromOtherVolume(remote_gocryptfsVolume, e.fullPath, dstPath)) { - return e.fullPath + val checkedDstPath = checkFileOverwrite(dstPath) + if (checkedDstPath == null){ + return "" + } else { + if (!importFileFromOtherVolume(remote_gocryptfsVolume, e.fullPath, checkedDstPath)) { + return e.fullPath + } } } } diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt index 782c1eb..bad2e3a 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt @@ -2,10 +2,13 @@ package sushi.hardcore.droidfs.explorers import android.content.Intent import android.net.Uri +import android.os.Looper import android.view.Menu import android.view.MenuItem import android.view.View +import androidx.appcompat.app.AppCompatActivity import sushi.hardcore.droidfs.R +import sushi.hardcore.droidfs.util.LoadingTask import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder @@ -28,43 +31,64 @@ class ExplorerActivityDrop : BaseExplorerActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.validate -> { - val alertDialog = ColoredAlertDialogBuilder(this) - alertDialog.setCancelable(false) - alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() } - var errorMsg: String? = null - val extras = intent.extras - if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)){ - if (intent.action == Intent.ACTION_SEND) { - val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM) - val outputPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri)) - errorMsg = if (gocryptfsVolume.importFile(this, uri, outputPath)) null else getString(R.string.import_failed, outputPath) - } else if (intent.action == Intent.ACTION_SEND_MULTIPLE) { - val uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM) - if (uris != null){ - for (uri in uris) { - val outputPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri)) - if (!gocryptfsVolume.importFile(this, uri, outputPath)) { - errorMsg = getString(R.string.import_failed, outputPath) - break + object : LoadingTask(this, R.string.loading_msg_import) { + override fun doTask(activity: AppCompatActivity) { + val alertDialog = ColoredAlertDialogBuilder(activity) + alertDialog.setCancelable(false) + alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() } + var errorMsg: String? = null + val extras = intent.extras + if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)){ + Looper.prepare() + if (intent.action == Intent.ACTION_SEND) { + val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM) + errorMsg = if (uri == null){ + getString(R.string.share_intent_parsing_failed) + } else { + val outputPathTest = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)) + val outputPath = checkFileOverwrite(outputPathTest) + if (outputPath == null) { + "" + } else { + if (gocryptfsVolume.importFile(activity, uri, outputPath)) null else getString(R.string.import_failed, outputPath) + } } + } else if (intent.action == Intent.ACTION_SEND_MULTIPLE) { + val uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM) + if (uris != null){ + for (uri in uris) { + val outputPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri))) + if (outputPath == null){ + errorMsg = "" + break + } else { + if (!gocryptfsVolume.importFile(activity, uri, outputPath)) { + errorMsg = getString(R.string.import_failed, outputPath) + break + } + } + } + } else { + errorMsg = getString(R.string.share_intent_parsing_failed) + } + } else { + errorMsg = getString(R.string.share_intent_parsing_failed) } } else { errorMsg = getString(R.string.share_intent_parsing_failed) } - } else { - errorMsg = getString(R.string.share_intent_parsing_failed) + if (errorMsg == null || errorMsg.isNotEmpty()){ + if (errorMsg == null) { + alertDialog.setTitle(R.string.success_import) + alertDialog.setMessage(R.string.success_import_msg) + } else if (errorMsg.isNotEmpty()) { + alertDialog.setTitle(R.string.error) + alertDialog.setMessage(errorMsg) + } + stopTask { alertDialog.show() } + } } - } else { - errorMsg = getString(R.string.share_intent_parsing_failed) } - if (errorMsg == null) { - alertDialog.setTitle(R.string.success_import) - alertDialog.setMessage(R.string.success_import_msg) - } else { - alertDialog.setTitle(R.string.error) - alertDialog.setMessage(errorMsg) - } - alertDialog.show() true } else -> super.onOptionsItemSelected(item) diff --git a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt index 3c01103..63ae814 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/file_viewers/MediaPlayer.kt @@ -1,5 +1,6 @@ package sushi.hardcore.droidfs.file_viewers +import android.view.WindowManager import androidx.appcompat.app.AlertDialog import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.Player @@ -56,6 +57,14 @@ abstract class MediaPlayer: FileViewerActivity() { errorDialog.show() } } + + override fun onIsPlayingChanged(isPlaying: Boolean) { + if (isPlaying){ + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + } }) player.prepare(LoopingMediaSource(mediaSource), false, false) } diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/LoadingTask.kt b/app/src/main/java/sushi/hardcore/droidfs/util/LoadingTask.kt index 241cc81..f1c8825 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/util/LoadingTask.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/util/LoadingTask.kt @@ -14,6 +14,7 @@ abstract class LoadingTask(private val activity: AppCompatActivity, private val .setTitle(R.string.loading) .setCancelable(false) .create() + private var isStopped = false init { dialogLoadingView.findViewById(R.id.text_message).text = activity.getString(loadingMessageResId) startTask() @@ -24,13 +25,19 @@ abstract class LoadingTask(private val activity: AppCompatActivity, private val dialogLoading.show() Thread { doTask(activity) + if (!isStopped){ + dialogLoading.dismiss() + } activity.runOnUiThread { doFinally(activity) } }.start() } - protected fun stopTask(onUiThread: () -> Unit){ + protected fun stopTask(onUiThread: (() -> Unit)?){ + isStopped = true dialogLoading.dismiss() - activity.runOnUiThread { - onUiThread() + onUiThread?.let { + activity.runOnUiThread { + onUiThread() + } } } protected fun stopTaskWithToast(stringId: Int){ diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredBorderListView.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredBorderListView.kt index a6226c3..c9c6b33 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredBorderListView.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredBorderListView.kt @@ -6,7 +6,6 @@ import android.graphics.drawable.DrawableContainer import android.graphics.drawable.GradientDrawable import android.graphics.drawable.StateListDrawable import android.util.AttributeSet -import android.util.Log import android.widget.ListView import androidx.core.content.ContextCompat import sushi.hardcore.droidfs.R diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredCheckBox.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredCheckBox.kt index f943b9a..9e26f44 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredCheckBox.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredCheckBox.kt @@ -8,7 +8,7 @@ import androidx.appcompat.widget.AppCompatCheckBox class ColoredCheckBox: AppCompatCheckBox { constructor(context: Context) : super(context) { applyColor() } constructor(context: Context, attrs: AttributeSet): super(context, attrs) { applyColor() } - //constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { applyColor() } + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int): super(context, attrs, defStyleAttr) { applyColor() } private fun applyColor(){ super.setButtonTintList(ColorStateList.valueOf(ThemeColor.getThemeColor(context))) } diff --git a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredEditText.kt b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredEditText.kt index de45fae..08adefa 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredEditText.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/widgets/ColoredEditText.kt @@ -2,16 +2,8 @@ package sushi.hardcore.droidfs.widgets import android.content.Context import android.content.res.ColorStateList -import android.graphics.Color -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter -import android.graphics.drawable.Drawable -import android.os.Build import android.util.AttributeSet -import android.widget.TextView import androidx.appcompat.widget.AppCompatEditText -import androidx.core.content.ContextCompat -import sushi.hardcore.droidfs.R class ColoredEditText: AppCompatEditText { constructor(context: Context) : super(context) { applyColor() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c7db361..8f32a11 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -135,7 +135,7 @@ Want to read the documentation, request feature, report bug, read the source code… Check the DroidFS\'s repository ! Share Decrypt - Copying selected items... + Copying selected items… Copy of %1$s failed. The selected items have been successfully copied. Copy successful ! @@ -144,4 +144,6 @@ Picture saved to %1$s Failed to save this picture. N//A + %s already exists, do you want to overwrite it ? + Enter new filename