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.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.RadioButton
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope 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.databinding.FragmentCreateVolumeBinding
import sushi.hardcore.droidfs.filesystems.CryfsVolume import sushi.hardcore.droidfs.filesystems.CryfsVolume
import sushi.hardcore.droidfs.filesystems.EncryptedVolume 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.util.UIUtils
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.io.File import java.io.File
import java.util.* import java.util.Arrays
class CreateVolumeFragment: Fragment() { class CreateVolumeFragment: Fragment() {
internal data class FileSystemInfo(val nameResource: Int, val detailsResource: Int, val ciphersResource: Int)
companion object { companion object {
private const val KEY_THEME_VALUE = "theme" private const val KEY_THEME_VALUE = "theme"
private const val KEY_VOLUME_PATH = "path" private const val KEY_VOLUME_PATH = "path"
@ -33,6 +45,17 @@ class CreateVolumeFragment: Fragment() {
private const val KEY_REMEMBER_VOLUME = "remember" private const val KEY_REMEMBER_VOLUME = "remember"
private const val KEY_PIN_PASSWORDS = Constants.PIN_PASSWORDS_KEY private const val KEY_PIN_PASSWORDS = Constants.PIN_PASSWORDS_KEY
private const val KEY_USF_FINGERPRINT = "fingerprint" 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( fun newInstance(
theme: Theme, theme: Theme,
@ -57,7 +80,7 @@ class CreateVolumeFragment: Fragment() {
private lateinit var binding: FragmentCreateVolumeBinding private lateinit var binding: FragmentCreateVolumeBinding
private lateinit var theme: Theme private lateinit var theme: Theme
private val volumeTypes = ArrayList<String>(2) private val fileSystemInfos = ArrayList<FileSystemInfo>(2)
private lateinit var volumePath: String private lateinit var volumePath: String
private var isHiddenVolume: Boolean = false private var isHiddenVolume: Boolean = false
private var rememberVolume: Boolean = false private var rememberVolume: Boolean = false
@ -92,17 +115,10 @@ class CreateVolumeFragment: Fragment() {
binding.checkboxSavePassword.visibility = View.GONE binding.checkboxSavePassword.visibility = View.GONE
} }
if (!BuildConfig.GOCRYPTFS_DISABLED) { if (!BuildConfig.GOCRYPTFS_DISABLED) {
volumeTypes.add(resources.getString(R.string.gocryptfs)) fileSystemInfos.add(GOCRYPTFS_INFO)
} }
if (!BuildConfig.CRYFS_DISABLED) { if (!BuildConfig.CRYFS_DISABLED) {
volumeTypes.add(resources.getString(R.string.cryfs)) fileSystemInfos.add(CRYFS_INFO)
}
binding.spinnerVolumeType.adapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_spinner_item,
volumeTypes
).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
} }
val encryptionCipherAdapter = ArrayAdapter( val encryptionCipherAdapter = ArrayAdapter(
requireContext(), requireContext(),
@ -111,19 +127,29 @@ class CreateVolumeFragment: Fragment() {
).apply { ).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
} }
binding.spinnerVolumeType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { for ((i, fs) in fileSystemInfos.iterator().withIndex()) {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { with(FileSystemRadioBinding.inflate(layoutInflater)) {
val ciphersArray = if (volumeTypes[position] == resources.getString(R.string.gocryptfs)) { title.text = getString(fs.nameResource)
R.array.gocryptfs_encryption_ciphers details.text = getString(fs.detailsResource)
} else { radio.isChecked = i == 0
R.array.cryfs_encryption_ciphers root.setOnClickListener {
radio.performClick()
} }
with(encryptionCipherAdapter) { radio.setOnCheckedChangeListener { _, isChecked ->
clear() if (isChecked) {
addAll(resources.getStringArray(ciphersArray).asList()) 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 binding.spinnerCipher.adapter = encryptionCipherAdapter
if (pinPasswords) { if (pinPasswords) {
@ -145,6 +171,15 @@ class CreateVolumeFragment: Fragment() {
(activity as AddVolumeActivity).onFragmentLoaded(false) (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() { private fun createVolume() {
val password = UIUtils.encodeEditTextContent(binding.editPassword) val password = UIUtils.encodeEditTextContent(binding.editPassword)
val passwordConfirm = UIUtils.encodeEditTextContent(binding.editPasswordConfirm) val passwordConfirm = UIUtils.encodeEditTextContent(binding.editPasswordConfirm)
@ -173,7 +208,7 @@ class CreateVolumeFragment: Fragment() {
val volumeFile = File(volumePath) val volumeFile = File(volumePath)
if (!volumeFile.exists()) if (!volumeFile.exists())
volumeFile.mkdirs() 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) { val xchacha = when (binding.spinnerCipher.selectedItemPosition) {
0 -> -1 // auto 0 -> -1 // auto
1 -> 0 // AES-GCM 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:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center_vertical" android:gravity="center_vertical">
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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 <RadioGroup
android:id="@+id/spinner_volume_type" android:id="@+id/radio_group_filesystems"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_marginVertical="10dp"/>
android:layout_marginVertical="@dimen/volume_operation_vertical_gap"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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 <EditText
android:id="@+id/edit_password" android:id="@+id/edit_password"
@ -30,13 +30,15 @@
android:inputType="textPassword" android:inputType="textPassword"
android:maxLines="1" android:maxLines="1"
android:autofillHints="password" android:autofillHints="password"
android:hint="@string/password"/> android:hint="@string/password"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/password_confirmation_label" 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 <EditText
android:id="@+id/edit_password_confirm" android:id="@+id/edit_password_confirm"
@ -45,11 +47,13 @@
android:inputType="textPassword" android:inputType="textPassword"
android:maxLines="1" android:maxLines="1"
android:autofillHints="password" android:autofillHints="password"
android:hint="@string/password_confirmation_hint"/> android:hint="@string/password_confirmation_hint"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"/>
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"
android:layout_marginVertical="@dimen/volume_operation_vertical_gap"> android:layout_marginVertical="@dimen/volume_operation_vertical_gap">
<TextView <TextView

View File

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

View File

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