diff --git a/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt index 884e4f0..650f469 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt @@ -66,7 +66,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { override fun onPictureTaken(result: PictureResult) { take_photo_button.onPhotoTaken() val inputStream = ByteArrayInputStream(result.data) - if (gocryptfsVolume.importFile(inputStream, PathUtils.path_join(outputDirectory, fileName))){ + if (gocryptfsVolume.importFile(inputStream, PathUtils.pathJoin(outputDirectory, fileName))){ Toast.makeText(applicationContext, getString(R.string.picture_save_success, fileName), Toast.LENGTH_SHORT).show() } else { ColoredAlertDialogBuilder(applicationContext) diff --git a/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt index c5ef6f8..f45c2b9 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/ChangePasswordActivity.kt @@ -78,7 +78,7 @@ class ChangePasswordActivity : VolumeActionActivity() { if (resultCode == Activity.RESULT_OK) { if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { if (data?.data != null) { - if (PathUtils.isTreeUriOnPrimaryStorage(data.data)){ + if (PathUtils.isTreeUriOnPrimaryStorage(data.data!!)){ val path = PathUtils.getFullPathFromTreeUri(data.data, this) if (path != null){ edit_volume_path.setText(path) diff --git a/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt index e8b619b..7e18f82 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/CreateActivity.kt @@ -45,7 +45,7 @@ class CreateActivity : VolumeActionActivity() { if (resultCode == Activity.RESULT_OK) { if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { if (data?.data != null) { - if (PathUtils.isTreeUriOnPrimaryStorage(data.data)){ + if (PathUtils.isTreeUriOnPrimaryStorage(data.data!!)){ val path = PathUtils.getFullPathFromTreeUri(data.data, this) if (path != null){ edit_volume_path.setText(path) diff --git a/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt index 73bc52f..c09fdc4 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/OpenActivity.kt @@ -100,21 +100,13 @@ class OpenActivity : VolumeActionActivity() { if (resultCode == Activity.RESULT_OK) { if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { if (data?.data != null) { - if (PathUtils.isTreeUriOnPrimaryStorage(data.data)){ - val path = PathUtils.getFullPathFromTreeUri(data.data, this) - if (path != null){ - edit_volume_path.setText(path) - } else { - ColoredAlertDialogBuilder(this) - .setTitle(R.string.error) - .setMessage(R.string.path_from_uri_null_error_msg) - .setPositiveButton(R.string.ok, null) - .show() - } + val path = PathUtils.getFullPathFromTreeUri(data.data, this) + if (path != null){ + edit_volume_path.setText(path) } else { ColoredAlertDialogBuilder(this) - .setTitle(R.string.warning) - .setMessage(R.string.open_on_sdcard_warning) + .setTitle(R.string.error) + .setMessage(R.string.path_from_uri_null_error_msg) .setPositiveButton(R.string.ok, null) .show() } @@ -129,19 +121,37 @@ class OpenActivity : VolumeActionActivity() { Toast.makeText(this, R.string.enter_volume_path, Toast.LENGTH_SHORT).show() } else { val rootCipherDirFile = File(rootCipherDir) - if (!GocryptfsVolume.isGocryptfsVolume(rootCipherDirFile)){ + if (!rootCipherDirFile.canRead()) { + ColoredAlertDialogBuilder(this) + .setTitle(R.string.error) + .setMessage(R.string.open_cant_read_error) + .setPositiveButton(R.string.ok, null) + .show() + } else if (!GocryptfsVolume.isGocryptfsVolume(rootCipherDirFile)){ ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.error_not_a_volume) .setPositiveButton(R.string.ok, null) .show() } else if (!rootCipherDirFile.canWrite()) { - ColoredAlertDialogBuilder(this) - .setTitle(R.string.warning) - .setMessage(R.string.open_cant_write_warning) - .setCancelable(false) - .setPositiveButton(R.string.ok) { _, _ -> openVolume() } - .show() + if ((intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE) && intent.extras != null) { //import via android share menu + ColoredAlertDialogBuilder(this) + .setTitle(R.string.error) + .setMessage(R.string.open_cant_write_error_msg) + .setPositiveButton(R.string.ok, null) + .show() + } else { + val dialog = ColoredAlertDialogBuilder(this) + .setTitle(R.string.warning) + .setCancelable(false) + .setPositiveButton(R.string.ok) { _, _ -> openVolume() } + if (PathUtils.isPathOnExternalStorage(rootCipherDir, this)){ + dialog.setMessage(R.string.open_on_sdcard_warning) + } else { + dialog.setMessage(R.string.open_cant_write_warning) + } + dialog.show() + } } else { openVolume() } 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 c38ce77..1c51d84 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt @@ -1,6 +1,8 @@ package sushi.hardcore.droidfs.explorers +import android.content.DialogInterface import android.content.Intent +import android.net.Uri import android.os.Bundle import android.os.Handler import android.os.Looper @@ -35,9 +37,11 @@ import sushi.hardcore.droidfs.file_viewers.VideoPlayer import sushi.hardcore.droidfs.provider.RestrictedFileProvider import sushi.hardcore.droidfs.util.ExternalProvider import sushi.hardcore.droidfs.util.GocryptfsVolume +import sushi.hardcore.droidfs.util.LoadingTask import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import java.io.File +import java.io.FileNotFoundException open class BaseExplorerActivity : BaseActivity() { private lateinit var sortOrderEntries: Array @@ -235,7 +239,7 @@ open class BaseExplorerActivity : BaseActivity() { if (folder_name.isEmpty()) { Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() } else { - if (!gocryptfsVolume.mkdir(PathUtils.path_join(currentDirectoryPath, folder_name))) { + if (!gocryptfsVolume.mkdir(PathUtils.pathJoin(currentDirectoryPath, folder_name))) { ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(R.string.error_mkdir) @@ -291,13 +295,13 @@ open class BaseExplorerActivity : BaseActivity() { .setView(dialogEditTextView) .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) }) + handler.sendMessage(Message().apply { obj = checkPathOverwrite(PathUtils.pathJoin(PathUtils.getParentPath(path), dialogEditText.text.toString()), isDirectory) }) } .setNegativeButton(R.string.cancel) { _, _ -> handler.sendMessage(Message().apply { obj = null }) } .create() dialogEditText.setOnEditorActionListener { _, _, _ -> dialog.dismiss() - handler.sendMessage(Message().apply { obj = checkPathOverwrite(PathUtils.path_join(PathUtils.getParentPath(path), dialogEditText.text.toString()), isDirectory) }) + handler.sendMessage(Message().apply { obj = checkPathOverwrite(PathUtils.pathJoin(PathUtils.getParentPath(path), dialogEditText.text.toString()), isDirectory) }) true } dialog.setOnCancelListener { handler.sendMessage(Message().apply { obj = null }) } @@ -317,11 +321,57 @@ open class BaseExplorerActivity : BaseActivity() { return outputPath } + protected fun importFilesFromUris(uris: List, task: LoadingTask, callback: (DialogInterface.OnClickListener)? = null): Boolean { + var success = false + for (uri in uris) { + val fileName = PathUtils.getFilenameFromURI(task.activity, uri) + if (fileName == null){ + task.stopTask { + ColoredAlertDialogBuilder(task.activity) + .setTitle(R.string.error) + .setMessage(getString(R.string.error_retrieving_filename, uri)) + .setPositiveButton(R.string.ok, null) + .show() + } + success = false + break + } else { + val dstPath = checkPathOverwrite(PathUtils.pathJoin(currentDirectoryPath, fileName), false) + if (dstPath == null){ + break + } else { + var message: String? = null + try { + success = gocryptfsVolume.importFile(task.activity, uri, dstPath) + } catch (e: FileNotFoundException){ + message = if (e.message != null){ + e.message!!+"\n" + } else { + "" + } + } + if (!success || message != null) { + task.stopTask { + ColoredAlertDialogBuilder(task.activity) + .setTitle(R.string.error) + .setMessage((message ?: "")+getString(R.string.import_failed, uri)) + .setCancelable(callback == null) + .setPositiveButton(R.string.ok, callback) + .show() + } + break + } + } + } + } + return success + } + 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() } else { - if (!gocryptfsVolume.rename(PathUtils.path_join(currentDirectoryPath, old_name), PathUtils.path_join(currentDirectoryPath, new_name))) { + if (!gocryptfsVolume.rename(PathUtils.pathJoin(currentDirectoryPath, old_name), PathUtils.pathJoin(currentDirectoryPath, new_name))) { ColoredAlertDialogBuilder(this) .setTitle(R.string.error) .setMessage(getString(R.string.rename_failed, old_name)) @@ -400,7 +450,7 @@ open class BaseExplorerActivity : BaseActivity() { } R.id.external_open -> { if (usf_open){ - openWithExternalApp(PathUtils.path_join(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name)) + openWithExternalApp(PathUtils.pathJoin(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name)) unselectAll() } true 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 1ded664..5f9070d 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivity.kt @@ -47,7 +47,7 @@ class ExplorerActivity : BaseExplorerActivity() { if (fileName.isEmpty()) { Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() } else { - checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, fileName), false)?.let { + checkPathOverwrite(PathUtils.pathJoin(currentDirectoryPath, fileName), false)?.let { val handleID = gocryptfsVolume.openWriteMode(it) if (handleID == -1) { ColoredAlertDialogBuilder(this) @@ -153,28 +153,7 @@ class ExplorerActivity : BaseExplorerActivity() { uris.add(singleUri) } Looper.prepare() - var success = false - for (uri in uris) { - val dstPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)), false) - 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) { + if (importFilesFromUris(uris, this)) { stopTask { ColoredAlertDialogBuilder(activity) .setTitle(R.string.success_import) @@ -185,7 +164,7 @@ class ExplorerActivity : BaseExplorerActivity() { .setPositiveButton(R.string.yes) { _, _ -> object : LoadingTask(activity, R.string.loading_msg_wipe){ override fun doTask(activity: AppCompatActivity) { - success = true + var success = true for (uri in uris) { val errorMsg = Wiper.wipe(activity, uri) if (errorMsg != null) { @@ -232,7 +211,7 @@ class ExplorerActivity : BaseExplorerActivity() { var failedItem: String? = null for (i in explorerAdapter.selectedItems) { val element = explorerAdapter.getItem(i) - val fullPath = PathUtils.path_join(currentDirectoryPath, element.name) + val fullPath = PathUtils.pathJoin(currentDirectoryPath, element.name) failedItem = if (element.isDirectory) { recursiveExportDirectory(fullPath, treeDocumentFile) } else { @@ -283,7 +262,7 @@ class ExplorerActivity : BaseExplorerActivity() { failedItem = if (types[i] == 0) { //directory recursiveImportDirectoryFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath) } else { - safeImportFileFromOtherVolume(remoteGocryptfsVolume, paths[i], PathUtils.path_join(currentDirectoryPath, File(paths[i]).name)) + safeImportFileFromOtherVolume(remoteGocryptfsVolume, paths[i], PathUtils.pathJoin(currentDirectoryPath, File(paths[i]).name)) } if (failedItem != null) { break @@ -291,7 +270,7 @@ class ExplorerActivity : BaseExplorerActivity() { } } } else { - failedItem = safeImportFileFromOtherVolume(remoteGocryptfsVolume, path, PathUtils.path_join(currentDirectoryPath, File(path).name)) + failedItem = safeImportFileFromOtherVolume(remoteGocryptfsVolume, path, PathUtils.pathJoin(currentDirectoryPath, File(path).name)) } if (failedItem == null) { stopTask { @@ -386,7 +365,7 @@ class ExplorerActivity : BaseExplorerActivity() { var failedItem: String? = null Looper.prepare() for (element in itemsToProcess) { - val dstPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, element.name), element.isDirectory) + val dstPath = checkPathOverwrite(PathUtils.pathJoin(currentDirectoryPath, element.name), element.isDirectory) failedItem = if (dstPath == null){ "" } else { @@ -547,7 +526,7 @@ class ExplorerActivity : BaseExplorerActivity() { } } for (e in mappedElements) { - val dstPath = checkPathOverwrite(PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, e.fullPath)), e.isDirectory) + val dstPath = checkPathOverwrite(PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, e.fullPath)), e.isDirectory) if (dstPath == null){ return "" } else { @@ -581,7 +560,7 @@ class ExplorerActivity : BaseExplorerActivity() { private fun moveElements(elements: List, dstDirectoryPath: String): String? { for (element in elements){ - val dstPath = checkPathOverwrite(PathUtils.path_join(dstDirectoryPath, element.name), element.isDirectory) + val dstPath = checkPathOverwrite(PathUtils.pathJoin(dstDirectoryPath, element.name), element.isDirectory) if (dstPath == null){ return "" } else { @@ -636,7 +615,7 @@ class ExplorerActivity : BaseExplorerActivity() { private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, outputPath: String): String? { val mappedElements = remote_gocryptfsVolume.recursiveMapFiles(remote_directory_path) - val dstDirectoryPath = checkPathOverwrite(PathUtils.path_join(outputPath, File(remote_directory_path).name), true) + val dstDirectoryPath = checkPathOverwrite(PathUtils.pathJoin(outputPath, File(remote_directory_path).name), true) if (dstDirectoryPath == null){ return "" } else { @@ -646,7 +625,7 @@ class ExplorerActivity : BaseExplorerActivity() { } } for (e in mappedElements) { - val dstPath = checkPathOverwrite(PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, e.fullPath)), e.isDirectory) + val dstPath = checkPathOverwrite(PathUtils.pathJoin(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, e.fullPath)), e.isDirectory) if (dstPath == null){ return "" } else { @@ -682,7 +661,7 @@ class ExplorerActivity : BaseExplorerActivity() { treeDocumentFile.createDirectory(File(plain_directory_path).name)?.let {childTree -> val explorerElements = gocryptfsVolume.listDir(plain_directory_path) for (e in explorerElements) { - val fullPath = PathUtils.path_join(plain_directory_path, e.name) + val fullPath = PathUtils.pathJoin(plain_directory_path, e.name) if (e.isDirectory) { val failedItem = recursiveExportDirectory(fullPath, childTree) failedItem?.let { return it } @@ -700,7 +679,7 @@ class ExplorerActivity : BaseExplorerActivity() { private fun recursiveRemoveDirectory(plain_directory_path: String): String? { val explorerElements = gocryptfsVolume.listDir(plain_directory_path) for (e in explorerElements) { - val fullPath = PathUtils.path_join(plain_directory_path, e.name) + val fullPath = PathUtils.pathJoin(plain_directory_path, e.name) if (e.isDirectory) { val result = recursiveRemoveDirectory(fullPath) result?.let { return it } @@ -721,7 +700,7 @@ class ExplorerActivity : BaseExplorerActivity() { var failedItem: String? = null for (i in explorerAdapter.selectedItems) { val element = explorerAdapter.getItem(i) - val fullPath = PathUtils.path_join(currentDirectoryPath, element.name) + val fullPath = PathUtils.pathJoin(currentDirectoryPath, element.name) if (element.isDirectory) { val result = recursiveRemoveDirectory(fullPath) result?.let{ failedItem = it } 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 836275d..b623dbd 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityDrop.kt @@ -1,5 +1,6 @@ package sushi.hardcore.droidfs.explorers +import android.content.DialogInterface import android.content.Intent import android.net.Uri import android.os.Looper @@ -9,7 +10,6 @@ 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 class ExplorerActivityDrop : BaseExplorerActivity() { @@ -36,42 +36,30 @@ class ExplorerActivityDrop : BaseExplorerActivity() { val alertDialog = ColoredAlertDialogBuilder(activity) alertDialog.setCancelable(false) alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() } - var errorMsg: String? = null + val errorMsg: String? 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 outputPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)), false) - if (outputPath == null) { - "" + when (intent.action) { + Intent.ACTION_SEND -> { + val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM) + errorMsg = if (uri == null){ + getString(R.string.share_intent_parsing_failed) } else { - if (gocryptfsVolume.importFile(activity, uri, outputPath)) null else getString(R.string.import_failed, uri) + if (importFilesFromUris(listOf(uri), this){ _, _ -> finish() }) null else "" } } - } else if (intent.action == Intent.ACTION_SEND_MULTIPLE) { - val uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM) - if (uris != null){ - for (uri in uris) { - val outputPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)), false) - if (outputPath == null){ - errorMsg = "" - break - } else { - if (!gocryptfsVolume.importFile(activity, uri, outputPath)) { - errorMsg = getString(R.string.import_failed, uri) - break - } - } + Intent.ACTION_SEND_MULTIPLE -> { + val uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM) + errorMsg = if (uris != null){ + if (importFilesFromUris(uris, this){ _, _ -> finish() }) null else "" + } else { + getString(R.string.share_intent_parsing_failed) } - } else { + } + 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) diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt index d5f2fe3..8003a35 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerActivityPick.kt @@ -22,7 +22,7 @@ class ExplorerActivityPick : BaseExplorerActivity() { explorerAdapter.onItemClick(position) if (explorerAdapter.selectedItems.isEmpty()) { if (!wasSelecting) { - val fullPath = PathUtils.path_join(currentDirectoryPath, explorerElements[position].name) + val fullPath = PathUtils.pathJoin(currentDirectoryPath, explorerElements[position].name) when { explorerElements[position].isDirectory -> { setCurrentPath(fullPath) @@ -61,7 +61,7 @@ class ExplorerActivityPick : BaseExplorerActivity() { val types = ArrayList() for (i in explorerAdapter.selectedItems) { val e = explorerElements[i] - paths.add(PathUtils.path_join(currentDirectoryPath, e.name)) + paths.add(PathUtils.pathJoin(currentDirectoryPath, e.name)) types.add(e.elementType.toInt()) } resultIntent.putStringArrayListExtra("paths", paths) diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt index 796b0dd..f011bd4 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/ExplorerElement.kt @@ -5,7 +5,7 @@ import java.util.* class ExplorerElement(val name: String, val elementType: Short, var size: Long, mtime: Long, parentPath: String) { val mTime = Date((mtime * 1000).toString().toLong()) - val fullPath: String = PathUtils.path_join(parentPath, name) + val fullPath: String = PathUtils.pathJoin(parentPath, name) val isDirectory: Boolean get() = elementType.toInt() == 0 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 f1c8825..c1ba88e 100644 --- a/app/src/main/java/sushi/hardcore/droidfs/util/LoadingTask.kt +++ b/app/src/main/java/sushi/hardcore/droidfs/util/LoadingTask.kt @@ -7,7 +7,7 @@ import androidx.appcompat.app.AppCompatActivity import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder -abstract class LoadingTask(private val activity: AppCompatActivity, private val loadingMessageResId: Int) { +abstract class LoadingTask(val activity: AppCompatActivity, loadingMessageResId: Int) { private val dialogLoadingView = activity.layoutInflater.inflate(R.layout.dialog_loading, null) private val dialogLoading: AlertDialog = ColoredAlertDialogBuilder(activity) .setView(dialogLoadingView) @@ -31,7 +31,7 @@ abstract class LoadingTask(private val activity: AppCompatActivity, private val activity.runOnUiThread { doFinally(activity) } }.start() } - protected fun stopTask(onUiThread: (() -> Unit)?){ + fun stopTask(onUiThread: (() -> Unit)?){ isStopped = true dialogLoading.dismiss() onUiThread?.let { diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.java b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.java deleted file mode 100644 index bef2ea2..0000000 --- a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.java +++ /dev/null @@ -1,157 +0,0 @@ -package sushi.hardcore.droidfs.util; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.storage.StorageManager; -import android.provider.DocumentsContract; -import android.provider.OpenableColumns; - -import androidx.annotation.Nullable; - -import java.io.File; -import java.lang.reflect.Array; -import java.lang.reflect.Method; -import java.text.DecimalFormat; - -public class PathUtils { - - public static String getParentPath(String path){ - if (path.endsWith("/")){ - String a = path.substring(0, path.length()-2); - if (a.contains("/")){ - return a.substring(0, a.lastIndexOf("/")); - } else { - return ""; - } - } else { - if (path.contains("/")){ - return path.substring(0, path.lastIndexOf("/")); - } else { - return ""; - } - } - } - - public static String path_join(String... strings){ - StringBuilder result = new StringBuilder(); - for (String element : strings){ - if (!element.isEmpty()){ - if (!element.endsWith("/")){ - element += "/"; - } - result.append(element); - } - } - return result.substring(0, result.length()-1); - } - - public static String getRelativePath(String parentPath, String childPath){ - return childPath.substring(parentPath.length()+1); - } - - public static String getFilenameFromURI(Context context, Uri uri){ - String result = null; - if (uri.getScheme().equals("content")){ - Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); - if (cursor != null){ - try { - if (cursor.moveToFirst()){ - result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } - } finally { - cursor.close(); - } - } - } - if (result == null){ - result = uri.getPath(); - int cut = result.lastIndexOf('/'); - if (cut != -1){ - result = result.substring(cut + 1); - } - } - return result; - } - - static final String[] units = new String[]{"B", "kB", "MB", "GB", "TB"}; - public static String formatSize(long size){ - if (size <= 0){ - return "0 B"; - } - int digitGroups = (int)(Math.log10(size)/Math.log10(1024)); - return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups))+" "+units[digitGroups]; - } - - public static Boolean isTreeUriOnPrimaryStorage(Uri treeUri){ - String volumeId = getVolumeIdFromTreeUri(treeUri); - if (volumeId != null) { - return volumeId.equals(PRIMARY_VOLUME_NAME) || volumeId.equals("home") || volumeId.equals("downloads"); - } else { - return false; - } - } - - private static final String PRIMARY_VOLUME_NAME = "primary"; - @Nullable - public static String getFullPathFromTreeUri(@Nullable Uri treeUri, Context context) { - if (treeUri == null) return null; - if ("content".equalsIgnoreCase(treeUri.getScheme())) { - String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri),context); - if (volumePath == null) return null; - if (volumePath.endsWith(File.separator)) - volumePath = volumePath.substring(0, volumePath.length() - 1); - String documentPath = getDocumentPathFromTreeUri(treeUri); - if (documentPath.endsWith(File.separator)) - documentPath = documentPath.substring(0, documentPath.length() - 1); - if (documentPath.length() > 0) { - return path_join(volumePath, documentPath); - } - else return volumePath; - } else if ("file".equalsIgnoreCase(treeUri.getScheme())) { - return treeUri.getPath(); - } - return null; - } - - private static String getVolumePath(final String volumeId, Context context) { - try { - StorageManager mStorageManager = - (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); - Class storageVolumeClazz = Class.forName("android.os.storage.StorageVolume"); - Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList"); - Method getUuid = storageVolumeClazz.getMethod("getUuid"); - Method getPath = storageVolumeClazz.getMethod("getPath"); - Method isPrimary = storageVolumeClazz.getMethod("isPrimary"); - Object result = getVolumeList.invoke(mStorageManager); - - final int length = Array.getLength(result); - for (int i = 0; i < length; i++) { - Object storageVolumeElement = Array.get(result, i); - String uuid = (String) getUuid.invoke(storageVolumeElement); - Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement); - if (primary && PRIMARY_VOLUME_NAME.equals(volumeId)) - return (String) getPath.invoke(storageVolumeElement); - if (uuid != null && uuid.equals(volumeId)) - return (String) getPath.invoke(storageVolumeElement); - } - return null; - } catch (Exception ex) { - return null; - } - } - - private static String getVolumeIdFromTreeUri(final Uri treeUri) { - final String docId = DocumentsContract.getTreeDocumentId(treeUri); - final String[] split = docId.split(":"); - if (split.length > 0) return split[0]; - else return null; - } - - private static String getDocumentPathFromTreeUri(final Uri treeUri) { - final String docId = DocumentsContract.getTreeDocumentId(treeUri); - final String[] split = docId.split(":"); - if ((split.length >= 2) && (split[1] != null)) return split[1]; - else return File.separator; - } -} diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt new file mode 100644 index 0000000..d2a6384 --- /dev/null +++ b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt @@ -0,0 +1,163 @@ +package sushi.hardcore.droidfs.util + +import android.content.Context +import android.net.Uri +import android.os.storage.StorageManager +import android.provider.DocumentsContract +import android.provider.OpenableColumns +import androidx.core.content.ContextCompat +import java.io.File +import java.text.DecimalFormat +import kotlin.math.log10 +import kotlin.math.pow + +object PathUtils { + fun getParentPath(path: String): String { + return if (path.endsWith("/")) { + val a = path.substring(0, path.length - 2) + if (a.contains("/")) { + a.substring(0, a.lastIndexOf("/")) + } else { + "" + } + } else { + if (path.contains("/")) { + path.substring(0, path.lastIndexOf("/")) + } else { + "" + } + } + } + + fun pathJoin(vararg strings: String): String { + val result = StringBuilder() + for (element in strings) { + if (element.isNotEmpty()) { + result.append(element) + if (!element.endsWith("/")) { + result.append("/") + } + } + } + return result.substring(0, result.length - 1) + } + + fun getRelativePath(parentPath: String, childPath: String): String { + return childPath.substring(parentPath.length + 1) + } + + fun getFilenameFromURI(context: Context, uri: Uri): String? { + var result: String? = null + if (uri.scheme == "content") { + context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> + if (cursor.moveToFirst()) { + result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)) + } + } + } + if (result == null) { + result = uri.path + result?.let { + val cut = it.lastIndexOf('/') + if (cut != -1) { + result = it.substring(cut + 1) + } + } + } + return result + } + + private val units = arrayOf("B", "kB", "MB", "GB", "TB") + fun formatSize(size: Long): String { + if (size <= 0) { + return "0 B" + } + val digitGroups = (log10(size.toDouble()) / log10(1024.0)).toInt() + return DecimalFormat("#,##0.#").format(size / 1024.0.pow(digitGroups.toDouble()) + ) + " " + units[digitGroups] + } + + fun isTreeUriOnPrimaryStorage(treeUri: Uri): Boolean { + val volumeId = getVolumeIdFromTreeUri(treeUri) + return if (volumeId != null) { + volumeId == PRIMARY_VOLUME_NAME || volumeId == "home" || volumeId == "downloads" + } else { + false + } + } + + private fun getExternalStoragePath(context: Context): List { + val externalPaths: MutableList = ArrayList() + ContextCompat.getExternalFilesDirs(context, null).forEach { + val rootPath = it.path.substring(0, it.path.indexOf(pathJoin("Android/data/", context.packageName, "files"))) + if (!rootPath.endsWith("/0/")){ //not primary storage + externalPaths.add(rootPath) + } + } + return externalPaths + } + + fun isPathOnExternalStorage(path: String, context: Context): Boolean { + getExternalStoragePath(context).forEach { + if (path.startsWith(it)){ + return true + } + } + return false + } + + private const val PRIMARY_VOLUME_NAME = "primary" + fun getFullPathFromTreeUri(treeUri: Uri?, context: Context): String? { + if (treeUri == null) return null + if ("content".equals(treeUri.scheme, ignoreCase = true)) { + val vId = getVolumeIdFromTreeUri(treeUri) + var volumePath = getVolumePath(vId, context) ?: return null + if (volumePath.endsWith(File.separator)) + volumePath = volumePath.substring(0, volumePath.length - 1) + var documentPath = getDocumentPathFromTreeUri(treeUri) + if (documentPath!!.endsWith(File.separator)) + documentPath = documentPath.substring(0, documentPath.length - 1) + return if (documentPath.isNotEmpty()) { + pathJoin(volumePath, documentPath) + } else volumePath + } else if ("file".equals(treeUri.scheme, ignoreCase = true)) { + return treeUri.path + } + return null + } + + private fun getVolumePath(volumeId: String?, context: Context): String? { + return try { + val mStorageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager + val storageVolumeClazz = Class.forName("android.os.storage.StorageVolume") + val getVolumeList = mStorageManager.javaClass.getMethod("getVolumeList") + val getUuid = storageVolumeClazz.getMethod("getUuid") + val getPath = storageVolumeClazz.getMethod("getPath") + val isPrimary = storageVolumeClazz.getMethod("isPrimary") + val result = getVolumeList.invoke(mStorageManager) + val length = java.lang.reflect.Array.getLength(result!!) + for (i in 0 until length) { + val storageVolumeElement = java.lang.reflect.Array.get(result, i) + val uuid = getUuid.invoke(storageVolumeElement) + val primary = isPrimary.invoke(storageVolumeElement) as Boolean + if (primary && PRIMARY_VOLUME_NAME == volumeId) return getPath.invoke(storageVolumeElement) as String + if (uuid == volumeId) return getPath.invoke(storageVolumeElement) as String + } + null + } catch (ex: Exception) { + null + } + } + + private fun getVolumeIdFromTreeUri(treeUri: Uri): String? { + val docId = DocumentsContract.getTreeDocumentId(treeUri) + val split = docId.split(":").toTypedArray() + return if (split.isNotEmpty()) split[0] else null + } + + private fun getDocumentPathFromTreeUri(treeUri: Uri): String? { + val docId = DocumentsContract.getTreeDocumentId(treeUri) + val split: Array = docId.split(":").toTypedArray() + return if (split.size >= 2 && split[1] != null) split[1] else File.separator + } +} \ 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 35a60fc..d82890c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,7 +26,7 @@ Export of %s failed. Export successful ! The selected files have been successfully exported. - Deletion of %s failed + Deletion of %s failed. Passwords don\'t match The selected directory isn\'t empty Volume successfully created ! @@ -164,8 +164,10 @@ Failed to retrieve the selected path. DroidFS doesn\'t have write access to this path. Please try another location. DroidFS can\'t write on removable SD cards, please select a path on internal storage. - DroidFS can\'t write on removable SD cards. You will only have read-only access to the volumes therein. - DroidFS doesn\'t have write access to this path. You will only have read-only access to this volume. + DroidFS can\'t write on removable SD cards. Opening volume with read-only access. + DroidFS doesn\'t have write access to this path. Opening volume with read-only access. + DroidFS doesn\'t have write access to this path. Please try another volume. + DroidFS doesn\'t have read access to this path. You can try to move the volume to a readable location. DroidFS doesn\'t have write access to this path. You can try to move the volume to a writable location. DroidFS can\'t write on removable SD cards, please move the volume to internal storage. Slideshow stopped @@ -182,4 +184,5 @@ No more images found. You should read it carefully before enabling any of these options. Unsafe features documentation + Unable to retrieve file name for URI: %s