2022-03-05 12:51:02 +01:00
|
|
|
package sushi.hardcore.droidfs.add_volume
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint
|
|
|
|
import android.os.Build
|
|
|
|
import android.os.Bundle
|
2022-04-17 17:38:49 +02:00
|
|
|
import android.text.InputType
|
2022-03-05 12:51:02 +01:00
|
|
|
import android.view.LayoutInflater
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
|
|
|
import android.widget.AdapterView
|
|
|
|
import android.widget.ArrayAdapter
|
|
|
|
import android.widget.Toast
|
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
|
|
import androidx.fragment.app.Fragment
|
2022-04-20 15:17:33 +02:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2022-03-05 12:51:02 +01:00
|
|
|
import sushi.hardcore.droidfs.*
|
|
|
|
import sushi.hardcore.droidfs.databinding.FragmentCreateVolumeBinding
|
2022-06-18 21:13:16 +02:00
|
|
|
import sushi.hardcore.droidfs.filesystems.CryfsVolume
|
|
|
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
|
|
|
import sushi.hardcore.droidfs.filesystems.GocryptfsVolume
|
2023-02-28 22:50:59 +01:00
|
|
|
import sushi.hardcore.droidfs.util.Compat
|
2022-06-29 13:43:56 +02:00
|
|
|
import sushi.hardcore.droidfs.util.ObjRef
|
2022-06-18 21:13:16 +02:00
|
|
|
import sushi.hardcore.droidfs.util.WidgetUtil
|
2022-03-05 12:51:02 +01:00
|
|
|
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
|
|
|
|
import java.io.File
|
|
|
|
import java.util.*
|
|
|
|
|
|
|
|
class CreateVolumeFragment: Fragment() {
|
|
|
|
companion object {
|
|
|
|
private const val KEY_THEME_VALUE = "theme"
|
|
|
|
private const val KEY_VOLUME_PATH = "path"
|
|
|
|
private const val KEY_IS_HIDDEN = "hidden"
|
2022-09-30 21:22:37 +02:00
|
|
|
private const val KEY_REMEMBER_VOLUME = "remember"
|
2023-02-06 10:52:51 +01:00
|
|
|
private const val KEY_PIN_PASSWORDS = Constants.PIN_PASSWORDS_KEY
|
2022-03-05 12:51:02 +01:00
|
|
|
private const val KEY_USF_FINGERPRINT = "fingerprint"
|
|
|
|
|
|
|
|
fun newInstance(
|
2023-02-28 22:50:59 +01:00
|
|
|
theme: Theme,
|
2022-03-05 12:51:02 +01:00
|
|
|
volumePath: String,
|
|
|
|
isHidden: Boolean,
|
2022-09-30 21:22:37 +02:00
|
|
|
rememberVolume: Boolean,
|
2022-04-17 17:38:49 +02:00
|
|
|
pinPasswords: Boolean,
|
2022-03-05 12:51:02 +01:00
|
|
|
usfFingerprint: Boolean,
|
|
|
|
): CreateVolumeFragment {
|
|
|
|
return CreateVolumeFragment().apply {
|
|
|
|
arguments = Bundle().apply {
|
2023-02-28 22:50:59 +01:00
|
|
|
putParcelable(KEY_THEME_VALUE, theme)
|
2022-03-05 12:51:02 +01:00
|
|
|
putString(KEY_VOLUME_PATH, volumePath)
|
|
|
|
putBoolean(KEY_IS_HIDDEN, isHidden)
|
2022-09-30 21:22:37 +02:00
|
|
|
putBoolean(KEY_REMEMBER_VOLUME, rememberVolume)
|
2022-04-17 17:38:49 +02:00
|
|
|
putBoolean(KEY_PIN_PASSWORDS, pinPasswords)
|
2022-03-05 12:51:02 +01:00
|
|
|
putBoolean(KEY_USF_FINGERPRINT, usfFingerprint)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private lateinit var binding: FragmentCreateVolumeBinding
|
2023-02-28 22:50:59 +01:00
|
|
|
private lateinit var theme: Theme
|
2022-06-18 21:13:16 +02:00
|
|
|
private val volumeTypes = ArrayList<String>(2)
|
2022-03-05 12:51:02 +01:00
|
|
|
private lateinit var volumePath: String
|
|
|
|
private var isHiddenVolume: Boolean = false
|
2022-09-30 21:22:37 +02:00
|
|
|
private var rememberVolume: Boolean = false
|
2022-03-05 12:51:02 +01:00
|
|
|
private var usfFingerprint: Boolean = false
|
|
|
|
private lateinit var volumeDatabase: VolumeDatabase
|
|
|
|
private var fingerprintProtector: FingerprintProtector? = null
|
|
|
|
private var hashStorageReset = false
|
|
|
|
|
|
|
|
override fun onCreateView(
|
|
|
|
inflater: LayoutInflater,
|
|
|
|
container: ViewGroup?,
|
|
|
|
savedInstanceState: Bundle?
|
|
|
|
): View {
|
|
|
|
binding = FragmentCreateVolumeBinding.inflate(layoutInflater, container, false)
|
|
|
|
return binding.root
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
2022-04-17 17:38:49 +02:00
|
|
|
val pinPasswords = requireArguments().let { arguments ->
|
2023-02-28 22:50:59 +01:00
|
|
|
theme = Compat.getParcelable(arguments, KEY_THEME_VALUE)!!
|
2022-03-05 12:51:02 +01:00
|
|
|
volumePath = arguments.getString(KEY_VOLUME_PATH)!!
|
|
|
|
isHiddenVolume = arguments.getBoolean(KEY_IS_HIDDEN)
|
2022-09-30 21:22:37 +02:00
|
|
|
rememberVolume = arguments.getBoolean(KEY_REMEMBER_VOLUME)
|
2022-03-05 12:51:02 +01:00
|
|
|
usfFingerprint = arguments.getBoolean(KEY_USF_FINGERPRINT)
|
2022-04-17 17:38:49 +02:00
|
|
|
arguments.getBoolean(KEY_PIN_PASSWORDS)
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
|
|
|
volumeDatabase = VolumeDatabase(requireContext())
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
2023-02-28 22:50:59 +01:00
|
|
|
fingerprintProtector = FingerprintProtector.new(requireActivity(), theme, volumeDatabase)
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
2022-09-30 21:22:37 +02:00
|
|
|
if (!rememberVolume || !usfFingerprint || fingerprintProtector == null) {
|
2022-03-05 12:51:02 +01:00
|
|
|
binding.checkboxSavePassword.visibility = View.GONE
|
|
|
|
}
|
2022-06-18 21:13:16 +02:00
|
|
|
if (!BuildConfig.GOCRYPTFS_DISABLED) {
|
|
|
|
volumeTypes.add(resources.getString(R.string.gocryptfs))
|
2022-04-17 17:38:49 +02:00
|
|
|
}
|
2022-06-18 21:13:16 +02:00
|
|
|
if (!BuildConfig.CRYFS_DISABLED) {
|
|
|
|
volumeTypes.add(resources.getString(R.string.cryfs))
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
2022-06-18 21:13:16 +02:00
|
|
|
binding.spinnerVolumeType.adapter = ArrayAdapter(
|
2022-03-05 12:51:02 +01:00
|
|
|
requireContext(),
|
|
|
|
android.R.layout.simple_spinner_item,
|
2022-06-18 21:13:16 +02:00
|
|
|
volumeTypes
|
2022-03-05 12:51:02 +01:00
|
|
|
).apply {
|
|
|
|
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
|
|
|
}
|
2022-06-18 21:13:16 +02:00
|
|
|
val encryptionCipherAdapter = ArrayAdapter(
|
|
|
|
requireContext(),
|
|
|
|
android.R.layout.simple_spinner_item,
|
|
|
|
resources.getStringArray(R.array.gocryptfs_encryption_ciphers).toMutableList()
|
|
|
|
).apply {
|
|
|
|
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
|
|
|
}
|
|
|
|
binding.spinnerVolumeType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
2022-03-05 12:51:02 +01:00
|
|
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
2022-06-18 21:13:16 +02:00
|
|
|
val ciphersArray = if (volumeTypes[position] == resources.getString(R.string.gocryptfs)) {
|
|
|
|
R.array.gocryptfs_encryption_ciphers
|
|
|
|
} else {
|
|
|
|
R.array.cryfs_encryption_ciphers
|
|
|
|
}
|
|
|
|
with(encryptionCipherAdapter) {
|
|
|
|
clear()
|
|
|
|
addAll(resources.getStringArray(ciphersArray).asList())
|
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
|
|
|
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
|
|
|
}
|
2022-06-18 21:13:16 +02:00
|
|
|
binding.spinnerCipher.adapter = encryptionCipherAdapter
|
|
|
|
if (pinPasswords) {
|
|
|
|
arrayOf(binding.editPassword, binding.editPasswordConfirm).forEach {
|
|
|
|
it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
|
|
|
|
}
|
|
|
|
}
|
|
|
|
binding.editPasswordConfirm.setOnEditorActionListener { _, _, _ ->
|
|
|
|
createVolume()
|
|
|
|
true
|
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
binding.buttonCreate.setOnClickListener {
|
|
|
|
createVolume()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
|
|
|
super.onViewStateRestored(savedInstanceState)
|
|
|
|
(activity as AddVolumeActivity).onFragmentLoaded(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun createVolume() {
|
2022-06-18 21:13:16 +02:00
|
|
|
val password = WidgetUtil.encodeEditTextContent(binding.editPassword)
|
|
|
|
val passwordConfirm = WidgetUtil.encodeEditTextContent(binding.editPasswordConfirm)
|
2022-03-05 12:51:02 +01:00
|
|
|
if (!password.contentEquals(passwordConfirm)) {
|
|
|
|
Toast.makeText(requireContext(), R.string.passwords_mismatch, Toast.LENGTH_SHORT).show()
|
2022-06-18 21:13:16 +02:00
|
|
|
Arrays.fill(password, 0)
|
|
|
|
Arrays.fill(passwordConfirm, 0)
|
2022-03-05 12:51:02 +01:00
|
|
|
} else {
|
2022-06-18 21:13:16 +02:00
|
|
|
Arrays.fill(passwordConfirm, 0)
|
2022-06-29 13:43:56 +02:00
|
|
|
val returnedHash: ObjRef<ByteArray?>? = if (binding.checkboxSavePassword.isChecked) {
|
|
|
|
ObjRef(null)
|
|
|
|
} else {
|
|
|
|
null
|
|
|
|
}
|
2023-03-07 23:25:17 +01:00
|
|
|
val encryptedVolume = ObjRef<EncryptedVolume?>(null)
|
2023-02-28 22:50:59 +01:00
|
|
|
object: LoadingTask<Byte>(requireActivity() as AppCompatActivity, theme, R.string.loading_msg_create) {
|
2022-09-30 21:22:37 +02:00
|
|
|
private fun generateResult(success: Boolean, volumeType: Byte): Byte {
|
|
|
|
return if (success) {
|
|
|
|
volumeType
|
|
|
|
} else {
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override suspend fun doTask(): Byte {
|
2022-03-05 12:51:02 +01:00
|
|
|
val volumeFile = File(volumePath)
|
|
|
|
if (!volumeFile.exists())
|
|
|
|
volumeFile.mkdirs()
|
2022-09-30 21:22:37 +02:00
|
|
|
val result = if (volumeTypes[binding.spinnerVolumeType.selectedItemPosition] == resources.getString(R.string.gocryptfs)) {
|
2022-06-18 21:13:16 +02:00
|
|
|
val xchacha = when (binding.spinnerCipher.selectedItemPosition) {
|
|
|
|
0 -> 0
|
|
|
|
1 -> 1
|
|
|
|
else -> -1
|
|
|
|
}
|
2022-09-30 21:22:37 +02:00
|
|
|
generateResult(GocryptfsVolume.createAndOpenVolume(
|
2022-04-20 15:17:33 +02:00
|
|
|
volumePath,
|
|
|
|
password,
|
|
|
|
false,
|
|
|
|
xchacha,
|
2022-06-29 13:43:56 +02:00
|
|
|
returnedHash?.apply {
|
|
|
|
value = ByteArray(GocryptfsVolume.KeyLen)
|
|
|
|
}?.value,
|
2022-09-30 21:22:37 +02:00
|
|
|
encryptedVolume,
|
2022-06-18 21:13:16 +02:00
|
|
|
), EncryptedVolume.GOCRYPTFS_VOLUME_TYPE)
|
2022-03-05 12:51:02 +01:00
|
|
|
} else {
|
2022-09-30 21:22:37 +02:00
|
|
|
generateResult(CryfsVolume.create(
|
2022-06-18 21:13:16 +02:00
|
|
|
volumePath,
|
|
|
|
CryfsVolume.getLocalStateDir(activity.filesDir.path),
|
|
|
|
password,
|
2022-06-29 13:43:56 +02:00
|
|
|
returnedHash,
|
2022-09-30 21:22:37 +02:00
|
|
|
resources.getStringArray(R.array.cryfs_encryption_ciphers)[binding.spinnerCipher.selectedItemPosition],
|
|
|
|
encryptedVolume,
|
2022-06-18 21:13:16 +02:00
|
|
|
), EncryptedVolume.CRYFS_VOLUME_TYPE)
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
2022-06-18 21:13:16 +02:00
|
|
|
Arrays.fill(password, 0)
|
2022-09-30 21:22:37 +02:00
|
|
|
return result
|
2022-04-20 15:17:33 +02:00
|
|
|
}
|
2022-09-30 21:22:37 +02:00
|
|
|
}.startTask(lifecycleScope) { result ->
|
|
|
|
if (result.compareTo(-1) == 0) {
|
2023-02-28 22:50:59 +01:00
|
|
|
CustomAlertDialogBuilder(requireContext(), theme)
|
2022-04-20 15:17:33 +02:00
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.create_volume_failed)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
} else {
|
2022-09-30 21:22:37 +02:00
|
|
|
val volumeName = if (isHiddenVolume) File(volumePath).name else volumePath
|
|
|
|
val volume = VolumeData(volumeName, isHiddenVolume, result)
|
|
|
|
var isVolumeSaved = false
|
|
|
|
volumeDatabase.apply {
|
|
|
|
if (isVolumeSaved(volumeName, isHiddenVolume)) // cleaning old saved path
|
|
|
|
removeVolume(volumeName)
|
|
|
|
if (rememberVolume) {
|
|
|
|
isVolumeSaved = saveVolume(volume)
|
|
|
|
}
|
|
|
|
}
|
2023-03-07 23:25:17 +01:00
|
|
|
val volumeId = encryptedVolume.value?.let {
|
|
|
|
(activity?.application as VolumeManagerApp).volumeManager.insert(it, volume)
|
|
|
|
}
|
2022-04-20 15:17:33 +02:00
|
|
|
@SuppressLint("NewApi") // if fingerprintProtector is null checkboxSavePassword is hidden
|
2022-09-30 21:22:37 +02:00
|
|
|
if (isVolumeSaved && binding.checkboxSavePassword.isChecked && returnedHash != null) {
|
2022-04-20 15:17:33 +02:00
|
|
|
fingerprintProtector!!.let {
|
|
|
|
it.listener = object : FingerprintProtector.Listener {
|
|
|
|
override fun onHashStorageReset() {
|
|
|
|
hashStorageReset = true
|
|
|
|
// retry
|
2022-06-29 13:43:56 +02:00
|
|
|
it.savePasswordHash(volume, returnedHash.value!!)
|
2022-04-20 15:17:33 +02:00
|
|
|
}
|
|
|
|
override fun onPasswordHashDecrypted(hash: ByteArray) {} // shouldn't happen here
|
|
|
|
override fun onPasswordHashSaved() {
|
2022-06-29 13:43:56 +02:00
|
|
|
Arrays.fill(returnedHash.value!!, 0)
|
2023-03-07 23:25:17 +01:00
|
|
|
onVolumeCreated(volumeId, volume.shortName)
|
2022-04-20 15:17:33 +02:00
|
|
|
}
|
|
|
|
override fun onFailed(pending: Boolean) {
|
|
|
|
if (!pending) {
|
2022-06-29 13:43:56 +02:00
|
|
|
Arrays.fill(returnedHash.value!!, 0)
|
2023-03-07 23:25:17 +01:00
|
|
|
onVolumeCreated(volumeId, volume.shortName)
|
2022-04-20 15:17:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-29 13:43:56 +02:00
|
|
|
it.savePasswordHash(volume, returnedHash.value!!)
|
2022-04-20 15:17:33 +02:00
|
|
|
}
|
2022-09-30 21:22:37 +02:00
|
|
|
} else {
|
2023-03-07 23:25:17 +01:00
|
|
|
onVolumeCreated(volumeId, volume.shortName)
|
2022-09-30 21:22:37 +02:00
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-07 23:25:17 +01:00
|
|
|
private fun onVolumeCreated(id: Int?, volumeShortName: String) {
|
2022-09-30 21:22:37 +02:00
|
|
|
(activity as AddVolumeActivity).apply {
|
2023-03-07 23:25:17 +01:00
|
|
|
if (rememberVolume || id == null) {
|
2022-09-30 21:22:37 +02:00
|
|
|
finish()
|
|
|
|
} else {
|
2023-03-07 23:25:17 +01:00
|
|
|
startExplorer(id, volumeShortName)
|
2022-09-30 21:22:37 +02:00
|
|
|
}
|
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
2023-02-02 19:37:10 +01:00
|
|
|
|
|
|
|
override fun onStop() {
|
|
|
|
super.onStop()
|
|
|
|
binding.editPassword.text.clear()
|
|
|
|
binding.editPasswordConfirm.text.clear()
|
|
|
|
}
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|