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-19 17:59:24 +02:00
|
|
|
import sushi.hardcore.droidfs.filesystems.CryfsVolume
|
2022-06-18 21:13:16 +02:00
|
|
|
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
|
2022-06-19 17:59:24 +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-04-17 17:38:49 +02:00
|
|
|
private const val KEY_PIN_PASSWORDS = ConstValues.PIN_PASSWORDS_KEY
|
2022-03-05 12:51:02 +01:00
|
|
|
private const val KEY_USF_FINGERPRINT = "fingerprint"
|
|
|
|
|
|
|
|
fun newInstance(
|
|
|
|
themeValue: String,
|
|
|
|
volumePath: String,
|
|
|
|
isHidden: 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 {
|
|
|
|
putString(KEY_THEME_VALUE, themeValue)
|
|
|
|
putString(KEY_VOLUME_PATH, volumePath)
|
|
|
|
putBoolean(KEY_IS_HIDDEN, isHidden)
|
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
|
|
|
|
private var themeValue = ConstValues.DEFAULT_THEME_VALUE
|
|
|
|
private lateinit var volumePath: String
|
|
|
|
private var isHiddenVolume: Boolean = false
|
|
|
|
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 ->
|
2022-03-05 12:51:02 +01:00
|
|
|
arguments.getString(KEY_THEME_VALUE)?.let { themeValue = it }
|
|
|
|
volumePath = arguments.getString(KEY_VOLUME_PATH)!!
|
|
|
|
isHiddenVolume = arguments.getBoolean(KEY_IS_HIDDEN)
|
|
|
|
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) {
|
|
|
|
fingerprintProtector = FingerprintProtector.new(requireActivity(), themeValue, volumeDatabase)
|
|
|
|
}
|
|
|
|
if (!usfFingerprint || fingerprintProtector == null) {
|
|
|
|
binding.checkboxSavePassword.visibility = View.GONE
|
|
|
|
}
|
2022-06-19 17:59:24 +02:00
|
|
|
binding.spinnerVolumeType.adapter = ArrayAdapter(
|
|
|
|
requireContext(),
|
|
|
|
android.R.layout.simple_spinner_item,
|
|
|
|
resources.getStringArray(R.array.volume_types)
|
|
|
|
).apply {
|
|
|
|
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
2022-06-19 17:59:24 +02:00
|
|
|
val encryptionCipherAdapter = ArrayAdapter(
|
2022-03-05 12:51:02 +01:00
|
|
|
requireContext(),
|
|
|
|
android.R.layout.simple_spinner_item,
|
2022-06-19 17:59:24 +02:00
|
|
|
resources.getStringArray(R.array.gocryptfs_encryption_ciphers).toMutableList()
|
2022-03-05 12:51:02 +01:00
|
|
|
).apply {
|
|
|
|
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
|
|
|
}
|
2022-06-19 17:59:24 +02:00
|
|
|
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-19 17:59:24 +02:00
|
|
|
val ciphersArray = if (position == 0) { // Gocryptfs
|
|
|
|
binding.checkboxSavePassword.visibility = View.VISIBLE
|
|
|
|
R.array.gocryptfs_encryption_ciphers
|
|
|
|
} else {
|
|
|
|
binding.checkboxSavePassword.visibility = View.GONE
|
|
|
|
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-19 17:59:24 +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)
|
|
|
|
}
|
|
|
|
|
2022-06-19 17:59:24 +02:00
|
|
|
private fun saveVolume(success: Boolean, volumeType: Byte): SavedVolume? {
|
|
|
|
return if (success) {
|
|
|
|
val volumeName = if (isHiddenVolume) File(volumePath).name else volumePath
|
|
|
|
val volume = SavedVolume(volumeName, isHiddenVolume, volumeType)
|
|
|
|
volumeDatabase.apply {
|
|
|
|
if (isVolumeSaved(volumeName, isHiddenVolume)) // cleaning old saved path
|
|
|
|
removeVolume(volumeName)
|
|
|
|
saveVolume(volume)
|
|
|
|
}
|
|
|
|
volume
|
|
|
|
} else {
|
|
|
|
null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-05 12:51:02 +01:00
|
|
|
private fun createVolume() {
|
2022-06-19 17:59:24 +02:00
|
|
|
val password = WidgetUtil.editTextContentEncode(binding.editPassword)
|
|
|
|
val passwordConfirm = WidgetUtil.editTextContentEncode(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-19 17:59:24 +02:00
|
|
|
Arrays.fill(password, 0)
|
|
|
|
Arrays.fill(passwordConfirm, 0)
|
2022-03-05 12:51:02 +01:00
|
|
|
} else {
|
2022-06-19 17:59:24 +02:00
|
|
|
Arrays.fill(passwordConfirm, 0)
|
2022-04-20 15:17:33 +02:00
|
|
|
var returnedHash: ByteArray? = null
|
|
|
|
if (binding.checkboxSavePassword.isChecked)
|
|
|
|
returnedHash = ByteArray(GocryptfsVolume.KeyLen)
|
2022-06-18 21:13:16 +02:00
|
|
|
object: LoadingTask<SavedVolume?>(requireActivity() as AppCompatActivity, themeValue, R.string.loading_msg_create) {
|
|
|
|
override suspend fun doTask(): SavedVolume? {
|
2022-03-05 12:51:02 +01:00
|
|
|
val volumeFile = File(volumePath)
|
|
|
|
if (!volumeFile.exists())
|
|
|
|
volumeFile.mkdirs()
|
2022-06-19 17:59:24 +02:00
|
|
|
val volume = if (binding.spinnerVolumeType.selectedItem == 0) { // Gocryptfs
|
|
|
|
val xchacha = when (binding.spinnerCipher.selectedItemPosition) {
|
|
|
|
0 -> 0
|
|
|
|
1 -> 1
|
|
|
|
else -> -1
|
|
|
|
}
|
|
|
|
saveVolume(GocryptfsVolume.createVolume(
|
2022-04-20 15:17:33 +02:00
|
|
|
volumePath,
|
|
|
|
password,
|
|
|
|
false,
|
|
|
|
xchacha,
|
|
|
|
GocryptfsVolume.ScryptDefaultLogN,
|
|
|
|
ConstValues.CREATOR,
|
|
|
|
returnedHash
|
2022-06-19 17:59:24 +02:00
|
|
|
), EncryptedVolume.GOCRYPTFS_VOLUME_TYPE)
|
2022-03-05 12:51:02 +01:00
|
|
|
} else {
|
2022-06-19 17:59:24 +02:00
|
|
|
saveVolume(CryfsVolume.create(
|
|
|
|
volumePath,
|
|
|
|
CryfsVolume.getLocalStateDir(activity.filesDir.path),
|
|
|
|
password,
|
|
|
|
resources.getStringArray(R.array.cryfs_encryption_ciphers)[binding.spinnerCipher.selectedItemPosition]
|
|
|
|
), EncryptedVolume.CRYFS_VOLUME_TYPE)
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
2022-06-19 17:59:24 +02:00
|
|
|
Arrays.fill(password, 0)
|
2022-04-20 15:17:33 +02:00
|
|
|
return volume
|
|
|
|
}
|
|
|
|
}.startTask(lifecycleScope) { volume ->
|
|
|
|
if (volume == null) {
|
|
|
|
CustomAlertDialogBuilder(requireContext(), themeValue)
|
|
|
|
.setTitle(R.string.error)
|
|
|
|
.setMessage(R.string.create_volume_failed)
|
|
|
|
.setPositiveButton(R.string.ok, null)
|
|
|
|
.show()
|
|
|
|
} else {
|
|
|
|
@SuppressLint("NewApi") // if fingerprintProtector is null checkboxSavePassword is hidden
|
|
|
|
if (binding.checkboxSavePassword.isChecked && returnedHash != null) {
|
|
|
|
fingerprintProtector!!.let {
|
|
|
|
it.listener = object : FingerprintProtector.Listener {
|
|
|
|
override fun onHashStorageReset() {
|
|
|
|
hashStorageReset = true
|
|
|
|
// retry
|
|
|
|
it.savePasswordHash(volume, returnedHash)
|
|
|
|
}
|
|
|
|
override fun onPasswordHashDecrypted(hash: ByteArray) {} // shouldn't happen here
|
|
|
|
override fun onPasswordHashSaved() {
|
|
|
|
Arrays.fill(returnedHash, 0)
|
|
|
|
onVolumeCreated()
|
|
|
|
}
|
|
|
|
override fun onFailed(pending: Boolean) {
|
|
|
|
if (!pending) {
|
|
|
|
Arrays.fill(returnedHash, 0)
|
|
|
|
onVolumeCreated()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
it.savePasswordHash(volume, returnedHash)
|
|
|
|
}
|
|
|
|
} else onVolumeCreated()
|
2022-03-05 12:51:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun onVolumeCreated() {
|
|
|
|
(activity as AddVolumeActivity).onVolumeAdded(hashStorageReset)
|
|
|
|
}
|
|
|
|
}
|