2020-07-17 16:35:39 +02:00
|
|
|
package sushi.hardcore.droidfs
|
|
|
|
|
2022-03-05 12:51:02 +01:00
|
|
|
import android.annotation.SuppressLint
|
2022-03-06 14:45:52 +01:00
|
|
|
import android.content.ComponentName
|
|
|
|
import android.content.Context
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.content.Intent
|
2022-03-06 14:45:52 +01:00
|
|
|
import android.content.ServiceConnection
|
|
|
|
import android.net.Uri
|
2022-03-05 12:51:02 +01:00
|
|
|
import android.os.Build
|
2020-07-17 16:35:39 +02:00
|
|
|
import android.os.Bundle
|
2022-03-06 14:45:52 +01:00
|
|
|
import android.os.IBinder
|
2021-06-07 14:55:01 +02:00
|
|
|
import android.view.Menu
|
|
|
|
import android.view.MenuItem
|
2022-03-05 12:51:02 +01:00
|
|
|
import android.view.View
|
|
|
|
import android.view.WindowManager
|
2022-03-06 14:45:52 +01:00
|
|
|
import android.widget.Toast
|
2022-03-05 12:51:02 +01:00
|
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2022-03-06 14:45:52 +01:00
|
|
|
import androidx.documentfile.provider.DocumentFile
|
2022-03-05 12:51:02 +01:00
|
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
|
|
import sushi.hardcore.droidfs.adapters.VolumeAdapter
|
|
|
|
import sushi.hardcore.droidfs.add_volume.AddVolumeActivity
|
|
|
|
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
|
2021-06-11 20:23:54 +02:00
|
|
|
import sushi.hardcore.droidfs.databinding.ActivityMainBinding
|
2022-03-05 12:51:02 +01:00
|
|
|
import sushi.hardcore.droidfs.databinding.DialogDeleteVolumeBinding
|
|
|
|
import sushi.hardcore.droidfs.databinding.DialogOpenVolumeBinding
|
|
|
|
import sushi.hardcore.droidfs.explorers.ExplorerActivity
|
|
|
|
import sushi.hardcore.droidfs.explorers.ExplorerActivityDrop
|
|
|
|
import sushi.hardcore.droidfs.explorers.ExplorerActivityPick
|
2022-03-06 14:45:52 +01:00
|
|
|
import sushi.hardcore.droidfs.file_operations.FileOperationService
|
2022-03-05 12:51:02 +01:00
|
|
|
import sushi.hardcore.droidfs.util.PathUtils
|
2021-11-09 11:12:09 +01:00
|
|
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
2022-03-05 12:51:02 +01:00
|
|
|
import java.io.File
|
|
|
|
import java.util.*
|
2020-07-17 16:35:39 +02:00
|
|
|
|
2020-07-21 15:05:05 +02:00
|
|
|
class MainActivity : BaseActivity() {
|
2021-06-11 20:23:54 +02:00
|
|
|
|
2022-03-05 12:51:02 +01:00
|
|
|
private lateinit var binding: ActivityMainBinding
|
|
|
|
private lateinit var volumeDatabase: VolumeDatabase
|
|
|
|
private lateinit var volumeAdapter: VolumeAdapter
|
|
|
|
private var fingerprintProtector: FingerprintProtector? = null
|
|
|
|
private var usfFingerprint: Boolean = false
|
|
|
|
private var usfKeepOpen: Boolean = false
|
|
|
|
private var addVolume = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
|
|
when (result.resultCode) {
|
2022-03-06 14:45:52 +01:00
|
|
|
AddVolumeActivity.RESULT_VOLUME_ADDED -> onVolumeAdded()
|
2022-03-05 12:51:02 +01:00
|
|
|
AddVolumeActivity.RESULT_HASH_STORAGE_RESET -> {
|
|
|
|
volumeAdapter.refresh()
|
|
|
|
binding.textNoVolumes.visibility = View.GONE
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private var changePasswordPosition: Int? = null
|
|
|
|
private var changePassword = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
2022-03-06 14:45:52 +01:00
|
|
|
changePasswordPosition?.let { unselect(it) }
|
|
|
|
}
|
|
|
|
private val pickDirectory = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
|
|
|
|
if (uri != null)
|
|
|
|
onDirectoryPicked(uri)
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
2022-03-06 14:45:52 +01:00
|
|
|
private lateinit var fileOperationService: FileOperationService
|
2022-03-05 12:51:02 +01:00
|
|
|
private var pickMode = false
|
|
|
|
private var dropMode = false
|
|
|
|
private var shouldCloseVolume = true // used when launched to pick file from another volume
|
|
|
|
|
2020-07-17 16:35:39 +02:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
2022-03-05 12:51:02 +01:00
|
|
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
2021-06-11 20:23:54 +02:00
|
|
|
setContentView(binding.root)
|
2021-11-09 11:12:09 +01:00
|
|
|
if (sharedPrefs.getBoolean("applicationFirstOpening", true)) {
|
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
|
|
|
.setTitle(R.string.warning)
|
|
|
|
.setMessage(R.string.usf_home_warning_msg)
|
|
|
|
.setCancelable(false)
|
|
|
|
.setPositiveButton(R.string.see_unsafe_features) { _, _ ->
|
|
|
|
val intent = Intent(this, SettingsActivity::class.java)
|
|
|
|
intent.putExtra("screen", "UnsafeFeaturesSettingsFragment")
|
|
|
|
startActivity(intent)
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.ok, null)
|
|
|
|
.setOnDismissListener { sharedPrefs.edit().putBoolean("applicationFirstOpening", false).apply() }
|
|
|
|
.show()
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
pickMode = intent.action == "pick"
|
|
|
|
dropMode = (intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE) && intent.extras != null
|
|
|
|
volumeDatabase = VolumeDatabase(this)
|
|
|
|
volumeAdapter = VolumeAdapter(
|
|
|
|
this,
|
|
|
|
volumeDatabase,
|
|
|
|
!pickMode && !dropMode,
|
|
|
|
!dropMode,
|
|
|
|
::onVolumeItemClick,
|
|
|
|
::onVolumeItemLongClick,
|
|
|
|
)
|
|
|
|
binding.recyclerViewVolumes.adapter = volumeAdapter
|
|
|
|
binding.recyclerViewVolumes.layoutManager = LinearLayoutManager(this)
|
|
|
|
if (volumeAdapter.volumes.isEmpty()) {
|
|
|
|
binding.textNoVolumes.visibility = View.VISIBLE
|
2021-06-07 16:34:50 +02:00
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
if (pickMode) {
|
|
|
|
title = getString(R.string.select_volume)
|
|
|
|
binding.fab.visibility = View.GONE
|
|
|
|
} else {
|
|
|
|
binding.fab.setOnClickListener {
|
|
|
|
addVolume.launch(Intent(this, AddVolumeActivity::class.java))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
usfKeepOpen = sharedPrefs.getBoolean("usf_keep_open", false)
|
|
|
|
usfFingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
fingerprintProtector = FingerprintProtector.new(this, themeValue, volumeDatabase)
|
2021-06-07 16:34:50 +02:00
|
|
|
}
|
2022-03-06 14:45:52 +01:00
|
|
|
Intent(this, FileOperationService::class.java).also {
|
|
|
|
bindService(it, object : ServiceConnection {
|
|
|
|
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
|
|
|
fileOperationService = (service as FileOperationService.LocalBinder).getService()
|
|
|
|
}
|
|
|
|
override fun onServiceDisconnected(arg0: ComponentName) {}
|
|
|
|
}, Context.BIND_AUTO_CREATE)
|
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun onVolumeItemClick(volume: Volume, position: Int) {
|
|
|
|
if (volumeAdapter.selectedItems.isEmpty())
|
|
|
|
openVolume(volume, position)
|
|
|
|
else
|
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onVolumeItemLongClick() {
|
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
|
|
|
|
2022-03-06 14:45:52 +01:00
|
|
|
private fun onVolumeAdded() {
|
|
|
|
volumeAdapter.apply {
|
|
|
|
volumes = volumeDatabase.getVolumes()
|
|
|
|
notifyItemInserted(volumes.size)
|
|
|
|
}
|
|
|
|
binding.textNoVolumes.visibility = View.GONE
|
|
|
|
}
|
|
|
|
|
2022-03-05 12:51:02 +01:00
|
|
|
private fun unselectAll() {
|
|
|
|
volumeAdapter.unSelectAll()
|
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
|
|
|
|
2022-03-06 14:45:52 +01:00
|
|
|
private fun unselect(position: Int) {
|
|
|
|
volumeAdapter.selectedItems.remove(position)
|
|
|
|
volumeAdapter.onVolumeChanged(position)
|
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
|
|
|
|
2022-03-05 12:51:02 +01:00
|
|
|
private fun removeVolumes(volumes: List<Volume>, i: Int = 0, doDeleteVolumeContent: Boolean? = null) {
|
|
|
|
if (i < volumes.size) {
|
|
|
|
if (volumes[i].isHidden) {
|
|
|
|
if (doDeleteVolumeContent == null) {
|
|
|
|
val dialogBinding = DialogDeleteVolumeBinding.inflate(layoutInflater)
|
|
|
|
dialogBinding.textContent.text = getString(R.string.delete_hidden_volume_question, volumes[i].name)
|
|
|
|
// show checkbox only if there is at least one other hidden volume
|
|
|
|
for (j in (i+1 until volumes.size)) {
|
|
|
|
if (volumes[j].isHidden) {
|
|
|
|
dialogBinding.checkboxApplyToAll.visibility = View.VISIBLE
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
|
|
|
.setTitle(R.string.warning)
|
|
|
|
.setView(dialogBinding.root)
|
|
|
|
.setPositiveButton(R.string.forget_only) { _, _ ->
|
|
|
|
volumeDatabase.removeVolume(volumes[i].name)
|
|
|
|
removeVolumes(volumes, i + 1, if (dialogBinding.checkboxApplyToAll.isChecked) false else null)
|
|
|
|
}
|
|
|
|
.setNegativeButton(R.string.delete_volume) { _, _ ->
|
|
|
|
PathUtils.recursiveRemoveDirectory(File(volumes[i].getFullPath(filesDir.path)))
|
|
|
|
volumeDatabase.removeVolume(volumes[i].name)
|
|
|
|
removeVolumes(volumes, i + 1, if (dialogBinding.checkboxApplyToAll.isChecked) true else null)
|
|
|
|
}
|
|
|
|
.setOnCancelListener {
|
|
|
|
volumeAdapter.refresh()
|
|
|
|
invalidateOptionsMenu()
|
|
|
|
}
|
|
|
|
.show()
|
|
|
|
} else {
|
|
|
|
if (doDeleteVolumeContent) {
|
|
|
|
PathUtils.recursiveRemoveDirectory(File(volumes[i].getFullPath(filesDir.path)))
|
|
|
|
}
|
|
|
|
volumeDatabase.removeVolume(volumes[i].name)
|
|
|
|
removeVolumes(volumes, i + 1, doDeleteVolumeContent)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
volumeDatabase.removeVolume(volumes[i].name)
|
|
|
|
removeVolumes(volumes, i + 1, doDeleteVolumeContent)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
volumeAdapter.refresh()
|
|
|
|
invalidateOptionsMenu()
|
|
|
|
if (volumeAdapter.volumes.isEmpty()) {
|
|
|
|
binding.textNoVolumes.visibility = View.VISIBLE
|
|
|
|
}
|
2021-06-07 16:34:50 +02:00
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
|
|
return when (item.itemId) {
|
2022-03-05 12:51:02 +01:00
|
|
|
android.R.id.home -> {
|
|
|
|
if (pickMode || dropMode) {
|
|
|
|
if (pickMode)
|
|
|
|
shouldCloseVolume = false
|
|
|
|
finish()
|
|
|
|
} else {
|
|
|
|
unselectAll()
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
R.id.select_all -> {
|
|
|
|
volumeAdapter.selectAll()
|
|
|
|
invalidateOptionsMenu()
|
|
|
|
true
|
|
|
|
}
|
|
|
|
R.id.remove -> {
|
|
|
|
val selectedVolumes = volumeAdapter.selectedItems.map { i -> volumeAdapter.volumes[i] }
|
|
|
|
removeVolumes(selectedVolumes)
|
|
|
|
true
|
|
|
|
}
|
|
|
|
R.id.forget_password -> {
|
|
|
|
for (i in volumeAdapter.selectedItems) {
|
|
|
|
if (volumeDatabase.removeHash(volumeAdapter.volumes[i]))
|
|
|
|
volumeAdapter.onVolumeChanged(i)
|
|
|
|
}
|
|
|
|
unselectAll()
|
|
|
|
true
|
|
|
|
}
|
|
|
|
R.id.change_password -> {
|
|
|
|
changePasswordPosition = volumeAdapter.selectedItems.elementAt(0)
|
|
|
|
changePassword.launch(Intent(this, ChangePasswordActivity::class.java).apply {
|
|
|
|
putExtra("volume", volumeAdapter.volumes[changePasswordPosition!!])
|
|
|
|
})
|
|
|
|
true
|
|
|
|
}
|
2022-03-06 14:45:52 +01:00
|
|
|
R.id.copy -> {
|
|
|
|
val position = volumeAdapter.selectedItems.elementAt(0)
|
|
|
|
val volume = volumeAdapter.volumes[position]
|
|
|
|
when {
|
|
|
|
volume.isHidden -> {
|
|
|
|
PathUtils.safePickDirectory(pickDirectory, this, themeValue)
|
|
|
|
}
|
|
|
|
File(filesDir, volume.shortName).exists() -> {
|
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.hidden_volume_already_exists)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
else -> {
|
|
|
|
unselect(position)
|
|
|
|
copyVolume(
|
|
|
|
DocumentFile.fromFile(File(volume.name)),
|
|
|
|
DocumentFile.fromFile(filesDir),
|
|
|
|
) {
|
|
|
|
Volume(volume.shortName, true, volume.encryptedHash, volume.iv)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
R.id.settings -> {
|
2020-07-17 16:35:39 +02:00
|
|
|
val intent = Intent(this, SettingsActivity::class.java)
|
|
|
|
startActivity(intent)
|
|
|
|
true
|
|
|
|
}
|
|
|
|
else -> super.onOptionsItemSelected(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
|
|
|
menuInflater.inflate(R.menu.main_activity, menu)
|
2022-03-05 12:51:02 +01:00
|
|
|
menu.findItem(R.id.settings).isVisible = !pickMode && !dropMode
|
|
|
|
val isSelecting = volumeAdapter.selectedItems.isNotEmpty()
|
|
|
|
menu.findItem(R.id.select_all).isVisible = isSelecting && !pickMode && !dropMode
|
|
|
|
menu.findItem(R.id.remove).isVisible = isSelecting && !pickMode && !dropMode
|
|
|
|
var showForgetPassword = isSelecting
|
|
|
|
if (isSelecting) {
|
|
|
|
for (volume in volumeAdapter.selectedItems.map { i -> volumeAdapter.volumes[i] }) {
|
|
|
|
if (volume.encryptedHash == null) {
|
|
|
|
showForgetPassword = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
menu.findItem(R.id.forget_password).isVisible = showForgetPassword && !pickMode
|
|
|
|
menu.findItem(R.id.change_password).isVisible =
|
|
|
|
!pickMode && !dropMode &&
|
|
|
|
volumeAdapter.selectedItems.size == 1 &&
|
|
|
|
volumeAdapter.volumes[volumeAdapter.selectedItems.elementAt(0)].canWrite(filesDir.path)
|
2022-03-06 14:45:52 +01:00
|
|
|
with(menu.findItem(R.id.copy)) {
|
|
|
|
isVisible = !pickMode && !dropMode && volumeAdapter.selectedItems.size == 1
|
|
|
|
if (isVisible) {
|
|
|
|
setTitle(if (volumeAdapter.volumes[volumeAdapter.selectedItems.elementAt(0)].isHidden)
|
|
|
|
R.string.copy_hidden_volume
|
|
|
|
else
|
|
|
|
R.string.copy_external_volume
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
supportActionBar?.setDisplayHomeAsUpEnabled(isSelecting || pickMode || dropMode)
|
2020-07-17 16:35:39 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2022-03-06 14:45:52 +01:00
|
|
|
private fun onDirectoryPicked(uri: Uri) {
|
|
|
|
val position = volumeAdapter.selectedItems.elementAt(0)
|
|
|
|
val volume = volumeAdapter.volumes[position]
|
|
|
|
unselect(position)
|
|
|
|
val dstDocumentFile = DocumentFile.fromTreeUri(this, uri)
|
|
|
|
if (dstDocumentFile == null) {
|
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.path_error)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
} else {
|
|
|
|
copyVolume(
|
|
|
|
DocumentFile.fromFile(File(volume.getFullPath(filesDir.path))),
|
|
|
|
dstDocumentFile,
|
|
|
|
) { dstRootDirectory ->
|
|
|
|
dstRootDirectory.name?.let { name ->
|
|
|
|
val path = PathUtils.getFullPathFromTreeUri(dstRootDirectory.uri, this)
|
|
|
|
if (path == null) null
|
|
|
|
else Volume(
|
|
|
|
PathUtils.pathJoin(path, name),
|
|
|
|
false,
|
|
|
|
volume.encryptedHash,
|
|
|
|
volume.iv
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun copyVolume(srcDocumentFile: DocumentFile, dstDocumentFile: DocumentFile, getResultVolume: (DocumentFile) -> Volume?) {
|
|
|
|
fileOperationService.copyVolume(srcDocumentFile, dstDocumentFile) { dstRootDirectory, failedItem ->
|
|
|
|
runOnUiThread {
|
|
|
|
if (failedItem == null) {
|
|
|
|
dstRootDirectory?.let {
|
|
|
|
getResultVolume(it)?.let { volume ->
|
|
|
|
volumeDatabase.saveVolume(volume)
|
|
|
|
onVolumeAdded()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Toast.makeText(this, R.string.copy_success, Toast.LENGTH_SHORT).show()
|
|
|
|
} else {
|
|
|
|
CustomAlertDialogBuilder(this, themeValue)
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(getString(R.string.copy_failed, failedItem.name))
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-05 12:51:02 +01:00
|
|
|
@SuppressLint("NewApi") // fingerprintProtector is non-null only when SDK_INT >= 23
|
|
|
|
private fun openVolume(volume: Volume, position: Int) {
|
|
|
|
var askForPassword = true
|
|
|
|
fingerprintProtector?.let { fingerprintProtector ->
|
|
|
|
volume.encryptedHash?.let { encryptedHash ->
|
|
|
|
volume.iv?.let { iv ->
|
|
|
|
askForPassword = false
|
|
|
|
fingerprintProtector.listener = object : FingerprintProtector.Listener {
|
|
|
|
override fun onHashStorageReset() {
|
|
|
|
volumeAdapter.refresh()
|
|
|
|
}
|
|
|
|
override fun onPasswordHashDecrypted(hash: ByteArray) {
|
|
|
|
object : LoadingTask(this@MainActivity, themeValue, R.string.loading_msg_open) {
|
|
|
|
override fun doTask(activity: AppCompatActivity) {
|
|
|
|
val sessionId = GocryptfsVolume.init(volume.getFullPath(filesDir.path), null, hash, null)
|
|
|
|
Arrays.fill(hash, 0)
|
|
|
|
if (sessionId != -1)
|
|
|
|
stopTask { startExplorer(sessionId, volume.shortName) }
|
|
|
|
else
|
|
|
|
stopTask {
|
|
|
|
CustomAlertDialogBuilder(activity, themeValue)
|
|
|
|
.setTitle(R.string.open_volume_failed)
|
|
|
|
.setMessage(R.string.open_failed_hash_msg)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
override fun onPasswordHashSaved() {}
|
|
|
|
override fun onFailed(pending: Boolean) {}
|
|
|
|
}
|
|
|
|
fingerprintProtector.loadPasswordHash(volume.shortName, encryptedHash, iv)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (askForPassword)
|
|
|
|
askForPassword(volume, position)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onPasswordSubmitted(volume: Volume, position: Int, dialogBinding: DialogOpenVolumeBinding) {
|
|
|
|
val password = CharArray(dialogBinding.editPassword.text.length)
|
|
|
|
dialogBinding.editPassword.text.getChars(0, password.size, password, 0)
|
|
|
|
// openVolumeWithPassword is responsible for wiping the password
|
|
|
|
openVolumeWithPassword(
|
|
|
|
volume,
|
|
|
|
position,
|
|
|
|
password,
|
|
|
|
dialogBinding.checkboxSavePassword.isChecked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun askForPassword(volume: Volume, position: Int) {
|
|
|
|
val dialogBinding = DialogOpenVolumeBinding.inflate(layoutInflater)
|
|
|
|
if (!usfFingerprint || fingerprintProtector == null) {
|
|
|
|
dialogBinding.checkboxSavePassword.visibility = View.GONE
|
|
|
|
}
|
|
|
|
val dialog = CustomAlertDialogBuilder(this, themeValue)
|
|
|
|
.setTitle(R.string.open_dialog_title)
|
|
|
|
.setView(dialogBinding.root)
|
|
|
|
.setNegativeButton(R.string.cancel, null)
|
|
|
|
.setPositiveButton(R.string.open) { _, _ ->
|
|
|
|
onPasswordSubmitted(volume, position, dialogBinding)
|
|
|
|
}
|
|
|
|
.create()
|
|
|
|
dialogBinding.editPassword.setOnEditorActionListener { _, _, _ ->
|
|
|
|
dialog.dismiss()
|
|
|
|
onPasswordSubmitted(volume, position, dialogBinding)
|
|
|
|
true
|
|
|
|
}
|
|
|
|
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
|
|
|
dialog.show()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun openVolumeWithPassword(volume: Volume, position: Int, password: CharArray, savePasswordHash: Boolean) {
|
|
|
|
val usfFingerprint = sharedPrefs.getBoolean("usf_fingerprint", false)
|
|
|
|
object : LoadingTask(this, themeValue, R.string.loading_msg_open) {
|
|
|
|
override fun doTask(activity: AppCompatActivity) {
|
|
|
|
var returnedHash: ByteArray? = null
|
|
|
|
if (savePasswordHash && usfFingerprint) {
|
|
|
|
returnedHash = ByteArray(GocryptfsVolume.KeyLen)
|
|
|
|
}
|
|
|
|
val sessionId = GocryptfsVolume.init(volume.getFullPath(filesDir.path), password, null, returnedHash)
|
|
|
|
Arrays.fill(password, 0.toChar())
|
|
|
|
if (sessionId != -1) {
|
|
|
|
val fingerprintProtector = fingerprintProtector
|
|
|
|
@SuppressLint("NewApi") // fingerprintProtector is non-null only when SDK_INT >= 23
|
|
|
|
if (savePasswordHash && returnedHash != null && fingerprintProtector != null)
|
|
|
|
stopTask {
|
|
|
|
fingerprintProtector.listener = object : FingerprintProtector.Listener {
|
|
|
|
override fun onHashStorageReset() {
|
|
|
|
volumeAdapter.refresh()
|
|
|
|
}
|
|
|
|
override fun onPasswordHashDecrypted(hash: ByteArray) {}
|
|
|
|
override fun onPasswordHashSaved() {
|
|
|
|
Arrays.fill(returnedHash, 0)
|
|
|
|
volumeAdapter.onVolumeChanged(position)
|
|
|
|
startExplorer(sessionId, volume.shortName)
|
|
|
|
}
|
|
|
|
private var isClosed = false
|
|
|
|
override fun onFailed(pending: Boolean) {
|
|
|
|
if (!isClosed) {
|
|
|
|
GocryptfsVolume(this@MainActivity, sessionId).close()
|
|
|
|
isClosed = true
|
|
|
|
}
|
|
|
|
Arrays.fill(returnedHash, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fingerprintProtector.savePasswordHash(volume, returnedHash)
|
|
|
|
}
|
|
|
|
else
|
|
|
|
stopTask { startExplorer(sessionId, volume.shortName) }
|
|
|
|
} else
|
|
|
|
stopTask {
|
|
|
|
CustomAlertDialogBuilder(activity, themeValue)
|
|
|
|
.setTitle(R.string.open_volume_failed)
|
|
|
|
.setMessage(R.string.open_volume_failed_msg)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.setOnDismissListener {
|
|
|
|
askForPassword(volume, position)
|
|
|
|
}
|
|
|
|
.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun startExplorer(sessionId: Int, volumeShortName: String) {
|
|
|
|
var explorerIntent: Intent? = null
|
|
|
|
if (dropMode) { //import via android share menu
|
|
|
|
explorerIntent = Intent(this, ExplorerActivityDrop::class.java)
|
|
|
|
explorerIntent.action = intent.action //forward action
|
|
|
|
explorerIntent.putExtras(intent.extras!!) //forward extras
|
|
|
|
} else if (pickMode) {
|
|
|
|
explorerIntent = Intent(this, ExplorerActivityPick::class.java)
|
|
|
|
explorerIntent.putExtra("originalSessionID", intent.getIntExtra("sessionID", -1))
|
|
|
|
explorerIntent.flags = Intent.FLAG_ACTIVITY_FORWARD_RESULT
|
|
|
|
}
|
|
|
|
if (explorerIntent == null) {
|
|
|
|
explorerIntent = Intent(this, ExplorerActivity::class.java) //default opening
|
|
|
|
}
|
|
|
|
explorerIntent.putExtra("sessionID", sessionId)
|
|
|
|
explorerIntent.putExtra("volume_name", volumeShortName)
|
|
|
|
startActivity(explorerIntent)
|
|
|
|
if (pickMode)
|
|
|
|
shouldCloseVolume = false
|
|
|
|
if (dropMode || pickMode)
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onBackPressed() {
|
|
|
|
if (volumeAdapter.selectedItems.isNotEmpty()) {
|
|
|
|
unselectAll()
|
|
|
|
} else {
|
|
|
|
if (pickMode)
|
|
|
|
shouldCloseVolume = false
|
|
|
|
super.onBackPressed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onStop() {
|
|
|
|
super.onStop()
|
|
|
|
if (pickMode && !usfKeepOpen) {
|
|
|
|
finish()
|
|
|
|
if (shouldCloseVolume) {
|
|
|
|
val sessionID = intent.getIntExtra("sessionID", -1)
|
|
|
|
if (sessionID != -1) {
|
|
|
|
GocryptfsVolume(this, sessionID).close()
|
|
|
|
RestrictedFileProvider.wipeAll(this)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-07-17 16:35:39 +02:00
|
|
|
}
|
|
|
|
}
|