Overwrite warning & KEEP_SCREEN_ON while playing

This commit is contained in:
Hardcore Sushi 2020-08-07 19:25:16 +02:00
parent 0bfb952365
commit d3bcd29806
9 changed files with 203 additions and 82 deletions

View File

@ -2,6 +2,9 @@ package sushi.hardcore.droidfs.explorers
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View 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.GocryptfsVolume
import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File
open class BaseExplorerActivity : BaseActivity() { open class BaseExplorerActivity : BaseActivity() {
private lateinit var sortOrderEntries: Array<String> private lateinit var sortOrderEntries: Array<String>
@ -253,6 +257,53 @@ open class BaseExplorerActivity : BaseActivity() {
dialog.show() 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<EditText>(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){ protected fun rename(old_name: String, new_name: String){
if (new_name.isEmpty()) { if (new_name.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()

View File

@ -3,6 +3,7 @@ package sushi.hardcore.droidfs.explorers
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Looper
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
@ -43,17 +44,19 @@ 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 {
val handleID = gocryptfsVolume.openWriteMode(PathUtils.path_join(currentDirectoryPath, fileName)) checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, fileName))?.let {
if (handleID == -1) { val handleID = gocryptfsVolume.openWriteMode(it)
ColoredAlertDialogBuilder(this) if (handleID == -1) {
.setTitle(R.string.error) ColoredAlertDialogBuilder(this)
.setMessage(R.string.file_creation_failed) .setTitle(R.string.error)
.setPositiveButton(R.string.ok, null) .setMessage(R.string.file_creation_failed)
.show() .setPositiveButton(R.string.ok, null)
} else { .show()
gocryptfsVolume.closeFile(handleID) } else {
setCurrentPath(currentDirectoryPath) gocryptfsVolume.closeFile(handleID)
invalidateOptionsMenu() setCurrentPath(currentDirectoryPath)
invalidateOptionsMenu()
}
} }
} }
} }
@ -143,21 +146,26 @@ class ExplorerActivity : BaseExplorerActivity() {
} else { } else {
uris.add(singleUri) uris.add(singleUri)
} }
var success = true Looper.prepare()
var success = false
for (uri in uris) { for (uri in uris) {
val dstPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)) val dstPath = checkFileOverwrite(PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri)))
contentResolver.openInputStream(uri)?.let { if (dstPath == null){
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 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 (success) {
@ -257,6 +265,7 @@ class ExplorerActivity : BaseExplorerActivity() {
val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID) val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID)
val path = data.getStringExtra("path") val path = data.getStringExtra("path")
var failedItem: String? = null var failedItem: String? = null
Looper.prepare()
if (path == null) { if (path == null) {
val paths = data.getStringArrayListExtra("paths") val paths = data.getStringArrayListExtra("paths")
val types = data.getIntegerArrayListExtra("types") val types = data.getIntegerArrayListExtra("types")
@ -265,7 +274,7 @@ class ExplorerActivity : BaseExplorerActivity() {
failedItem = if (types[i] == 0) { //directory failedItem = if (types[i] == 0) { //directory
recursiveImportDirectoryFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath) recursiveImportDirectoryFromOtherVolume(remoteGocryptfsVolume, paths[i], currentDirectoryPath)
} else { } 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) { if (failedItem != null) {
break break
@ -273,7 +282,7 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
} }
} else { } 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) { if (failedItem == null) {
stopTask { stopTask {
@ -283,7 +292,7 @@ class ExplorerActivity : BaseExplorerActivity() {
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
} }
} else { } else if (failedItem!!.isNotEmpty()){
stopTask { stopTask {
ColoredAlertDialogBuilder(activity) ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error) .setTitle(R.string.error)
@ -356,13 +365,19 @@ class ExplorerActivity : BaseExplorerActivity() {
object : LoadingTask(this, R.string.loading_msg_copy){ object : LoadingTask(this, R.string.loading_msg_copy){
override fun doTask(activity: AppCompatActivity) { override fun doTask(activity: AppCompatActivity) {
var failedItem: String? = null var failedItem: String? = null
Looper.prepare()
for (element in filesToCopy) { for (element in filesToCopy) {
failedItem = if (element.isDirectory) { failedItem = if (element.isDirectory) {
recursiveCopyDirectory(element.fullPath, currentDirectoryPath) recursiveCopyDirectory(element.fullPath, currentDirectoryPath)
} else { } 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 { stopTask {
ColoredAlertDialogBuilder(activity) ColoredAlertDialogBuilder(activity)
.setTitle(R.string.error) .setTitle(R.string.error)
@ -483,25 +498,33 @@ class ExplorerActivity : BaseExplorerActivity() {
return e.fullPath return e.fullPath
} }
} else { } else {
if (!copyFile(e.fullPath, dstPath)) { val checkedDstPath = checkFileOverwrite(dstPath)
return e.fullPath if (checkedDstPath == null){
return ""
} else {
if (!copyFile(e.fullPath, dstPath)) {
return e.fullPath
}
} }
} }
} }
return null 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 var success = true
val srcHandleID = remote_gocryptfsVolume.openReadMode(srcPath) val srcHandleID = remoteGocryptfsVolume.openReadMode(srcPath)
if (srcHandleID != -1) { if (srcHandleID != -1) {
val dstHandleID = gocryptfsVolume.openWriteMode(dstPath) val dstHandleID = gocryptfsVolume.openWriteMode(dstPath)
if (dstHandleID != -1) { if (dstHandleID != -1) {
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 (remote_gocryptfsVolume.readFile(srcHandleID, offset, ioBuffer).also { length = it } > 0){ while (remoteGocryptfsVolume.readFile(srcHandleID, offset, ioBuffer)
val written = gocryptfsVolume.writeFile(dstHandleID, offset, ioBuffer, length).toLong() .also { length = it } > 0
) {
val written =
gocryptfsVolume.writeFile(dstHandleID, offset, ioBuffer, length).toLong()
if (written == length.toLong()) { if (written == length.toLong()) {
offset += length.toLong() offset += length.toLong()
} else { } else {
@ -511,11 +534,20 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
gocryptfsVolume.closeFile(dstHandleID) gocryptfsVolume.closeFile(dstHandleID)
} }
remote_gocryptfsVolume.closeFile(srcHandleID) remoteGocryptfsVolume.closeFile(srcHandleID)
} }
return success 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? { 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 = PathUtils.path_join(outputPath, File(remote_directory_path).name)
@ -531,8 +563,13 @@ class ExplorerActivity : BaseExplorerActivity() {
return e.fullPath return e.fullPath
} }
} else { } else {
if (!importFileFromOtherVolume(remote_gocryptfsVolume, e.fullPath, dstPath)) { val checkedDstPath = checkFileOverwrite(dstPath)
return e.fullPath if (checkedDstPath == null){
return ""
} else {
if (!importFileFromOtherVolume(remote_gocryptfsVolume, e.fullPath, checkedDstPath)) {
return e.fullPath
}
} }
} }
} }

View File

@ -2,10 +2,13 @@ package sushi.hardcore.droidfs.explorers
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Looper
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.util.LoadingTask
import sushi.hardcore.droidfs.util.PathUtils import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
@ -28,43 +31,64 @@ class ExplorerActivityDrop : BaseExplorerActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.validate -> { R.id.validate -> {
val alertDialog = ColoredAlertDialogBuilder(this) object : LoadingTask(this, R.string.loading_msg_import) {
alertDialog.setCancelable(false) override fun doTask(activity: AppCompatActivity) {
alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() } val alertDialog = ColoredAlertDialogBuilder(activity)
var errorMsg: String? = null alertDialog.setCancelable(false)
val extras = intent.extras alertDialog.setPositiveButton(R.string.ok) { _, _ -> finish() }
if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)){ var errorMsg: String? = null
if (intent.action == Intent.ACTION_SEND) { val extras = intent.extras
val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) if (extras != null && extras.containsKey(Intent.EXTRA_STREAM)){
val outputPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri)) Looper.prepare()
errorMsg = if (gocryptfsVolume.importFile(this, uri, outputPath)) null else getString(R.string.import_failed, outputPath) if (intent.action == Intent.ACTION_SEND) {
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE) { val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
val uris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM) errorMsg = if (uri == null){
if (uris != null){ getString(R.string.share_intent_parsing_failed)
for (uri in uris) { } else {
val outputPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri)) val outputPathTest = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri))
if (!gocryptfsVolume.importFile(this, uri, outputPath)) { val outputPath = checkFileOverwrite(outputPathTest)
errorMsg = getString(R.string.import_failed, outputPath) if (outputPath == null) {
break ""
} 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<Uri>(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 { } else {
errorMsg = getString(R.string.share_intent_parsing_failed) errorMsg = getString(R.string.share_intent_parsing_failed)
} }
} else { if (errorMsg == null || errorMsg.isNotEmpty()){
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 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 true
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)

View File

@ -1,5 +1,6 @@
package sushi.hardcore.droidfs.file_viewers package sushi.hardcore.droidfs.file_viewers
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.ExoPlaybackException
import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.Player
@ -56,6 +57,14 @@ abstract class MediaPlayer: FileViewerActivity() {
errorDialog.show() 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) player.prepare(LoopingMediaSource(mediaSource), false, false)
} }

View File

@ -14,6 +14,7 @@ abstract class LoadingTask(private val activity: AppCompatActivity, private val
.setTitle(R.string.loading) .setTitle(R.string.loading)
.setCancelable(false) .setCancelable(false)
.create() .create()
private var isStopped = false
init { init {
dialogLoadingView.findViewById<TextView>(R.id.text_message).text = activity.getString(loadingMessageResId) dialogLoadingView.findViewById<TextView>(R.id.text_message).text = activity.getString(loadingMessageResId)
startTask() startTask()
@ -24,13 +25,19 @@ abstract class LoadingTask(private val activity: AppCompatActivity, private val
dialogLoading.show() dialogLoading.show()
Thread { Thread {
doTask(activity) doTask(activity)
if (!isStopped){
dialogLoading.dismiss()
}
activity.runOnUiThread { doFinally(activity) } activity.runOnUiThread { doFinally(activity) }
}.start() }.start()
} }
protected fun stopTask(onUiThread: () -> Unit){ protected fun stopTask(onUiThread: (() -> Unit)?){
isStopped = true
dialogLoading.dismiss() dialogLoading.dismiss()
activity.runOnUiThread { onUiThread?.let {
onUiThread() activity.runOnUiThread {
onUiThread()
}
} }
} }
protected fun stopTaskWithToast(stringId: Int){ protected fun stopTaskWithToast(stringId: Int){

View File

@ -6,7 +6,6 @@ import android.graphics.drawable.DrawableContainer
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.StateListDrawable import android.graphics.drawable.StateListDrawable
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.widget.ListView import android.widget.ListView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R

View File

@ -8,7 +8,7 @@ import androidx.appcompat.widget.AppCompatCheckBox
class ColoredCheckBox: AppCompatCheckBox { class ColoredCheckBox: AppCompatCheckBox {
constructor(context: Context) : super(context) { applyColor() } constructor(context: Context) : super(context) { applyColor() }
constructor(context: Context, attrs: AttributeSet): super(context, attrs) { 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(){ private fun applyColor(){
super.setButtonTintList(ColorStateList.valueOf(ThemeColor.getThemeColor(context))) super.setButtonTintList(ColorStateList.valueOf(ThemeColor.getThemeColor(context)))
} }

View File

@ -2,16 +2,8 @@ package sushi.hardcore.droidfs.widgets
import android.content.Context import android.content.Context
import android.content.res.ColorStateList 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.util.AttributeSet
import android.widget.TextView
import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatEditText
import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.R
class ColoredEditText: AppCompatEditText { class ColoredEditText: AppCompatEditText {
constructor(context: Context) : super(context) { applyColor() } constructor(context: Context) : super(context) { applyColor() }

View File

@ -135,7 +135,7 @@
<string name="github_summary">Want to read the documentation, request feature, report bug, read the source code… Check the DroidFS\'s repository !</string> <string name="github_summary">Want to read the documentation, request feature, report bug, read the source code… Check the DroidFS\'s repository !</string>
<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 %1$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>
@ -144,4 +144,6 @@
<string name="picture_save_success">Picture saved to %1$s</string> <string name="picture_save_success">Picture saved to %1$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="enter_new_filename">Enter new filename</string>
</resources> </resources>