Safe volume directory picking

This commit is contained in:
Matéo Duparc 2021-06-07 14:12:40 +02:00
parent fcd382ca8b
commit 9f8b653cc7
Signed by untrusted user: hardcoresushi
GPG Key ID: 007F84120107191E
6 changed files with 209 additions and 236 deletions

View File

@ -1,7 +1,6 @@
package sushi.hardcore.droidfs package sushi.hardcore.droidfs
import android.app.Activity import android.net.Uri
import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
@ -14,15 +13,14 @@ import kotlinx.android.synthetic.main.activity_change_password.*
import kotlinx.android.synthetic.main.checkboxes_section.* import kotlinx.android.synthetic.main.checkboxes_section.*
import kotlinx.android.synthetic.main.volume_path_section.* import kotlinx.android.synthetic.main.volume_path_section.*
import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
import sushi.hardcore.droidfs.util.* import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.*
class ChangePasswordActivity : VolumeActionActivity() { class ChangePasswordActivity : VolumeActionActivity() {
companion object {
private const val PICK_DIRECTORY_REQUEST_CODE = 1
}
private lateinit var savedVolumesAdapter: SavedVolumesAdapter private lateinit var savedVolumesAdapter: SavedVolumesAdapter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -80,35 +78,27 @@ class ChangePasswordActivity : VolumeActionActivity() {
} }
fun pickDirectory(view: View?) { fun pickDirectory(view: View?) {
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) safePickDirectory()
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onDirectoryPicked(uri: Uri) {
super.onActivityResult(requestCode, resultCode, data) if (PathUtils.isTreeUriOnPrimaryStorage(uri)){
if (resultCode == Activity.RESULT_OK) { val path = PathUtils.getFullPathFromTreeUri(uri, this)
if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { if (path != null){
if (data?.data != null) { edit_volume_path.setText(path)
if (PathUtils.isTreeUriOnPrimaryStorage(data.data!!)){ } else {
val path = PathUtils.getFullPathFromTreeUri(data.data, this) ColoredAlertDialogBuilder(this)
if (path != null){ .setTitle(R.string.error)
edit_volume_path.setText(path) .setMessage(R.string.path_from_uri_null_error_msg)
} else { .setPositiveButton(R.string.ok, null)
ColoredAlertDialogBuilder(this) .show()
.setTitle(R.string.error)
.setMessage(R.string.path_from_uri_null_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.change_pwd_on_sdcard_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
} }
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.change_pwd_on_sdcard_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
} }
} }

View File

@ -1,7 +1,7 @@
package sushi.hardcore.droidfs package sushi.hardcore.droidfs
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
@ -11,15 +11,13 @@ import kotlinx.android.synthetic.main.activity_create.*
import kotlinx.android.synthetic.main.checkboxes_section.* import kotlinx.android.synthetic.main.checkboxes_section.*
import kotlinx.android.synthetic.main.volume_path_section.* import kotlinx.android.synthetic.main.volume_path_section.*
import sushi.hardcore.droidfs.explorers.ExplorerActivity import sushi.hardcore.droidfs.explorers.ExplorerActivity
import sushi.hardcore.droidfs.util.* import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.*
class CreateActivity : VolumeActionActivity() { class CreateActivity : VolumeActionActivity() {
companion object {
private const val PICK_DIRECTORY_REQUEST_CODE = 1
}
private var sessionID = -1 private var sessionID = -1
private var isStartingExplorer = false private var isStartingExplorer = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -44,35 +42,27 @@ class CreateActivity : VolumeActionActivity() {
} }
fun pickDirectory(view: View?) { fun pickDirectory(view: View?) {
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) safePickDirectory()
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onDirectoryPicked(uri: Uri) {
super.onActivityResult(requestCode, resultCode, data) if (PathUtils.isTreeUriOnPrimaryStorage(uri)){
if (resultCode == Activity.RESULT_OK) { val path = PathUtils.getFullPathFromTreeUri(uri, this)
if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { if (path != null){
if (data?.data != null) { edit_volume_path.setText(path)
if (PathUtils.isTreeUriOnPrimaryStorage(data.data!!)){ } else {
val path = PathUtils.getFullPathFromTreeUri(data.data, this) ColoredAlertDialogBuilder(this)
if (path != null){ .setTitle(R.string.error)
edit_volume_path.setText(path) .setMessage(R.string.path_from_uri_null_error_msg)
} else { .setPositiveButton(R.string.ok, null)
ColoredAlertDialogBuilder(this) .show()
.setTitle(R.string.error)
.setMessage(R.string.path_from_uri_null_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.create_on_sdcard_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
} }
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.warning)
.setMessage(R.string.create_on_sdcard_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
} }
} }

View File

@ -1,7 +1,7 @@
package sushi.hardcore.droidfs package sushi.hardcore.droidfs
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
@ -14,23 +14,23 @@ import kotlinx.android.synthetic.main.activity_open.*
import kotlinx.android.synthetic.main.checkboxes_section.* import kotlinx.android.synthetic.main.checkboxes_section.*
import kotlinx.android.synthetic.main.volume_path_section.* import kotlinx.android.synthetic.main.volume_path_section.*
import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter import sushi.hardcore.droidfs.adapters.SavedVolumesAdapter
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
import sushi.hardcore.droidfs.explorers.ExplorerActivity import sushi.hardcore.droidfs.explorers.ExplorerActivity
import sushi.hardcore.droidfs.explorers.ExplorerActivityDrop import sushi.hardcore.droidfs.explorers.ExplorerActivityDrop
import sushi.hardcore.droidfs.explorers.ExplorerActivityPick import sushi.hardcore.droidfs.explorers.ExplorerActivityPick
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider import sushi.hardcore.droidfs.util.PathUtils
import sushi.hardcore.droidfs.util.* import sushi.hardcore.droidfs.util.WidgetUtil
import sushi.hardcore.droidfs.util.Wiper
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.*
class OpenActivity : VolumeActionActivity() { class OpenActivity : VolumeActionActivity() {
companion object {
private const val PICK_DIRECTORY_REQUEST_CODE = 1
}
private lateinit var savedVolumesAdapter: SavedVolumesAdapter private lateinit var savedVolumesAdapter: SavedVolumesAdapter
private var sessionID = -1 private var sessionID = -1
private var isStartingActivity = false private var isStartingActivity = false
private var isFinishingIntentionally = false private var isFinishingIntentionally = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_open) setContentView(R.layout.activity_open)
@ -107,28 +107,20 @@ class OpenActivity : VolumeActionActivity() {
} }
fun pickDirectory(view: View?) { fun pickDirectory(view: View?) {
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
isStartingActivity = true isStartingActivity = true
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE) safePickDirectory()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onDirectoryPicked(uri: Uri) {
super.onActivityResult(requestCode, resultCode, data) val path = PathUtils.getFullPathFromTreeUri(uri, this)
if (resultCode == Activity.RESULT_OK) { if (path != null){
if (requestCode == PICK_DIRECTORY_REQUEST_CODE) { edit_volume_path.setText(path)
if (data?.data != null) { } else {
val path = PathUtils.getFullPathFromTreeUri(data.data, this) ColoredAlertDialogBuilder(this)
if (path != null){ .setTitle(R.string.error)
edit_volume_path.setText(path) .setMessage(R.string.path_from_uri_null_error_msg)
} else { .setPositiveButton(R.string.ok, null)
ColoredAlertDialogBuilder(this) .show()
.setTitle(R.string.error)
.setMessage(R.string.path_from_uri_null_error_msg)
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
} }
} }

View File

@ -1,7 +1,9 @@
package sushi.hardcore.droidfs package sushi.hardcore.droidfs
import android.app.KeyguardManager import android.app.KeyguardManager
import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.net.Uri
import android.os.Build import android.os.Build
import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyPermanentlyInvalidatedException import android.security.keystore.KeyPermanentlyInvalidatedException
@ -9,6 +11,7 @@ import android.security.keystore.KeyProperties
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt import androidx.biometric.BiometricPrompt
@ -23,10 +26,15 @@ import java.security.KeyStore
import javax.crypto.* import javax.crypto.*
import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.GCMParameterSpec
open class VolumeActionActivity : BaseActivity() { abstract class VolumeActionActivity : BaseActivity() {
protected lateinit var currentVolumeName: String protected lateinit var currentVolumeName: String
protected lateinit var currentVolumePath: String protected lateinit var currentVolumePath: String
protected lateinit var volumeDatabase: VolumeDatabase protected lateinit var volumeDatabase: VolumeDatabase
protected val pickDirectory = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
if (uri != null) {
onDirectoryPicked(uri)
}
}
private var usf_fingerprint = false private var usf_fingerprint = false
private var biometricCanAuthenticateCode: Int = -1 private var biometricCanAuthenticateCode: Int = -1
private lateinit var biometricManager: BiometricManager private lateinit var biometricManager: BiometricManager
@ -48,6 +56,20 @@ open class VolumeActionActivity : BaseActivity() {
private const val GCM_TAG_LEN = 128 private const val GCM_TAG_LEN = 128
} }
protected abstract fun onDirectoryPicked(uri: Uri)
protected fun safePickDirectory() {
try {
pickDirectory.launch(null)
} catch (e: ActivityNotFoundException) {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(R.string.open_tree_failed)
.setPositiveButton(R.string.ok, null)
.show()
}
}
protected fun setupFingerprintStuff(){ protected fun setupFingerprintStuff(){
originalHiddenVolumeSectionLayoutParams = hidden_volume_section.layoutParams as LinearLayout.LayoutParams originalHiddenVolumeSectionLayoutParams = hidden_volume_section.layoutParams as LinearLayout.LayoutParams
originalNormalVolumeSectionLayoutParams = normal_volume_section.layoutParams as LinearLayout.LayoutParams originalNormalVolumeSectionLayoutParams = normal_volume_section.layoutParams as LinearLayout.LayoutParams
@ -137,7 +159,7 @@ open class VolumeActionActivity : BaseActivity() {
return if (!keyguardManager.isKeyguardSecure) { return if (!keyguardManager.isKeyguardSecure) {
1 1
} else { } else {
when (biometricManager.canAuthenticate()){ when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)){
BiometricManager.BIOMETRIC_SUCCESS -> 0 BiometricManager.BIOMETRIC_SUCCESS -> 0
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> 2 BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> 2
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> 3 BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> 3
@ -209,7 +231,7 @@ open class VolumeActionActivity : BaseActivity() {
.setSubtitle(getString(R.string.encrypt_action_description)) .setSubtitle(getString(R.string.encrypt_action_description))
.setDescription(getString(R.string.fingerprint_instruction)) .setDescription(getString(R.string.fingerprint_instruction))
.setNegativeButtonText(getString(R.string.cancel)) .setNegativeButtonText(getString(R.string.cancel))
.setDeviceCredentialAllowed(false) .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
.setConfirmationRequired(false) .setConfirmationRequired(false)
.build() .build()
if (!isCipherReady){ if (!isCipherReady){
@ -233,7 +255,7 @@ open class VolumeActionActivity : BaseActivity() {
.setSubtitle(getString(R.string.decrypt_action_description)) .setSubtitle(getString(R.string.decrypt_action_description))
.setDescription(getString(R.string.fingerprint_instruction)) .setDescription(getString(R.string.fingerprint_instruction))
.setNegativeButtonText(getString(R.string.cancel)) .setNegativeButtonText(getString(R.string.cancel))
.setDeviceCredentialAllowed(false) .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
.setConfirmationRequired(false) .setConfirmationRequired(false)
.build() .build()
this.onPasswordDecrypted = onPasswordDecrypted this.onPasswordDecrypted = onPasswordDecrypted

View File

@ -2,35 +2,148 @@ 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.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.EditText import android.widget.EditText
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import sushi.hardcore.droidfs.CameraActivity import sushi.hardcore.droidfs.CameraActivity
import sushi.hardcore.droidfs.GocryptfsVolume
import sushi.hardcore.droidfs.OpenActivity import sushi.hardcore.droidfs.OpenActivity
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.adapters.IconTextDialogAdapter import sushi.hardcore.droidfs.adapters.IconTextDialogAdapter
import sushi.hardcore.droidfs.file_operations.OperationFile
import sushi.hardcore.droidfs.content_providers.ExternalProvider import sushi.hardcore.droidfs.content_providers.ExternalProvider
import sushi.hardcore.droidfs.GocryptfsVolume import sushi.hardcore.droidfs.file_operations.OperationFile
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 import java.io.File
class ExplorerActivity : BaseExplorerActivity() { class ExplorerActivity : BaseExplorerActivity() {
companion object { companion object {
private const val PICK_DIRECTORY_REQUEST_CODE = 1
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 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 currentItemAction = ItemsActions.NONE private var currentItemAction = ItemsActions.NONE
private val itemsToProcess = ArrayList<OperationFile>() private val itemsToProcess = ArrayList<OperationFile>()
private val pickFromOtherVolumes = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.let { resultIntent ->
val remoteSessionID = resultIntent.getIntExtra("sessionID", -1)
val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID)
val path = resultIntent.getStringExtra("path")
val operationFiles = ArrayList<OperationFile>()
if (path == null){ //multiples elements
val paths = resultIntent.getStringArrayListExtra("paths")
val types = resultIntent.getIntegerArrayListExtra("types")
if (types != null && paths != null){
for (i in paths.indices) {
operationFiles.add(
OperationFile.fromExplorerElement(
ExplorerElement(File(paths[i]).name, types[i].toShort(), -1, -1, PathUtils.getParentPath(paths[i]))
)
)
if (types[i] == 0){ //directory
remoteGocryptfsVolume.recursiveMapFiles(paths[i]).forEach {
operationFiles.add(OperationFile.fromExplorerElement(it))
}
}
}
}
} else {
operationFiles.add(
OperationFile.fromExplorerElement(
ExplorerElement(File(path).name, 1, -1, -1, PathUtils.getParentPath(path))
)
)
}
if (operationFiles.size > 0){
checkPathOverwrite(operationFiles, currentDirectoryPath) { items ->
if (items == null) {
remoteGocryptfsVolume.close()
} else {
fileOperationService.copyElements(items, remoteGocryptfsVolume){ failedItem ->
runOnUiThread {
if (failedItem == null){
Toast.makeText(this, R.string.success_import, Toast.LENGTH_SHORT).show()
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
setCurrentPath(currentDirectoryPath)
}
remoteGocryptfsVolume.close()
}
}
}
} else {
remoteGocryptfsVolume.close()
}
}
}
}
private val pickFiles = registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris ->
if (uris != null) {
importFilesFromUris(uris){ failedItem ->
if (failedItem == null){
ColoredAlertDialogBuilder(this)
.setTitle(R.string.success_import)
.setMessage("""
${getString(R.string.success_import_msg)}
${getString(R.string.ask_for_wipe)}
""".trimIndent())
.setPositiveButton(R.string.yes) { _, _ ->
fileOperationService.wipeUris(uris) { errorMsg ->
runOnUiThread {
if (errorMsg == null){
Toast.makeText(this, R.string.wipe_successful, Toast.LENGTH_SHORT).show()
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.wipe_failed, errorMsg))
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
}
.setNegativeButton(R.string.no, null)
.show()
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
setCurrentPath(currentDirectoryPath)
}
}
}
private val pickDirectory = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
if (uri != null) {
fileOperationService.exportFiles(uri, explorerAdapter.selectedItems.map { i -> explorerElements[i] }){ failedItem ->
runOnUiThread {
if (failedItem == null){
Toast.makeText(this, R.string.success_export, Toast.LENGTH_SHORT).show()
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.export_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
}
unselectAll()
}
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)
@ -82,15 +195,11 @@ class ExplorerActivity : BaseExplorerActivity() {
intent.action = "pick" intent.action = "pick"
intent.putExtra("sessionID", gocryptfsVolume.sessionID) intent.putExtra("sessionID", gocryptfsVolume.sessionID)
isStartingActivity = true isStartingActivity = true
startActivityForResult(intent, PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE) pickFromOtherVolumes.launch(intent)
} }
"importFiles" -> { "importFiles" -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.addCategory(Intent.CATEGORY_OPENABLE)
isStartingActivity = true isStartingActivity = true
startActivityForResult(intent, PICK_FILES_REQUEST_CODE) pickFiles.launch(arrayOf("*/*"))
} }
"createFile" -> { "createFile" -> {
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null) val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
@ -132,136 +241,6 @@ class ExplorerActivity : BaseExplorerActivity() {
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_FILES_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
val uris: MutableList<Uri> = ArrayList()
val singleUri = data.data
if (singleUri == null) { //multiples choices
val clipData = data.clipData
if (clipData != null){
for (i in 0 until clipData.itemCount) {
uris.add(clipData.getItemAt(i).uri)
}
}
} else {
uris.add(singleUri)
}
importFilesFromUris(uris){ failedItem ->
if (failedItem == null){
ColoredAlertDialogBuilder(this)
.setTitle(R.string.success_import)
.setMessage("""
${getString(R.string.success_import_msg)}
${getString(R.string.ask_for_wipe)}
""".trimIndent())
.setPositiveButton(R.string.yes) { _, _ ->
fileOperationService.wipeUris(uris) { errorMsg ->
runOnUiThread {
if (errorMsg == null){
Toast.makeText(this, R.string.wipe_successful, Toast.LENGTH_SHORT).show()
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.wipe_failed, errorMsg))
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
}
.setNegativeButton(R.string.no, null)
.show()
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
setCurrentPath(currentDirectoryPath)
}
}
} else if (requestCode == PICK_DIRECTORY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
data.data?.let { uri ->
fileOperationService.exportFiles(uri, explorerAdapter.selectedItems.map { i -> explorerElements[i] }){ failedItem ->
runOnUiThread {
if (failedItem == null){
Toast.makeText(this, R.string.success_export, Toast.LENGTH_SHORT).show()
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.export_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
unselectAll()
}
}
} else if (requestCode == PICK_OTHER_VOLUME_ITEMS_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
val remoteSessionID = data.getIntExtra("sessionID", -1)
val remoteGocryptfsVolume = GocryptfsVolume(remoteSessionID)
val path = data.getStringExtra("path")
val operationFiles = ArrayList<OperationFile>()
if (path == null){ //multiples elements
val paths = data.getStringArrayListExtra("paths")
val types = data.getIntegerArrayListExtra("types")
if (types != null && paths != null){
for (i in paths.indices) {
operationFiles.add(
OperationFile.fromExplorerElement(
ExplorerElement(File(paths[i]).name, types[i].toShort(), -1, -1, PathUtils.getParentPath(paths[i]))
)
)
if (types[i] == 0){ //directory
remoteGocryptfsVolume.recursiveMapFiles(paths[i]).forEach {
operationFiles.add(OperationFile.fromExplorerElement(it))
}
}
}
}
} else {
operationFiles.add(
OperationFile.fromExplorerElement(
ExplorerElement(File(path).name, 1, -1, -1, PathUtils.getParentPath(path))
)
)
}
if (operationFiles.size > 0){
checkPathOverwrite(operationFiles, currentDirectoryPath) { items ->
if (items == null) {
remoteGocryptfsVolume.close()
} else {
fileOperationService.copyElements(items, remoteGocryptfsVolume){ failedItem ->
runOnUiThread {
if (failedItem == null){
Toast.makeText(this, R.string.success_import, Toast.LENGTH_SHORT).show()
} else {
ColoredAlertDialogBuilder(this)
.setTitle(R.string.error)
.setMessage(getString(R.string.import_failed, failedItem))
.setPositiveButton(R.string.ok, null)
.show()
}
setCurrentPath(currentDirectoryPath)
}
remoteGocryptfsVolume.close()
}
}
}
} else {
remoteGocryptfsVolume.close()
}
}
}
}
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 (currentItemAction != ItemsActions.NONE) { if (currentItemAction != ItemsActions.NONE) {
@ -398,9 +377,8 @@ class ExplorerActivity : BaseExplorerActivity() {
true true
} }
R.id.decrypt -> { R.id.decrypt -> {
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
isStartingActivity = true isStartingActivity = true
startActivityForResult(i, PICK_DIRECTORY_REQUEST_CODE) pickDirectory.launch(null)
true true
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)

View File

@ -194,4 +194,5 @@
<string name="folders_first_summary">Show folders at the beginning of the list</string> <string name="folders_first_summary">Show folders at the beginning of the list</string>
<string name="auto_fit_title">Video player screen auto-rotation</string> <string name="auto_fit_title">Video player screen auto-rotation</string>
<string name="auto_fit_summary">Auto-rotate the screen to fit video dimensions</string> <string name="auto_fit_summary">Auto-rotate the screen to fit video dimensions</string>
<string name="open_tree_failed">No file explorer found. Please install one and retry.</string>
</resources> </resources>