Explorer loading dialogs & Logo fix
This commit is contained in:
parent
2571849bc3
commit
34d7f19927
@ -15,7 +15,7 @@ android {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName "1.1.0"
|
||||
versionName "1.1.1"
|
||||
|
||||
ndk {
|
||||
abiFilters 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
|
@ -3,7 +3,6 @@ package sushi.hardcore.droidfs
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.jaredrummler.cyanea.app.CyaneaAppCompatActivity
|
||||
@ -30,8 +29,4 @@ open class BaseActivity: CyaneaAppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun toastFromThread(stringId: Int){
|
||||
runOnUiThread { Toast.makeText(this, stringId, Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
}
|
@ -8,8 +8,8 @@ import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.android.synthetic.main.activity_change_password.*
|
||||
import kotlinx.android.synthetic.main.activity_change_password.checkbox_remember_path
|
||||
import kotlinx.android.synthetic.main.activity_change_password.checkbox_save_password
|
||||
@ -18,10 +18,7 @@ import kotlinx.android.synthetic.main.activity_change_password.saved_path_listvi
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
|
||||
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
|
||||
import sushi.hardcore.droidfs.util.PathUtils
|
||||
import sushi.hardcore.droidfs.util.GocryptfsVolume
|
||||
import sushi.hardcore.droidfs.util.WidgetUtil
|
||||
import sushi.hardcore.droidfs.util.Wiper
|
||||
import sushi.hardcore.droidfs.util.*
|
||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||
import java.util.*
|
||||
|
||||
@ -59,8 +56,11 @@ class ChangePasswordActivity : BaseActivity() {
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
if (sharedPrefs.getString(s.toString(), null) == null) {
|
||||
edit_old_password.hint = null
|
||||
edit_old_password.isEnabled = true
|
||||
} else {
|
||||
edit_old_password.text = null
|
||||
edit_old_password.hint = getString(R.string.hash_saved_hint)
|
||||
edit_old_password.isEnabled = false
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -97,21 +97,12 @@ class ChangePasswordActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun changePassword(givenHash: ByteArray?){
|
||||
val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
|
||||
val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
|
||||
dialogTextMessage.text = getString(R.string.loading_msg_change_password)
|
||||
val dialogLoading = ColoredAlertDialogBuilder(this)
|
||||
.setView(dialogLoadingView)
|
||||
.setTitle(R.string.loading)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
dialogLoading.show()
|
||||
Thread {
|
||||
object : LoadingTask(this, R.string.loading_msg_change_password){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
val newPassword = edit_new_password.text.toString().toCharArray()
|
||||
val newPasswordConfirm = edit_new_password_confirm.text.toString().toCharArray()
|
||||
if (!newPassword.contentEquals(newPasswordConfirm)) {
|
||||
dialogLoading.dismiss()
|
||||
toastFromThread(R.string.passwords_mismatch)
|
||||
stopTaskWithToast(R.string.passwords_mismatch)
|
||||
} else {
|
||||
val oldPassword = edit_old_password.text.toString().toCharArray()
|
||||
var returnedHash: ByteArray? = null
|
||||
@ -122,8 +113,9 @@ class ChangePasswordActivity : BaseActivity() {
|
||||
if (givenHash == null){
|
||||
val cipherText = sharedPrefs.getString(rootCipherDir, null)
|
||||
if (cipherText != null){ //password hash saved
|
||||
dialogLoading.dismiss()
|
||||
stopTask {
|
||||
fingerprintPasswordHashSaver.decrypt(cipherText, rootCipherDir, ::changePassword)
|
||||
}
|
||||
changePasswordImmediately = false
|
||||
}
|
||||
}
|
||||
@ -144,21 +136,18 @@ class ChangePasswordActivity : BaseActivity() {
|
||||
editor.apply()
|
||||
}
|
||||
if (checkbox_save_password.isChecked && returnedHash != null){
|
||||
dialogLoading.dismiss()
|
||||
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ ->
|
||||
onPasswordChanged()
|
||||
stopTask { onPasswordChanged() }
|
||||
}
|
||||
continueImmediately = false
|
||||
}
|
||||
}
|
||||
if (continueImmediately){
|
||||
dialogLoading.dismiss()
|
||||
runOnUiThread { onPasswordChanged() }
|
||||
stopTask { onPasswordChanged() }
|
||||
}
|
||||
} else {
|
||||
dialogLoading.dismiss()
|
||||
runOnUiThread {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.change_password_failed)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
@ -170,7 +159,8 @@ class ChangePasswordActivity : BaseActivity() {
|
||||
}
|
||||
Arrays.fill(newPassword, 0.toChar())
|
||||
Arrays.fill(newPasswordConfirm, 0.toChar())
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onPasswordChanged(){
|
||||
|
@ -5,7 +5,7 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.android.synthetic.main.activity_create.*
|
||||
import kotlinx.android.synthetic.main.activity_create.checkbox_remember_path
|
||||
import kotlinx.android.synthetic.main.activity_create.checkbox_save_password
|
||||
@ -14,10 +14,7 @@ import kotlinx.android.synthetic.main.activity_create.edit_volume_path
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import sushi.hardcore.droidfs.explorers.ExplorerActivity
|
||||
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
|
||||
import sushi.hardcore.droidfs.util.PathUtils
|
||||
import sushi.hardcore.droidfs.util.GocryptfsVolume
|
||||
import sushi.hardcore.droidfs.util.WidgetUtil
|
||||
import sushi.hardcore.droidfs.util.Wiper
|
||||
import sushi.hardcore.droidfs.util.*
|
||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@ -64,21 +61,12 @@ class CreateActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
fun onClickCreate(view: View?) {
|
||||
val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
|
||||
val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
|
||||
dialogTextMessage.text = getString(R.string.loading_msg_create)
|
||||
val dialogLoading = ColoredAlertDialogBuilder(this)
|
||||
.setView(dialogLoadingView)
|
||||
.setTitle(R.string.loading)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
dialogLoading.show()
|
||||
Thread {
|
||||
object: LoadingTask(this, R.string.loading_msg_create){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
val password = edit_password.text.toString().toCharArray()
|
||||
val passwordConfirm = edit_password_confirm.text.toString().toCharArray()
|
||||
if (!password.contentEquals(passwordConfirm)) {
|
||||
dialogLoading.dismiss()
|
||||
toastFromThread(R.string.passwords_mismatch)
|
||||
stopTaskWithToast(R.string.passwords_mismatch)
|
||||
} else {
|
||||
rootCipherDir = edit_volume_path.text.toString()
|
||||
val volumePathFile = File(rootCipherDir)
|
||||
@ -87,8 +75,7 @@ class CreateActivity : BaseActivity() {
|
||||
if (volumePathFile.mkdirs()) {
|
||||
goodDirectory = true
|
||||
} else {
|
||||
dialogLoading.dismiss()
|
||||
toastFromThread(R.string.error_mkdir)
|
||||
stopTaskWithToast(R.string.error_mkdir)
|
||||
}
|
||||
} else {
|
||||
val dirContent = volumePathFile.list()
|
||||
@ -96,12 +83,10 @@ class CreateActivity : BaseActivity() {
|
||||
if (dirContent.isEmpty()) {
|
||||
goodDirectory = true
|
||||
} else {
|
||||
dialogLoading.dismiss()
|
||||
toastFromThread(R.string.dir_not_empty)
|
||||
stopTaskWithToast(R.string.dir_not_empty)
|
||||
}
|
||||
} else {
|
||||
dialogLoading.dismiss()
|
||||
toastFromThread(R.string.listdir_null_error_msg)
|
||||
stopTaskWithToast(R.string.listdir_null_error_msg)
|
||||
}
|
||||
}
|
||||
if (goodDirectory) {
|
||||
@ -127,25 +112,21 @@ class CreateActivity : BaseActivity() {
|
||||
}
|
||||
editor.apply()
|
||||
if (checkbox_save_password.isChecked && returnedHash != null){
|
||||
dialogLoading.dismiss()
|
||||
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir){ _ ->
|
||||
runOnUiThread { startExplorer() }
|
||||
stopTask { startExplorer() }
|
||||
}
|
||||
startExplorerImmediately = false
|
||||
}
|
||||
}
|
||||
if (startExplorerImmediately){
|
||||
dialogLoading.dismiss()
|
||||
runOnUiThread { startExplorer() }
|
||||
stopTask { startExplorer() }
|
||||
}
|
||||
} else {
|
||||
dialogLoading.dismiss()
|
||||
toastFromThread(R.string.open_volume_failed)
|
||||
stopTaskWithToast(R.string.open_volume_failed)
|
||||
}
|
||||
} else {
|
||||
dialogLoading.dismiss()
|
||||
runOnUiThread {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.create_volume_failed)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
@ -156,7 +137,8 @@ class CreateActivity : BaseActivity() {
|
||||
}
|
||||
Arrays.fill(password, 0.toChar())
|
||||
Arrays.fill(passwordConfirm, 0.toChar())
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startExplorer(){
|
||||
|
@ -7,6 +7,8 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import kotlinx.android.synthetic.main.activity_open.checkbox_remember_path
|
||||
import kotlinx.android.synthetic.main.activity_open.checkbox_save_password
|
||||
import kotlinx.android.synthetic.main.activity_open.edit_password
|
||||
@ -18,10 +20,7 @@ import sushi.hardcore.droidfs.explorers.ExplorerActivity
|
||||
import sushi.hardcore.droidfs.explorers.ExplorerActivityDrop
|
||||
import sushi.hardcore.droidfs.explorers.ExplorerActivityPick
|
||||
import sushi.hardcore.droidfs.fingerprint_stuff.FingerprintPasswordHashSaver
|
||||
import sushi.hardcore.droidfs.util.PathUtils
|
||||
import sushi.hardcore.droidfs.util.GocryptfsVolume
|
||||
import sushi.hardcore.droidfs.util.WidgetUtil
|
||||
import sushi.hardcore.droidfs.util.Wiper
|
||||
import sushi.hardcore.droidfs.util.*
|
||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@ -83,20 +82,11 @@ class OpenActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
fun onClickOpen(view: View?) {
|
||||
val dialogLoadingView = layoutInflater.inflate(R.layout.dialog_loading, null)
|
||||
val dialogTextMessage = dialogLoadingView.findViewById<TextView>(R.id.text_message)
|
||||
dialogTextMessage.text = getString(R.string.loading_msg_open)
|
||||
val dialogLoading = ColoredAlertDialogBuilder(this)
|
||||
.setView(dialogLoadingView)
|
||||
.setTitle(R.string.loading)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
dialogLoading.show()
|
||||
Thread {
|
||||
object : LoadingTask(this, R.string.loading_msg_open){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
rootCipherDir = edit_volume_path.text.toString() //fresh get in case of manual rewrite
|
||||
if (rootCipherDir.isEmpty()) {
|
||||
dialogLoading.dismiss()
|
||||
toastFromThread(R.string.enter_volume_path)
|
||||
stopTaskWithToast(R.string.enter_volume_path)
|
||||
} else {
|
||||
val password = edit_password.text.toString().toCharArray()
|
||||
var returnedHash: ByteArray? = null
|
||||
@ -109,23 +99,18 @@ class OpenActivity : BaseActivity() {
|
||||
if (checkbox_remember_path.isChecked) {
|
||||
savedVolumesAdapter.addVolumePath(rootCipherDir)
|
||||
if (checkbox_save_password.isChecked && returnedHash != null){
|
||||
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir) { success ->
|
||||
dialogLoading.dismiss()
|
||||
if (success){
|
||||
startExplorer()
|
||||
}
|
||||
fingerprintPasswordHashSaver.encryptAndSave(returnedHash, rootCipherDir) { _ ->
|
||||
stopTask { startExplorer() }
|
||||
}
|
||||
startExplorerImmediately = false
|
||||
}
|
||||
}
|
||||
if (startExplorerImmediately){
|
||||
dialogLoading.dismiss()
|
||||
startExplorer()
|
||||
stopTask { startExplorer() }
|
||||
}
|
||||
} else {
|
||||
dialogLoading.dismiss()
|
||||
runOnUiThread {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.open_volume_failed)
|
||||
.setMessage(R.string.open_volume_failed_msg)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
@ -134,22 +119,29 @@ class OpenActivity : BaseActivity() {
|
||||
}
|
||||
Arrays.fill(password, 0.toChar())
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openUsingPasswordHash(passwordHash: ByteArray){
|
||||
object : LoadingTask(this, R.string.loading_msg_open){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
sessionID = GocryptfsVolume.init(rootCipherDir, null, passwordHash, null)
|
||||
if (sessionID != -1){
|
||||
startExplorer()
|
||||
stopTask { startExplorer() }
|
||||
} else {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.open_volume_failed)
|
||||
.setMessage(R.string.open_failed_hash_msg)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
Arrays.fill(passwordHash, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startExplorer() {
|
||||
var explorerIntent: Intent? = null
|
||||
|
@ -133,6 +133,11 @@ open class BaseExplorerActivity : BaseActivity() {
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
protected fun unselectAll(){
|
||||
explorerAdapter.unSelectAll()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
private fun sortExplorerElements() {
|
||||
when (sortModesValues[currentSortModeIndex]) {
|
||||
"name" -> {
|
||||
@ -199,8 +204,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
||||
setCurrentPath(PathUtils.get_parent_path(currentDirectoryPath))
|
||||
}
|
||||
} else {
|
||||
explorerAdapter.unSelectAll()
|
||||
invalidateOptionsMenu()
|
||||
unselectAll()
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,8 +291,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
explorerAdapter.unSelectAll()
|
||||
invalidateOptionsMenu()
|
||||
unselectAll()
|
||||
true
|
||||
}
|
||||
R.id.explorer_menu_sort -> {
|
||||
@ -331,8 +334,7 @@ open class BaseExplorerActivity : BaseActivity() {
|
||||
R.id.explorer_menu_external_open -> {
|
||||
if (usf_open){
|
||||
ExternalProvider.open(this, gocryptfsVolume, PathUtils.path_join(currentDirectoryPath, explorerElements[explorerAdapter.selectedItems[0]].name))
|
||||
explorerAdapter.unSelectAll()
|
||||
invalidateOptionsMenu()
|
||||
unselectAll()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -8,15 +8,14 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.github.clans.fab.FloatingActionMenu
|
||||
import kotlinx.android.synthetic.main.activity_explorer.*
|
||||
import sushi.hardcore.droidfs.OpenActivity
|
||||
import sushi.hardcore.droidfs.R
|
||||
import sushi.hardcore.droidfs.util.ExternalProvider
|
||||
import sushi.hardcore.droidfs.util.PathUtils
|
||||
import sushi.hardcore.droidfs.util.GocryptfsVolume
|
||||
import sushi.hardcore.droidfs.util.Wiper
|
||||
import sushi.hardcore.droidfs.util.*
|
||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@ -77,7 +76,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
|
||||
fun onClickAddFile(view: View?) {
|
||||
fam_explorer.close(true)
|
||||
val i = Intent(Intent.ACTION_GET_CONTENT)
|
||||
val i = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
i.type = "*/*"
|
||||
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
i.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
@ -95,6 +94,8 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == PICK_FILES_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
object : LoadingTask(this, R.string.loading_msg_import){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
val uris: MutableList<Uri> = ArrayList()
|
||||
val singleUri = data.data
|
||||
if (singleUri == null) { //multiples choices
|
||||
@ -107,60 +108,77 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
} else {
|
||||
uris.add(singleUri)
|
||||
}
|
||||
if (uris.isNotEmpty()){
|
||||
var success = true
|
||||
for (uri in uris) {
|
||||
val dstPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(this, uri))
|
||||
val dstPath = PathUtils.path_join(currentDirectoryPath, PathUtils.getFilenameFromURI(activity, uri))
|
||||
contentResolver.openInputStream(uri)?.let {
|
||||
success = gocryptfsVolume.import_file(it, dstPath)
|
||||
}
|
||||
if (!success) {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(getString(R.string.import_failed, uri))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.success_import)
|
||||
.setMessage("""
|
||||
${getString(R.string.success_import_msg)}
|
||||
${getString(R.string.ask_for_wipe)}
|
||||
""".trimIndent())
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
object : LoadingTask(activity, R.string.loading_msg_wipe){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
success = true
|
||||
for (uri in uris) {
|
||||
if (!Wiper.wipe(this, uri)) {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
val errorMsg = Wiper.wipe(activity, uri)
|
||||
if (errorMsg != null) {
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(getString(R.string.wipe_failed, uri))
|
||||
.setMessage(getString(R.string.wipe_failed, errorMsg))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
success = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.wipe_successful)
|
||||
.setMessage(R.string.wipe_success_msg)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(getString(R.string.no), null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun doFinally(activity: AppCompatActivity){
|
||||
setCurrentPath(currentDirectoryPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (requestCode == PICK_DIRECTORY_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
object : LoadingTask(this, R.string.loading_msg_export){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
val uri = data.data
|
||||
val outputDir = PathUtils.getFullPathFromTreeUri(uri, this)
|
||||
val outputDir = PathUtils.getFullPathFromTreeUri(uri, activity)
|
||||
var failedItem: String? = null
|
||||
for (i in explorerAdapter.selectedItems) {
|
||||
val element = explorerAdapter.getItem(i)
|
||||
@ -171,26 +189,35 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
if (gocryptfsVolume.export_file(fullPath, PathUtils.path_join(outputDir, element.name))) null else fullPath
|
||||
}
|
||||
if (failedItem != null) {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(getString(R.string.export_failed, failedItem))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if (failedItem == null) {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.success_export)
|
||||
.setMessage(R.string.success_export_msg)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
explorerAdapter.unSelectAll()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
override fun doFinally(activity: AppCompatActivity) {
|
||||
unselectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (requestCode == PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
object : LoadingTask(this, R.string.loading_msg_import){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
val remoteSessionID = data.getIntExtra("sessionID", -1)
|
||||
val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID)
|
||||
val path = data.getStringExtra("path")
|
||||
@ -214,23 +241,31 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
failedItem = if (importFileFromOtherVolume(remoteGocryptfsVolume, path, currentDirectoryPath)) null else path
|
||||
}
|
||||
if (failedItem == null) {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.success_import)
|
||||
.setMessage(R.string.success_import_msg)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
ColoredAlertDialogBuilder(this)
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(getString(R.string.import_failed, failedItem))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
remoteGocryptfsVolume.close()
|
||||
}
|
||||
override fun doFinally(activity: AppCompatActivity) {
|
||||
setCurrentPath(currentDirectoryPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.explorer, menu)
|
||||
@ -285,8 +320,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
paths.add(PathUtils.path_join(currentDirectoryPath, e.name))
|
||||
}
|
||||
ExternalProvider.share(this, gocryptfsVolume, paths)
|
||||
explorerAdapter.unSelectAll()
|
||||
invalidateOptionsMenu()
|
||||
unselectAll()
|
||||
true
|
||||
}
|
||||
R.id.explorer_menu_decrypt -> {
|
||||
@ -407,8 +441,7 @@ class ExplorerActivity : BaseExplorerActivity() {
|
||||
break
|
||||
}
|
||||
}
|
||||
explorerAdapter.unSelectAll()
|
||||
invalidateOptionsMenu()
|
||||
unselectAll()
|
||||
setCurrentPath(currentDirectoryPath) //refresh
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package sushi.hardcore.droidfs.fingerprint_stuff
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.os.Build
|
||||
import android.os.CancellationSignal
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
class FingerprintHandler(private val context: Context) : FingerprintManager.AuthenticationCallback(){
|
||||
private lateinit var cancellationSignal: CancellationSignal
|
||||
private lateinit var onTouched: (resultCode: onTouchedResultCodes) -> Unit
|
||||
|
||||
fun startAuth(fingerprintManager: FingerprintManager, cryptoObject: FingerprintManager.CryptoObject, onTouched: (resultCode: onTouchedResultCodes) -> Unit){
|
||||
cancellationSignal = CancellationSignal()
|
||||
this.onTouched = onTouched
|
||||
fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, this, null)
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult?) {
|
||||
onTouched(onTouchedResultCodes.SUCCEED)
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
|
||||
onTouched(onTouchedResultCodes.ERROR)
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
onTouched(onTouchedResultCodes.FAILED)
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package sushi.hardcore.droidfs.fingerprint_stuff
|
||||
|
||||
enum class onTouchedResultCodes {
|
||||
SUCCEED,
|
||||
FAILED,
|
||||
ERROR
|
||||
}
|
@ -3,12 +3,14 @@ package sushi.hardcore.droidfs.util
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import sushi.hardcore.droidfs.R
|
||||
import sushi.hardcore.droidfs.provider.RestrictedFileProvider
|
||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||
import java.io.File
|
||||
import java.net.URLConnection
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
object ExternalProvider {
|
||||
private const val content_type_all = "*/*"
|
||||
@ -37,23 +39,27 @@ object ExternalProvider {
|
||||
return Pair(tmpFileUri, getContentType(fileName, previous_content_type))
|
||||
}
|
||||
}
|
||||
ColoredAlertDialogBuilder(context)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(context.getString(R.string.export_failed, file_path))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
return Pair(null, null)
|
||||
}
|
||||
|
||||
fun share(context: Context, gocryptfsVolume: GocryptfsVolume, file_paths: List<String>) {
|
||||
fun share(activity: AppCompatActivity, gocryptfsVolume: GocryptfsVolume, file_paths: List<String>) {
|
||||
object : LoadingTask(activity, R.string.loading_msg_export){
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
var contentType: String? = null
|
||||
val uris = ArrayList<Uri>()
|
||||
for (path in file_paths) {
|
||||
val result = exportFile(context, gocryptfsVolume, path, contentType)
|
||||
val result = exportFile(activity, gocryptfsVolume, path, contentType)
|
||||
contentType = if (result.first != null) {
|
||||
uris.add(result.first!!)
|
||||
result.second
|
||||
} else {
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(activity.getString(R.string.export_failed, path))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -66,26 +72,45 @@ object ExternalProvider {
|
||||
shareIntent.action = Intent.ACTION_SEND_MULTIPLE
|
||||
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
|
||||
}
|
||||
context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_chooser)))
|
||||
stopTask {
|
||||
activity.startActivity(Intent.createChooser(shareIntent, activity.getString(R.string.share_chooser)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun open(context: Context, gocryptfsVolume: GocryptfsVolume, file_path: String) {
|
||||
val result = exportFile(context, gocryptfsVolume, file_path, null)
|
||||
result.first?.let {
|
||||
val openIntent = Intent()
|
||||
openIntent.action = Intent.ACTION_VIEW
|
||||
fun open(activity: AppCompatActivity, gocryptfsVolume: GocryptfsVolume, file_path: String) {
|
||||
object : LoadingTask(activity, R.string.loading_msg_export) {
|
||||
override fun doTask(activity: AppCompatActivity) {
|
||||
val result = exportFile(activity, gocryptfsVolume, file_path, null)
|
||||
if (result.first != null) {
|
||||
val openIntent = Intent(Intent.ACTION_VIEW)
|
||||
openIntent.setDataAndType(result.first, result.second)
|
||||
context.startActivity(openIntent)
|
||||
stopTask { activity.startActivity(openIntent) }
|
||||
} else {
|
||||
stopTask {
|
||||
ColoredAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(activity.getString(R.string.export_failed, file_path))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeFiles(context: Context) {
|
||||
Thread{
|
||||
val wiped = ArrayList<Uri>()
|
||||
for (uri in storedFiles) {
|
||||
if (Wiper.wipe(context, uri)){
|
||||
storedFiles.remove(uri)
|
||||
if (Wiper.wipe(context, uri) == null){
|
||||
wiped.add(uri)
|
||||
}
|
||||
}
|
||||
for (uri in wiped){
|
||||
storedFiles.remove(uri)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
39
app/src/main/java/sushi/hardcore/droidfs/util/LoadingTask.kt
Normal file
39
app/src/main/java/sushi/hardcore/droidfs/util/LoadingTask.kt
Normal file
@ -0,0 +1,39 @@
|
||||
package sushi.hardcore.droidfs.util
|
||||
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
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) {
|
||||
private val dialogLoadingView = activity.layoutInflater.inflate(R.layout.dialog_loading, null)
|
||||
private val dialogLoading: AlertDialog = ColoredAlertDialogBuilder(activity)
|
||||
.setView(dialogLoadingView)
|
||||
.setTitle(R.string.loading)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
init {
|
||||
dialogLoadingView.findViewById<TextView>(R.id.text_message).text = activity.getString(loadingMessageResId)
|
||||
startTask()
|
||||
}
|
||||
abstract fun doTask(activity: AppCompatActivity)
|
||||
open fun doFinally(activity: AppCompatActivity){}
|
||||
private fun startTask() {
|
||||
dialogLoading.show()
|
||||
Thread {
|
||||
doTask(activity)
|
||||
activity.runOnUiThread { doFinally(activity) }
|
||||
}.start()
|
||||
}
|
||||
protected fun stopTask(onUiThread: () -> Unit){
|
||||
dialogLoading.dismiss()
|
||||
activity.runOnUiThread {
|
||||
onUiThread()
|
||||
}
|
||||
}
|
||||
protected fun stopTaskWithToast(stringId: Int){
|
||||
stopTask { Toast.makeText(activity, stringId, Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
}
|
@ -4,7 +4,8 @@ import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.widget.EditText
|
||||
import sushi.hardcore.droidfs.ConstValues.Companion.wipe_passes
|
||||
import sushi.hardcore.droidfs.ConstValues
|
||||
import sushi.hardcore.droidfs.R
|
||||
import java.io.*
|
||||
import java.lang.Exception
|
||||
import java.lang.StringBuilder
|
||||
@ -14,7 +15,7 @@ import kotlin.math.ceil
|
||||
|
||||
object Wiper {
|
||||
private const val buff_size = 4096
|
||||
fun wipe(context: Context, uri: Uri): Boolean {
|
||||
fun wipe(context: Context, uri: Uri): String? {
|
||||
val cursor = context.contentResolver.query(uri, null, null, null, null)
|
||||
cursor?.let {
|
||||
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||
@ -26,11 +27,11 @@ object Wiper {
|
||||
val buff = ByteArray(buff_size)
|
||||
Arrays.fill(buff, 0.toByte())
|
||||
val writes = ceil(size.toDouble() / buff_size).toInt()
|
||||
for (i in 0 until wipe_passes) {
|
||||
for (i in 0 until ConstValues.wipe_passes) {
|
||||
for (j in 0 until writes) {
|
||||
os!!.write(buff)
|
||||
}
|
||||
if (i < wipe_passes - 1) {
|
||||
if (i < ConstValues.wipe_passes - 1) {
|
||||
//reopening to flush and seek
|
||||
os!!.close()
|
||||
os = context.contentResolver.openOutputStream(uri)
|
||||
@ -42,26 +43,25 @@ object Wiper {
|
||||
(os as FileOutputStream).channel.truncate(0) //truncate to 0 if cannot delete
|
||||
}
|
||||
os!!.close()
|
||||
return true
|
||||
return null
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return e.message
|
||||
}
|
||||
}
|
||||
return false
|
||||
return context.getString(R.string.query_cursor_null_error_msg)
|
||||
}
|
||||
@JvmStatic
|
||||
fun wipe(file: File): Boolean{
|
||||
fun wipe(file: File): String? {
|
||||
val size = file.length()
|
||||
try {
|
||||
var os = FileOutputStream(file)
|
||||
val buff = ByteArray(buff_size)
|
||||
Arrays.fill(buff, 0.toByte())
|
||||
val writes = ceil(size.toDouble() / buff_size).toInt()
|
||||
for (i in 0 until wipe_passes) {
|
||||
for (i in 0 until ConstValues.wipe_passes) {
|
||||
for (j in 0 until writes) {
|
||||
os.write(buff)
|
||||
}
|
||||
if (i < wipe_passes - 1) {
|
||||
if (i < ConstValues.wipe_passes - 1) {
|
||||
//reopening to flush and seek
|
||||
os.close()
|
||||
os = FileOutputStream(file)
|
||||
@ -73,10 +73,9 @@ object Wiper {
|
||||
os.channel.truncate(0) //truncate to 0 if cannot delete
|
||||
}
|
||||
os.close()
|
||||
return true
|
||||
return null
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
return e.message
|
||||
}
|
||||
}
|
||||
private fun randomString(minSize: Int, maxSize: Int): String {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 42 KiB |
@ -126,21 +126,22 @@
|
||||
android:id="@+id/saved_path_listview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="50dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginHorizontal="@dimen/action_activity_listview_margin_horizontal"
|
||||
android:layout_marginTop="@dimen/action_activity_listview_margin_top"
|
||||
android:background="@drawable/listview_border"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:padding="@dimen/warning_msg_padding"
|
||||
android:gravity="center"
|
||||
android:text="@string/create_password_warning"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/action_activity_button_height"
|
||||
android:layout_marginHorizontal="@dimen/action_activity_button_hor_margin"
|
||||
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
|
||||
android:layout_marginBottom="@dimen/action_activity_button_margin_bottom"
|
||||
android:onClick="onClickChangePassword"
|
||||
android:text="@string/change_volume_password"
|
||||
style="@style/button"/>
|
||||
|
@ -105,14 +105,14 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:padding="@dimen/warning_msg_padding"
|
||||
android:gravity="center"
|
||||
android:text="@string/create_password_warning"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/action_activity_button_height"
|
||||
android:layout_marginHorizontal="@dimen/action_activity_button_hor_margin"
|
||||
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
|
||||
android:onClick="onClickCreate"
|
||||
android:text="@string/create_volume"
|
||||
style="@style/button"/>
|
||||
|
@ -86,20 +86,21 @@
|
||||
android:id="@+id/saved_path_listview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="50dp"
|
||||
android:layout_marginTop="20dp"/>
|
||||
android:layout_marginHorizontal="@dimen/action_activity_listview_margin_horizontal"
|
||||
android:layout_marginTop="@dimen/action_activity_listview_margin_top"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:padding="@dimen/warning_msg_padding"
|
||||
android:gravity="center"
|
||||
android:text="@string/open_activity_warning"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/action_activity_button_height"
|
||||
android:layout_marginHorizontal="@dimen/action_activity_button_hor_margin"
|
||||
android:layout_marginHorizontal="@dimen/action_activity_button_horizontal_margin"
|
||||
android:layout_marginBottom="@dimen/action_activity_button_margin_bottom"
|
||||
android:onClick="onClickOpen"
|
||||
android:text="@string/open_volume"
|
||||
style="@style/button"/>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.5 KiB |
@ -7,7 +7,11 @@
|
||||
<dimen name="edit_text_label_size">15sp</dimen>
|
||||
<dimen name="open_activity_label_width">90dp</dimen>
|
||||
<dimen name="change_password_activity_label_width">100dp</dimen>
|
||||
<dimen name="action_activity_button_hor_margin">60dp</dimen>
|
||||
<dimen name="action_activity_button_horizontal_margin">60dp</dimen>
|
||||
<dimen name="action_activity_button_height">60dp</dimen>
|
||||
<dimen name="action_activity_listview_margin_horizontal">50dp</dimen>
|
||||
<dimen name="action_activity_listview_margin_top">20dp</dimen>
|
||||
<dimen name="warning_msg_padding">20dp</dimen>
|
||||
<dimen name="action_activity_button_margin_bottom">20dp</dimen>
|
||||
<dimen name="adapter_text_size">18sp</dimen>
|
||||
</resources>
|
@ -50,7 +50,7 @@
|
||||
<string name="yes">YES</string>
|
||||
<string name="no">NO</string>
|
||||
<string name="ask_for_wipe">Do you want to wipe the original files ?</string>
|
||||
<string name="wipe_failed">Wiping of %1$s failed</string>
|
||||
<string name="wipe_failed">Wiping failed: %1$s</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="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>
|
||||
@ -62,6 +62,7 @@
|
||||
<string name="sort_order">Sort order:</string>
|
||||
<string name="old_password">Old password:</string>
|
||||
<string name="new_password">New password:</string>
|
||||
<string name="new_password_confirmation">New Password (confirmation):</string>
|
||||
<string name="success_change_password">Password successfully changed !</string>
|
||||
<string name="success_change_password_msg">The volume\'s password has been successfully changed.</string>
|
||||
<string name="change_password_failed">Failed to change the volume\'s password. Check the selected volume path and the entered old password.</string>
|
||||
@ -118,12 +119,15 @@
|
||||
<string name="discard">Discard</string>
|
||||
<string name="word_wrap">Word Wrap</string>
|
||||
<string name="outofmemoryerror_msg">OutOfMemoryError: This file is too large to be loaded in memory.</string>
|
||||
<string name="new_file">New File</string>
|
||||
<string name="new_file">Create New File</string>
|
||||
<string name="enter_file_name">File name:</string>
|
||||
<string name="file_creation_failed">Failed to create the file.</string>
|
||||
<string name="loading">Loading...</string>
|
||||
<string name="loading_msg_create">Creating volume...</string>
|
||||
<string name="loading_msg_change_password">Changing password...</string>
|
||||
<string name="new_password_confirmation">New Password (confirmation):</string>
|
||||
<string name="loading_msg_open">Opening volume...</string>
|
||||
<string name="loading">Loading…</string>
|
||||
<string name="loading_msg_create">Creating volume…</string>
|
||||
<string name="loading_msg_change_password">Changing password…</string>
|
||||
<string name="loading_msg_open">Opening volume…</string>
|
||||
<string name="loading_msg_import">Importing selected files…</string>
|
||||
<string name="loading_msg_wipe">Wiping original files…</string>
|
||||
<string name="loading_msg_export">Exporting files…</string>
|
||||
<string name="query_cursor_null_error_msg">Unable to access this file</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user