Move feature

This commit is contained in:
Hardcore Sushi 2020-08-09 14:34:42 +02:00
parent 35a2e35bdc
commit cac264043f
6 changed files with 208 additions and 105 deletions

View File

@ -250,7 +250,7 @@ open class BaseExplorerActivity : BaseActivity() {
dialog.show() dialog.show()
} }
protected fun checkFileOverwrite(path: String): String? { protected fun checkPathOverwrite(path: String, isDirectory: Boolean): String? {
var outputPath: String? = null var outputPath: String? = null
if (gocryptfsVolume.pathExists(path)){ if (gocryptfsVolume.pathExists(path)){
val fileName = File(path).name val fileName = File(path).name
@ -261,7 +261,7 @@ open class BaseExplorerActivity : BaseActivity() {
runOnUiThread { runOnUiThread {
val dialog = ColoredAlertDialogBuilder(this) val dialog = ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning) .setTitle(R.string.warning)
.setMessage(getString(R.string.file_overwrite_question, fileName)) .setMessage(getString(if (isDirectory){R.string.dir_overwrite_question} else {R.string.file_overwrite_question}, path))
.setNegativeButton(R.string.no) { _, _ -> .setNegativeButton(R.string.no) { _, _ ->
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null) val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text) val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
@ -269,15 +269,15 @@ open class BaseExplorerActivity : BaseActivity() {
dialogEditText.selectAll() dialogEditText.selectAll()
val dialog = ColoredAlertDialogBuilder(this) val dialog = ColoredAlertDialogBuilder(this)
.setView(dialogEditTextView) .setView(dialogEditTextView)
.setTitle(getString(R.string.enter_new_filename)) .setTitle(getString(R.string.enter_new_name))
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
handler.sendMessage(Message().apply { obj = checkFileOverwrite(PathUtils.path_join(PathUtils.getParentPath(path), dialogEditText.text.toString())) }) handler.sendMessage(Message().apply { obj = checkPathOverwrite(PathUtils.path_join(PathUtils.getParentPath(path), dialogEditText.text.toString()), isDirectory) })
} }
.setNegativeButton(R.string.cancel) { _, _ -> handler.sendMessage(Message().apply { obj = null }) } .setNegativeButton(R.string.cancel) { _, _ -> handler.sendMessage(Message().apply { obj = null }) }
.create() .create()
dialogEditText.setOnEditorActionListener { _, _, _ -> dialogEditText.setOnEditorActionListener { _, _, _ ->
dialog.dismiss() dialog.dismiss()
handler.sendMessage(Message().apply { obj = checkFileOverwrite(PathUtils.path_join(PathUtils.getParentPath(path), dialogEditText.text.toString())) }) handler.sendMessage(Message().apply { obj = checkPathOverwrite(PathUtils.path_join(PathUtils.getParentPath(path), dialogEditText.text.toString()), isDirectory) })
true true
} }
dialog.setOnCancelListener { handler.sendMessage(Message().apply { obj = null }) } dialog.setOnCancelListener { handler.sendMessage(Message().apply { obj = null }) }

View File

@ -18,16 +18,18 @@ import sushi.hardcore.droidfs.adapters.IconTextDialogAdapter
import sushi.hardcore.droidfs.util.* import sushi.hardcore.droidfs.util.*
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import kotlin.collections.ArrayList
class ExplorerActivity : BaseExplorerActivity() { class ExplorerActivity : BaseExplorerActivity() {
private val PICK_DIRECTORY_REQUEST_CODE = 1 companion object {
private val PICK_FILES_REQUEST_CODE = 2 private const val PICK_DIRECTORY_REQUEST_CODE = 1
private val PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE = 3 private const val PICK_FILES_REQUEST_CODE = 2
private const val PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE = 3
private enum class ItemsActions {NONE, COPY, MOVE}
}
private var usf_decrypt = false private var usf_decrypt = false
private var usf_share = false private var usf_share = false
private var modeSelectLocation = false private var currentItemAction = ItemsActions.NONE
private val filesToCopy = ArrayList<ExplorerElement>() private val itemsToProcess = ArrayList<ExplorerElement>()
override fun init() { override fun init() {
setContentView(R.layout.activity_explorer) setContentView(R.layout.activity_explorer)
usf_decrypt = sharedPrefs.getBoolean("usf_decrypt", false) usf_decrypt = sharedPrefs.getBoolean("usf_decrypt", false)
@ -35,7 +37,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
override fun onExplorerItemLongClick(position: Int) { override fun onExplorerItemLongClick(position: Int) {
cancelCopy() cancelItemAction()
explorerAdapter.onItemLongClick(position) explorerAdapter.onItemLongClick(position)
invalidateOptionsMenu() invalidateOptionsMenu()
} }
@ -44,7 +46,7 @@ class ExplorerActivity : BaseExplorerActivity() {
if (fileName.isEmpty()) { if (fileName.isEmpty()) {
Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.error_filename_empty, Toast.LENGTH_SHORT).show()
} else { } else {
checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, fileName))?.let { checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, fileName), false)?.let {
val handleID = gocryptfsVolume.openWriteMode(it) val handleID = gocryptfsVolume.openWriteMode(it)
if (handleID == -1) { if (handleID == -1) {
ColoredAlertDialogBuilder(this) ColoredAlertDialogBuilder(this)
@ -62,7 +64,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
fun onClickFAB(view: View) { fun onClickFAB(view: View) {
if (modeSelectLocation){ if (currentItemAction != ItemsActions.NONE){
openDialogCreateFolder() openDialogCreateFolder()
} else { } else {
val adapter = IconTextDialogAdapter(this) val adapter = IconTextDialogAdapter(this)
@ -152,7 +154,7 @@ class ExplorerActivity : BaseExplorerActivity() {
Looper.prepare() Looper.prepare()
var success = false var success = false
for (uri in uris) { for (uri in uris) {
val dstPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri))) val dstPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)), false)
if (dstPath == null){ if (dstPath == null){
break break
} else { } else {
@ -316,7 +318,7 @@ class ExplorerActivity : BaseExplorerActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.explorer, menu) menuInflater.inflate(R.menu.explorer, menu)
if (modeSelectLocation) { if (currentItemAction != ItemsActions.NONE) {
menu.findItem(R.id.validate).isVisible = true menu.findItem(R.id.validate).isVisible = true
menu.findItem(R.id.close).isVisible = false menu.findItem(R.id.close).isVisible = false
} else { } else {
@ -328,6 +330,7 @@ class ExplorerActivity : BaseExplorerActivity() {
menu.findItem(R.id.select_all).isVisible = anyItemSelected menu.findItem(R.id.select_all).isVisible = anyItemSelected
menu.findItem(R.id.delete).isVisible = anyItemSelected menu.findItem(R.id.delete).isVisible = anyItemSelected
menu.findItem(R.id.copy).isVisible = anyItemSelected menu.findItem(R.id.copy).isVisible = anyItemSelected
menu.findItem(R.id.cut).isVisible = anyItemSelected
menu.findItem(R.id.decrypt).isVisible = anyItemSelected && usf_decrypt menu.findItem(R.id.decrypt).isVisible = anyItemSelected && usf_decrypt
if (anyItemSelected && usf_share){ if (anyItemSelected && usf_share){
var containsDir = false var containsDir = false
@ -348,7 +351,7 @@ class ExplorerActivity : BaseExplorerActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
cancelCopy() cancelItemAction()
super.onOptionsItemSelected(item) super.onOptionsItemSelected(item)
} }
R.id.select_all -> { R.id.select_all -> {
@ -356,55 +359,99 @@ class ExplorerActivity : BaseExplorerActivity() {
invalidateOptionsMenu() invalidateOptionsMenu()
true true
} }
R.id.cut -> {
for (i in explorerAdapter.selectedItems){
itemsToProcess.add(explorerElements[i])
}
currentItemAction = ItemsActions.MOVE
unselectAll()
true
}
R.id.copy -> { R.id.copy -> {
for (i in explorerAdapter.selectedItems){ for (i in explorerAdapter.selectedItems){
filesToCopy.add(explorerElements[i]) itemsToProcess.add(explorerElements[i])
} }
modeSelectLocation = true currentItemAction = ItemsActions.COPY
unselectAll() unselectAll()
true true
} }
R.id.validate -> { R.id.validate -> {
object : LoadingTask(this, R.string.loading_msg_copy){ if (currentItemAction == ItemsActions.COPY){
override fun doTask(activity: AppCompatActivity) { object : LoadingTask(this, R.string.loading_msg_copy){
var failedItem: String? = null override fun doTask(activity: AppCompatActivity) {
Looper.prepare() var failedItem: String? = null
for (element in filesToCopy) { Looper.prepare()
failedItem = if (element.isDirectory) { for (element in itemsToProcess) {
recursiveCopyDirectory(element.fullPath, currentDirectoryPath) val dstPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, element.name), element.isDirectory)
} else { failedItem = if (dstPath == null){
val dstPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, element.name))
if (dstPath == null){
"" ""
} else { } else {
if (copyFile(element.fullPath, dstPath)) null else element.fullPath if (element.isDirectory) {
recursiveCopyDirectory(element.fullPath, dstPath)
} else {
if (copyFile(element.fullPath, dstPath)) null else element.fullPath
}
}
if (failedItem != null){
if (failedItem.isNotEmpty()) {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(getString(
R.string.copy_failed,
failedItem
))
.setPositiveButton(R.string.ok, null)
.show()
}
}
break
} }
} }
if (failedItem != null && failedItem.isNotEmpty()) { if (failedItem == null) {
stopTask { stopTask {
ColoredAlertDialogBuilder(activity) ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error) .setTitle(getString(R.string.copy_success))
.setMessage(getString(R.string.copy_failed, failedItem)) .setMessage(getString(R.string.copy_success_msg))
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
} }
break
} }
} }
if (failedItem == null) { override fun doFinally(activity: AppCompatActivity) {
stopTask { cancelItemAction()
ColoredAlertDialogBuilder(activity) unselectAll()
.setTitle(getString(R.string.copy_success)) setCurrentPath(currentDirectoryPath)
.setMessage(getString(R.string.copy_success_msg))
.setPositiveButton(R.string.ok, null)
.show()
}
} }
} }
override fun doFinally(activity: AppCompatActivity) { } else if (currentItemAction == ItemsActions.MOVE){
cancelCopy() object : LoadingTask(this, R.string.loading_msg_move){
unselectAll() override fun doTask(activity: AppCompatActivity) {
setCurrentPath(currentDirectoryPath) Looper.prepare()
val failedItem = moveElements(itemsToProcess, currentDirectoryPath)
if (failedItem == null) {
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(getString(R.string.move_success))
.setMessage(getString(R.string.move_success_msg))
.setPositiveButton(R.string.ok, null)
.show()
}
} else if (failedItem.isNotEmpty()){
stopTask {
ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error)
.setMessage(getString(R.string.move_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
override fun doFinally(activity: AppCompatActivity) {
cancelItemAction()
unselectAll()
setCurrentPath(currentDirectoryPath)
}
} }
} }
true true
@ -443,16 +490,16 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
} }
private fun cancelCopy() { private fun cancelItemAction() {
if (modeSelectLocation){ if (currentItemAction != ItemsActions.NONE){
modeSelectLocation = false currentItemAction = ItemsActions.NONE
filesToCopy.clear() itemsToProcess.clear()
} }
} }
override fun onBackPressed() { override fun onBackPressed() {
if (modeSelectLocation) { if (currentItemAction != ItemsActions.NONE) {
cancelCopy() cancelItemAction()
invalidateOptionsMenu() invalidateOptionsMenu()
} else { } else {
super.onBackPressed() super.onBackPressed()
@ -488,24 +535,24 @@ class ExplorerActivity : BaseExplorerActivity() {
return success return success
} }
private fun recursiveCopyDirectory(srcDirectoryPath: String, outputPath: String): String? { private fun recursiveCopyDirectory(srcDirectoryPath: String, dstDirectoryPath: String): String? {
val mappedElements = gocryptfsVolume.recursiveMapFiles(srcDirectoryPath) val mappedElements = gocryptfsVolume.recursiveMapFiles(srcDirectoryPath)
val dstDirectoryPath = PathUtils.path_join(outputPath, File(srcDirectoryPath).name) if (!gocryptfsVolume.pathExists(dstDirectoryPath)){
if (!gocryptfsVolume.pathExists(dstDirectoryPath)) {
if (!gocryptfsVolume.mkdir(dstDirectoryPath)) { if (!gocryptfsVolume.mkdir(dstDirectoryPath)) {
return dstDirectoryPath return srcDirectoryPath
} }
} }
for (e in mappedElements) { for (e in mappedElements) {
val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, e.fullPath)) val dstPath = checkPathOverwrite(PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(srcDirectoryPath, e.fullPath)), e.isDirectory)
if (e.isDirectory) { if (dstPath == null){
if (!gocryptfsVolume.mkdir(dstPath)){ return ""
return e.fullPath
}
} else { } else {
val checkedDstPath = checkFileOverwrite(dstPath) if (e.isDirectory) {
if (checkedDstPath == null){ if (!gocryptfsVolume.pathExists(dstPath)){
return "" if (!gocryptfsVolume.mkdir(dstPath)){
return e.fullPath
}
}
} else { } else {
if (!copyFile(e.fullPath, dstPath)) { if (!copyFile(e.fullPath, dstPath)) {
return e.fullPath return e.fullPath
@ -516,6 +563,38 @@ class ExplorerActivity : BaseExplorerActivity() {
return null return null
} }
private fun moveDirectory(srcDirectoryPath: String, dstDirectoryPath: String): String? {
if (!gocryptfsVolume.pathExists(dstDirectoryPath)) {
if (!gocryptfsVolume.rename(srcDirectoryPath, dstDirectoryPath)) {
return srcDirectoryPath
}
} else {
moveElements(gocryptfsVolume.listDir(srcDirectoryPath), dstDirectoryPath)
gocryptfsVolume.rmdir(srcDirectoryPath)
}
return null
}
private fun moveElements(elements: List<ExplorerElement>, dstDirectoryPath: String): String? {
for (element in elements){
val dstPath = checkPathOverwrite(PathUtils.path_join(dstDirectoryPath, element.name), element.isDirectory)
if (dstPath == null){
return ""
} else {
if (element.isDirectory){
moveDirectory(element.fullPath, dstPath)?.let{
return it
}
} else {
if (!gocryptfsVolume.rename(element.fullPath, dstPath)){
return element.fullPath
}
}
}
}
return null
}
private fun importFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): Boolean { private fun importFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): Boolean {
var success = true var success = true
val srcHandleID = remoteGocryptfsVolume.openReadMode(srcPath) val srcHandleID = remoteGocryptfsVolume.openReadMode(srcPath)
@ -525,9 +604,7 @@ class ExplorerActivity : BaseExplorerActivity() {
var length: Int var length: Int
val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS) val ioBuffer = ByteArray(GocryptfsVolume.DefaultBS)
var offset: Long = 0 var offset: Long = 0
while (remoteGocryptfsVolume.readFile(srcHandleID, offset, ioBuffer) while (remoteGocryptfsVolume.readFile(srcHandleID, offset, ioBuffer).also { length = it } > 0) {
.also { length = it } > 0
) {
val written = val written =
gocryptfsVolume.writeFile(dstHandleID, offset, ioBuffer, length).toLong() gocryptfsVolume.writeFile(dstHandleID, offset, ioBuffer, length).toLong()
if (written == length.toLong()) { if (written == length.toLong()) {
@ -545,35 +622,40 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
private fun safeImportFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): String? { private fun safeImportFileFromOtherVolume(remoteGocryptfsVolume: GocryptfsVolume, srcPath: String, dstPath: String): String? {
val checkedDstPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, File(dstPath).name)) val checkedDstPath = checkPathOverwrite(dstPath, false)
return if (checkedDstPath == null){ return if (checkedDstPath == null){
"" ""
} else { } else {
if (importFileFromOtherVolume(remoteGocryptfsVolume, srcPath, checkedDstPath)) null else dstPath if (importFileFromOtherVolume(remoteGocryptfsVolume, srcPath, checkedDstPath)) null else srcPath
} }
} }
private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, outputPath: String): String? { private fun recursiveImportDirectoryFromOtherVolume(remote_gocryptfsVolume: GocryptfsVolume, remote_directory_path: String, outputPath: String): String? {
val mappedElements = gocryptfsVolume.recursiveMapFiles(remote_directory_path) val mappedElements = gocryptfsVolume.recursiveMapFiles(remote_directory_path)
val dstDirectoryPath = PathUtils.path_join(outputPath, File(remote_directory_path).name) val dstDirectoryPath = checkPathOverwrite(PathUtils.path_join(outputPath, File(remote_directory_path).name), true)
if (!gocryptfsVolume.pathExists(dstDirectoryPath)) { if (dstDirectoryPath == null){
if (!gocryptfsVolume.mkdir(dstDirectoryPath)) { return ""
return dstDirectoryPath } else {
} if (!gocryptfsVolume.pathExists(dstDirectoryPath)) {
} if (!gocryptfsVolume.mkdir(dstDirectoryPath)) {
for (e in mappedElements) { return remote_directory_path
val dstPath = PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, e.fullPath))
if (e.isDirectory) {
if (!gocryptfsVolume.mkdir(dstPath)){
return e.fullPath
} }
} else { }
val checkedDstPath = checkFileOverwrite(dstPath) for (e in mappedElements) {
if (checkedDstPath == null){ val dstPath = checkPathOverwrite(PathUtils.path_join(dstDirectoryPath, PathUtils.getRelativePath(remote_directory_path, e.fullPath)), e.isDirectory)
if (dstPath == null){
return "" return ""
} else { } else {
if (!importFileFromOtherVolume(remote_gocryptfsVolume, e.fullPath, checkedDstPath)) { if (e.isDirectory) {
return e.fullPath if (!gocryptfsVolume.pathExists(dstPath)){
if (!gocryptfsVolume.mkdir(dstPath)){
return e.fullPath
}
}
} else {
if (!importFileFromOtherVolume(remote_gocryptfsVolume, e.fullPath, dstPath)) {
return e.fullPath
}
} }
} }
} }

View File

@ -45,25 +45,24 @@ class ExplorerActivityDrop : BaseExplorerActivity() {
errorMsg = if (uri == null){ errorMsg = if (uri == null){
getString(R.string.share_intent_parsing_failed) getString(R.string.share_intent_parsing_failed)
} else { } else {
val outputPathTest = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)) val outputPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)), false)
val outputPath = checkFileOverwrite(outputPathTest)
if (outputPath == null) { if (outputPath == null) {
"" ""
} else { } else {
if (gocryptfsVolume.importFile(activity, uri, outputPath)) null else getString(R.string.import_failed, outputPath) if (gocryptfsVolume.importFile(activity, uri, outputPath)) null else getString(R.string.import_failed, uri)
} }
} }
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE) { } else if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM) val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
if (uris != null){ if (uris != null){
for (uri in uris) { for (uri in uris) {
val outputPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri))) val outputPath = checkPathOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)), false)
if (outputPath == null){ if (outputPath == null){
errorMsg = "" errorMsg = ""
break break
} else { } else {
if (!gocryptfsVolume.importFile(activity, uri, outputPath)) { if (!gocryptfsVolume.importFile(activity, uri, outputPath)) {
errorMsg = getString(R.string.import_failed, outputPath) errorMsg = getString(R.string.import_failed, uri)
break break
} }
} }

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="#FFFFFF"
android:pathData="M19.28,15.28c0.45,-1 0.72,-2.11 0.72,-3.28 0,-4.42 -3.58,-8 -8,-8s-8,3.58 -8,8 3.58,8 8,8c1.17,0 2.28,-0.27 3.28,-0.72l4.72,4.72 -4.72,4.72c-1,-0.45 -2.11,-0.72 -3.28,-0.72 -4.42,0 -8,3.58 -8,8s3.58,8 8,8 8,-3.58 8,-8c0,-1.17 -0.27,-2.28 -0.72,-3.28l4.72,-4.72 14,14h6v-2l-24.72,-24.72zM12,16c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4zM12,40c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4zM24,25c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM38,6l-12,12 4,4 14,-14v-2z"/>
</vector>

View File

@ -9,10 +9,10 @@
android:icon="@drawable/icon_select_all"/> android:icon="@drawable/icon_select_all"/>
<item <item
android:id="@+id/copy" android:id="@+id/cut"
app:showAsAction="always" app:showAsAction="always"
android:visible="false" android:visible="false"
android:icon="@drawable/icon_copy"/> android:icon="@drawable/icon_cut"/>
<item <item
android:id="@+id/delete" android:id="@+id/delete"
@ -20,6 +20,13 @@
android:visible="false" android:visible="false"
android:icon="@drawable/icon_delete" /> android:icon="@drawable/icon_delete" />
<item
android:id="@+id/copy"
app:showAsAction="ifRoom"
android:visible="false"
android:icon="@drawable/icon_copy"
android:title="@string/copy_menu_title"/>
<item <item
android:id="@+id/decrypt" android:id="@+id/decrypt"
app:showAsAction="ifRoom" app:showAsAction="ifRoom"

View File

@ -19,11 +19,11 @@
<string name="error_mkdir">Folder creation failed.</string> <string name="error_mkdir">Folder creation failed.</string>
<string name="success_import">Import successful !</string> <string name="success_import">Import successful !</string>
<string name="success_import_msg">The selected files have been successfully imported.</string> <string name="success_import_msg">The selected files have been successfully imported.</string>
<string name="import_failed">Import of %1$s failed.</string> <string name="import_failed">Import of %s failed.</string>
<string name="export_failed">Export of %1$s failed.</string> <string name="export_failed">Export of %s failed.</string>
<string name="success_export">Export successful !</string> <string name="success_export">Export successful !</string>
<string name="success_export_msg">The selected files have been successfully exported.</string> <string name="success_export_msg">The selected files have been successfully exported.</string>
<string name="remove_failed">Deletion of %1$s failed</string> <string name="remove_failed">Deletion of %s failed</string>
<string name="passwords_mismatch">Passwords don\'t match</string> <string name="passwords_mismatch">Passwords don\'t match</string>
<string name="dir_not_empty">The selected directory isn\'t empty</string> <string name="dir_not_empty">The selected directory isn\'t empty</string>
<string name="success_volume_create">Volume successfully created !</string> <string name="success_volume_create">Volume successfully created !</string>
@ -40,24 +40,24 @@
<string name="parent_folder">Parent Folder</string> <string name="parent_folder">Parent Folder</string>
<string name="enter_volume_path">Please enter the volume path</string> <string name="enter_volume_path">Please enter the volume path</string>
<string name="external_open">Open with external app</string> <string name="external_open">Open with external app</string>
<string name="single_delete_confirm">Are you sure you want to delete %1$s ?</string> <string name="single_delete_confirm">Are you sure you want to delete %s ?</string>
<string name="multiple_delete_confirm">Are you sure you want to delete these %1$s items ?</string> <string name="multiple_delete_confirm">Are you sure you want to delete these %s items ?</string>
<string name="location">Location: /%1$s</string> <string name="location">Location: /%s</string>
<string name="total_size">Total size: %1$s</string> <string name="total_size">Total size: %s</string>
<string name="import_from_other_volume">Import from another volume</string> <string name="import_from_other_volume">Import from another volume</string>
<string name="read_file_failed">Failed to open this file.</string> <string name="read_file_failed">Failed to open this file.</string>
<string name="volume">Volume: %1$s</string> <string name="volume">Volume: %s</string>
<string name="yes">YES</string> <string name="yes">YES</string>
<string name="no">NO</string> <string name="no">NO</string>
<string name="ask_for_wipe">Do you want to wipe the original files ?</string> <string name="ask_for_wipe">Do you want to wipe the original files ?</string>
<string name="wipe_failed">Wiping failed: %1$s</string> <string name="wipe_failed">Wiping failed: %s</string>
<string name="wipe_successful">Files successfully wiped !</string> <string name="wipe_successful">Files successfully wiped !</string>
<string name="wipe_success_msg">The imported files have been successfully wiped from their original locations.</string> <string name="wipe_success_msg">The imported files have been successfully wiped from their original locations.</string>
<string name="create_password_warning">Warning !\nThis password will be the only way to decrypt the volume and access the files inside.\nChoose a very strong password (not \"123456\" or \"password\"), do not lose it and keep it secure (preferably only in your mind).\n\nDroidFS cannot protect you from screen recording apps, keyloggers, apk backdooring, compromised root accesses, memory dumps etc.\nDo not type passwords in insecure environments.</string> <string name="create_password_warning">Warning !\nThis password will be the only way to decrypt the volume and access the files inside.\nChoose a very strong password (not \"123456\" or \"password\"), do not lose it and keep it secure (preferably only in your mind).\n\nDroidFS cannot protect you from screen recording apps, keyloggers, apk backdooring, compromised root accesses, memory dumps etc.\nDo not type passwords in insecure environments.</string>
<string name="open_activity_warning">Warning !\nOpening volumes in insecure environments can lead to data leaks.\nDroidFS cannot protect you from screen recording apps, keyloggers, apk backdooring, compromised root accesses, memory dumps etc.\nDo not open volumes containing sensitive data unless you know exactly what you are doing.</string> <string name="open_activity_warning">Warning !\nOpening volumes in insecure environments can lead to data leaks.\nDroidFS cannot protect you from screen recording apps, keyloggers, apk backdooring, compromised root accesses, memory dumps etc.\nDo not open volumes containing sensitive data unless you know exactly what you are doing.</string>
<string name="rename">Rename</string> <string name="rename">Rename</string>
<string name="rename_title">New name:</string> <string name="rename_title">New name:</string>
<string name="rename_failed">Failed to rename %1$s</string> <string name="rename_failed">Failed to rename %s</string>
<string name="remember_volume_path">Remember volume path</string> <string name="remember_volume_path">Remember volume path</string>
<string name="sort_order">Sort order:</string> <string name="sort_order">Sort order:</string>
<string name="old_password">Old password:</string> <string name="old_password">Old password:</string>
@ -137,16 +137,22 @@
<string name="share">Share</string> <string name="share">Share</string>
<string name="decrypt">Decrypt</string> <string name="decrypt">Decrypt</string>
<string name="loading_msg_copy">Copying selected items…</string> <string name="loading_msg_copy">Copying selected items…</string>
<string name="copy_failed">Copy of %1$s failed.</string> <string name="copy_failed">Copy of %s failed.</string>
<string name="copy_success_msg">The selected items have been successfully copied.</string> <string name="copy_success_msg">The selected items have been successfully copied.</string>
<string name="copy_success">Copy successful !</string> <string name="copy_success">Copy successful !</string>
<string name="fab_dialog_title">Add</string> <string name="fab_dialog_title">Add</string>
<string name="take_photo">Take photo</string> <string name="take_photo">Take photo</string>
<string name="picture_save_success">Picture saved to %1$s</string> <string name="picture_save_success">Picture saved to %s</string>
<string name="picture_save_failed">Failed to save this picture.</string> <string name="picture_save_failed">Failed to save this picture.</string>
<string name="default_total_size">N//A</string> <string name="default_total_size">N//A</string>
<string name="file_overwrite_question">%s already exists, do you want to overwrite it ?</string> <string name="file_overwrite_question">%s already exists, do you want to overwrite it ?</string>
<string name="enter_new_filename">Enter new filename</string> <string name="dir_overwrite_question">%s already exists, do you want to merge its content ?</string>
<string name="enter_new_name">Enter new name</string>
<string name="reset_theme_color">Reset theme color</string> <string name="reset_theme_color">Reset theme color</string>
<string name="reset_theme_color_summary">Reset theme color to the default one</string> <string name="reset_theme_color_summary">Reset theme color to the default one</string>
<string name="copy_menu_title">Copy</string>
<string name="loading_msg_move">Moving selected items…</string>
<string name="move_failed">Move of %s failed.</string>
<string name="move_success_msg">The selected items have been successfully moved.</string>
<string name="move_success">Move successful !</string>
</resources> </resources>