Replace file systems dropdown with radio buttons

This commit is contained in:
Matéo Duparc 2024-07-18 22:12:53 +02:00
parent 15f288be11
commit 85e24921fa
Signed by: hardcoresushi
GPG Key ID: AFE384344A45E13A
6 changed files with 118 additions and 36 deletions

View File

@ -7,13 +7,23 @@ import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.RadioButton
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import sushi.hardcore.droidfs.*
import sushi.hardcore.droidfs.BuildConfig
import sushi.hardcore.droidfs.Constants
import sushi.hardcore.droidfs.FingerprintProtector
import sushi.hardcore.droidfs.LoadingTask
import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.Theme
import sushi.hardcore.droidfs.VolumeData
import sushi.hardcore.droidfs.VolumeDatabase
import sushi.hardcore.droidfs.VolumeManagerApp
import sushi.hardcore.droidfs.databinding.FileSystemRadioBinding
import sushi.hardcore.droidfs.databinding.FragmentCreateVolumeBinding
import sushi.hardcore.droidfs.filesystems.CryfsVolume
import sushi.hardcore.droidfs.filesystems.EncryptedVolume
@ -23,9 +33,11 @@ import sushi.hardcore.droidfs.util.ObjRef
import sushi.hardcore.droidfs.util.UIUtils
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.io.File
import java.util.*
import java.util.Arrays
class CreateVolumeFragment: Fragment() {
internal data class FileSystemInfo(val nameResource: Int, val detailsResource: Int, val ciphersResource: Int)
companion object {
private const val KEY_THEME_VALUE = "theme"
private const val KEY_VOLUME_PATH = "path"
@ -34,6 +46,17 @@ class CreateVolumeFragment: Fragment() {
private const val KEY_PIN_PASSWORDS = Constants.PIN_PASSWORDS_KEY
private const val KEY_USF_FINGERPRINT = "fingerprint"
private val GOCRYPTFS_INFO = FileSystemInfo(
R.string.gocryptfs,
R.string.gocryptfs_details,
R.array.gocryptfs_encryption_ciphers,
)
private val CRYFS_INFO = FileSystemInfo(
R.string.cryfs,
R.string.cryfs_details,
R.array.cryfs_encryption_ciphers,
)
fun newInstance(
theme: Theme,
volumePath: String,
@ -57,7 +80,7 @@ class CreateVolumeFragment: Fragment() {
private lateinit var binding: FragmentCreateVolumeBinding
private lateinit var theme: Theme
private val volumeTypes = ArrayList<String>(2)
private val fileSystemInfos = ArrayList<FileSystemInfo>(2)
private lateinit var volumePath: String
private var isHiddenVolume: Boolean = false
private var rememberVolume: Boolean = false
@ -92,17 +115,10 @@ class CreateVolumeFragment: Fragment() {
binding.checkboxSavePassword.visibility = View.GONE
}
if (!BuildConfig.GOCRYPTFS_DISABLED) {
volumeTypes.add(resources.getString(R.string.gocryptfs))
fileSystemInfos.add(GOCRYPTFS_INFO)
}
if (!BuildConfig.CRYFS_DISABLED) {
volumeTypes.add(resources.getString(R.string.cryfs))
}
binding.spinnerVolumeType.adapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_spinner_item,
volumeTypes
).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
fileSystemInfos.add(CRYFS_INFO)
}
val encryptionCipherAdapter = ArrayAdapter(
requireContext(),
@ -111,19 +127,29 @@ class CreateVolumeFragment: Fragment() {
).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
binding.spinnerVolumeType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val ciphersArray = if (volumeTypes[position] == resources.getString(R.string.gocryptfs)) {
R.array.gocryptfs_encryption_ciphers
} else {
R.array.cryfs_encryption_ciphers
for ((i, fs) in fileSystemInfos.iterator().withIndex()) {
with(FileSystemRadioBinding.inflate(layoutInflater)) {
title.text = getString(fs.nameResource)
details.text = getString(fs.detailsResource)
radio.isChecked = i == 0
root.setOnClickListener {
radio.performClick()
}
with(encryptionCipherAdapter) {
clear()
addAll(resources.getStringArray(ciphersArray).asList())
radio.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
with(encryptionCipherAdapter) {
clear()
addAll(resources.getStringArray(fs.ciphersResource).asList())
}
binding.radioGroupFilesystems.children.forEach {
if (it != root) {
it.findViewById<RadioButton>(R.id.radio).isChecked = false
}
}
}
}
binding.radioGroupFilesystems.addView(root)
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
binding.spinnerCipher.adapter = encryptionCipherAdapter
if (pinPasswords) {
@ -145,6 +171,15 @@ class CreateVolumeFragment: Fragment() {
(activity as AddVolumeActivity).onFragmentLoaded(false)
}
private fun getSelectedFileSystemIndex(): Int {
for ((i, child) in binding.radioGroupFilesystems.children.iterator().withIndex()) {
if (child.findViewById<RadioButton>(R.id.radio).isChecked) {
return i
}
}
return -1
}
private fun createVolume() {
val password = UIUtils.encodeEditTextContent(binding.editPassword)
val passwordConfirm = UIUtils.encodeEditTextContent(binding.editPasswordConfirm)
@ -173,7 +208,7 @@ class CreateVolumeFragment: Fragment() {
val volumeFile = File(volumePath)
if (!volumeFile.exists())
volumeFile.mkdirs()
val result = if (volumeTypes[binding.spinnerVolumeType.selectedItemPosition] == resources.getString(R.string.gocryptfs)) {
val result = if (fileSystemInfos[getSelectedFileSystemIndex()] == GOCRYPTFS_INFO) {
val xchacha = when (binding.spinnerCipher.selectedItemPosition) {
0 -> -1 // auto
1 -> 0 // AES-GCM

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:paddingVertical="@dimen/selectable_row_vertical_padding">
<RadioButton
android:id="@+id/radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="30dp"
android:layout_marginEnd="10dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_toEndOf="@+id/radio"
android:layout_alignParentEnd="true">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/title_text_size"/>
<TextView
android:id="@+id/details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/textColorSecondary"/>
</LinearLayout>
</RelativeLayout>

View File

@ -3,25 +3,25 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap">
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/volume_type_label"/>
android:text="@string/volume_type_label"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"/>
<Spinner
android:id="@+id/spinner_volume_type"
android:layout_width="wrap_content"
<RadioGroup
android:id="@+id/radio_group_filesystems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="@dimen/volume_operation_vertical_gap"/>
android:layout_marginVertical="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/password_label"/>
android:text="@string/password_label"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"/>
<EditText
android:id="@+id/edit_password"
@ -30,13 +30,15 @@
android:inputType="textPassword"
android:maxLines="1"
android:autofillHints="password"
android:hint="@string/password"/>
android:hint="@string/password"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/password_confirmation_label"
android:layout_marginTop="@dimen/volume_operation_vertical_gap"/>
android:layout_marginTop="@dimen/volume_operation_vertical_gap"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"/>
<EditText
android:id="@+id/edit_password_confirm"
@ -45,11 +47,13 @@
android:inputType="textPassword"
android:maxLines="1"
android:autofillHints="password"
android:hint="@string/password_confirmation_hint"/>
android:hint="@string/password_confirmation_hint"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"
android:layout_marginVertical="@dimen/volume_operation_vertical_gap">
<TextView

View File

@ -13,6 +13,7 @@
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:paddingHorizontal="@dimen/volume_operation_horizontal_gap"
android:paddingVertical="@dimen/selectable_row_vertical_padding"
android:layout_marginBottom="@dimen/volume_operation_vertical_gap">
<ImageView

View File

@ -12,4 +12,5 @@
<dimen name="dialog_padding_top">10dp</dimen>
<dimen name="dialog_text_size">16sp</dimen>
<dimen name="title_file_name_text_size">20sp</dimen>
<dimen name="selectable_row_vertical_padding">10dp</dimen>
</resources>

View File

@ -287,4 +287,6 @@
<string name="usf_background_summary">Don\'t lock volumes when the app goes in background</string>
<string name="usf_keep_open">Keep volumes open</string>
<string name="usf_keep_open_summary">Maintain the app always running in the background to keep volumes open</string>
<string name="gocryptfs_details">Fast, but doesn\'t hide file sizes and directory structure</string>
<string name="cryfs_details">Slower, but protects metadata and prevents replacement attacks</string>
</resources>