Improve volume adding UI

This commit is contained in:
Matéo Duparc 2024-07-22 21:06:29 +02:00
parent 85e24921fa
commit c0dcaed8d2
Signed by untrusted user: hardcoresushi
GPG Key ID: AFE384344A45E13A
15 changed files with 270 additions and 183 deletions

View File

@ -79,10 +79,12 @@ class VolumeData(
if (other !is VolumeData) { if (other !is VolumeData) {
return false return false
} }
return other.uuid == uuid return other.uuid == uuid || (other.name == name && other.isHidden == isHidden)
} }
override fun hashCode() = uuid.hashCode() override fun hashCode(): Int {
return name.hashCode()+isHidden.hashCode()
}
companion object { companion object {
const val VOLUMES_DIRECTORY = "volumes" const val VOLUMES_DIRECTORY = "volumes"

View File

@ -1,6 +1,16 @@
package sushi.hardcore.droidfs.add_volume package sushi.hardcore.droidfs.add_volume
import sushi.hardcore.droidfs.R
enum class Action { enum class Action {
OPEN,
ADD, ADD,
CREATE, CREATE,
;
fun getStringResId() = when (this) {
OPEN -> R.string.open
ADD -> R.string.add_volume
CREATE -> R.string.create_volume
}
} }

View File

@ -67,17 +67,17 @@ class AddVolumeActivity: BaseActivity() {
finish() finish()
} }
fun onVolumeSelected(volume: VolumeData, rememberVolume: Boolean) { fun onVolumeAdded() {
if (rememberVolume) { setResult(RESULT_USER_BACK)
setResult(RESULT_USER_BACK) finish()
finish() }
} else {
volumeOpener.openVolume(volume, false, object : VolumeOpener.VolumeOpenerCallbacks { fun openVolume(volume: VolumeData, isVolumeKnown: Boolean) {
override fun onVolumeOpened(id: Int) { volumeOpener.openVolume(volume, isVolumeKnown, object : VolumeOpener.VolumeOpenerCallbacks {
startExplorer(id, volume.shortName) override fun onVolumeOpened(id: Int) {
} startExplorer(id, volume.shortName)
}) }
} })
} }
fun createVolume(volumePath: String, isHidden: Boolean, rememberVolume: Boolean) { fun createVolume(volumePath: String, isHidden: Boolean, rememberVolume: Boolean) {

View File

@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.add_volume
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -15,10 +16,14 @@ import android.text.TextWatcher
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.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModel
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import sushi.hardcore.droidfs.Constants import sushi.hardcore.droidfs.Constants
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
@ -35,6 +40,10 @@ import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.io.File import java.io.File
class SelectPathFragment: Fragment() { class SelectPathFragment: Fragment() {
internal class InputViewModel: ViewModel() {
var showEditText = false
}
companion object { companion object {
private const val KEY_THEME_VALUE = "theme" private const val KEY_THEME_VALUE = "theme"
private const val KEY_PICK_MODE = "pick" private const val KEY_PICK_MODE = "pick"
@ -74,6 +83,7 @@ class SelectPathFragment: Fragment() {
private var originalRememberVolume = true private var originalRememberVolume = true
private var currentVolumeData: VolumeData? = null private var currentVolumeData: VolumeData? = null
private var volumeAction: Action? = null private var volumeAction: Action? = null
private val inputViewModel: InputViewModel by viewModels()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -93,17 +103,13 @@ class SelectPathFragment: Fragment() {
theme = Compat.getParcelable(arguments, KEY_THEME_VALUE)!! theme = Compat.getParcelable(arguments, KEY_THEME_VALUE)!!
pickMode = arguments.getBoolean(KEY_PICK_MODE) pickMode = arguments.getBoolean(KEY_PICK_MODE)
} }
if (pickMode) {
binding.buttonAction.text = getString(R.string.add_volume)
}
volumeDatabase = VolumeDatabase(requireContext()) volumeDatabase = VolumeDatabase(requireContext())
filesDir = requireContext().filesDir.path filesDir = requireContext().filesDir.path
binding.containerHiddenVolume.setOnClickListener { binding.containerHiddenVolume.setOnClickListener {
binding.switchHiddenVolume.performClick() binding.switchHiddenVolume.performClick()
} }
binding.switchHiddenVolume.setOnClickListener { binding.switchHiddenVolume.setOnClickListener {
showRightSection() updateUi()
refreshStatus(binding.editVolumeName.text)
} }
binding.buttonPickDirectory.setOnClickListener { binding.buttonPickDirectory.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@ -137,22 +143,41 @@ class SelectPathFragment: Fragment() {
launchPickDirectory() launchPickDirectory()
} }
} }
binding.buttonEnterPath.setOnClickListener {
inputViewModel.showEditText = true
updateUi()
binding.editVolumeName.requestFocus()
(app.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(
binding.editVolumeName,
InputMethodManager.SHOW_IMPLICIT
)
}
binding.editVolumeName.addTextChangedListener(object: TextWatcher { binding.editVolumeName.addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) {} override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
refreshStatus(s) updateUi(s)
} }
}) })
binding.switchRemember.setOnCheckedChangeListener { _, _ -> refreshButtonText() } binding.switchRemember.setOnCheckedChangeListener { _, _ -> updateUi() }
binding.editVolumeName.setOnEditorActionListener { _, _, _ -> onPathSelected(); true } binding.editVolumeName.setOnEditorActionListener { _, _, _ ->
if (binding.editVolumeName.text.isEmpty()) {
Toast.makeText(
requireContext(),
if (binding.switchHiddenVolume.isChecked) R.string.empty_volume_name else R.string.empty_volume_path,
Toast.LENGTH_SHORT
).show()
} else {
onPathSelected()
}
true
}
binding.buttonAction.setOnClickListener { onPathSelected() } binding.buttonAction.setOnClickListener { onPathSelected() }
} }
override fun onViewStateRestored(savedInstanceState: Bundle?) { override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState) super.onViewStateRestored(savedInstanceState)
(activity as AddVolumeActivity).onFragmentLoaded(true) (activity as AddVolumeActivity).onFragmentLoaded(true)
showRightSection()
} }
private fun launchPickDirectory() { private fun launchPickDirectory() {
@ -160,70 +185,83 @@ class SelectPathFragment: Fragment() {
PathUtils.safePickDirectory(pickDirectory, requireContext(), theme) PathUtils.safePickDirectory(pickDirectory, requireContext(), theme)
} }
private fun showRightSection() { private fun updateUi(volumeName: CharSequence = binding.editVolumeName.text) {
if (binding.switchHiddenVolume.isChecked) { var warning = -1
binding.textLabel.text = requireContext().getString(R.string.volume_name_label) fun updateWarning() {
binding.editVolumeName.hint = requireContext().getString(R.string.volume_name_hint) if (warning == -1) {
binding.buttonPickDirectory.visibility = View.GONE binding.textWarning.isVisible = false
} else {
binding.textLabel.text = requireContext().getString(R.string.volume_path_label)
binding.editVolumeName.hint = requireContext().getString(R.string.volume_path_hint)
binding.buttonPickDirectory.visibility = View.VISIBLE
}
}
private fun refreshButtonText() {
binding.buttonAction.text = getString(
if (pickMode || volumeAction == Action.ADD) {
if (binding.switchRemember.isChecked || currentVolumeData != null) {
R.string.add_volume
} else {
R.string.open_volume
}
} else { } else {
R.string.create_volume binding.textWarning.isVisible = true
binding.textWarning.text = getString(warning)
} }
) }
}
private fun refreshStatus(content: CharSequence) { val hidden = binding.switchHiddenVolume.isChecked
binding.editVolumeName.isVisible = hidden || inputViewModel.showEditText
binding.buttonPickDirectory.isVisible = !hidden
binding.textOr.isVisible = !hidden && !inputViewModel.showEditText
binding.buttonEnterPath.isVisible = !hidden && !inputViewModel.showEditText
if (hidden) {
binding.textLabel.text = getString(R.string.volume_name_label)
binding.editVolumeName.hint = getString(R.string.volume_name_hint)
} else {
binding.textLabel.text = getString(R.string.volume_path_label)
binding.editVolumeName.hint = getString(R.string.volume_path_hint)
}
if (hidden && volumeName.contains(PathUtils.SEPARATOR)) {
warning = R.string.error_slash_in_name
}
// exit early if possible to avoid filesystem queries
if (volumeName.isEmpty() || warning != -1 || (!hidden && !inputViewModel.showEditText)) {
binding.buttonAction.isVisible = false
binding.switchRemember.isVisible = false
updateWarning()
return
}
val path = File(getCurrentVolumePath()) val path = File(getCurrentVolumePath())
volumeAction = if (path.isDirectory) { volumeAction = if (path.isDirectory) {
if (path.list()?.isEmpty() == true || content.isEmpty()) Action.CREATE else Action.ADD if (path.list()?.isEmpty() == true) {
Action.CREATE
} else if (pickMode || !binding.switchRemember.isChecked) {
Action.OPEN
} else {
Action.ADD
}
} else { } else {
Action.CREATE Action.CREATE
} }
currentVolumeData = if (volumeAction == Action.CREATE) { val valid = !(volumeAction == Action.CREATE && pickMode)
null binding.switchRemember.isVisible = valid
} else { binding.buttonAction.isVisible = valid
volumeDatabase.getVolume(content.toString(), binding.switchHiddenVolume.isChecked) if (valid) {
} binding.buttonAction.text = getString(volumeAction!!.getStringResId())
binding.textWarning.visibility = if (volumeAction == Action.CREATE && pickMode) { currentVolumeData = if (volumeAction == Action.CREATE) {
binding.textWarning.text = getString(R.string.choose_existing_volume) null
binding.buttonAction.isEnabled = false
View.VISIBLE
} else {
refreshButtonText()
binding.buttonAction.isEnabled = true
if (currentVolumeData == null) {
View.GONE
} else { } else {
binding.textWarning.text = getString(R.string.volume_alread_saved) volumeDatabase.getVolume(volumeName.toString(), hidden)
View.VISIBLE
} }
if (currentVolumeData != null) {
warning = R.string.volume_alread_saved
}
} else {
warning = R.string.choose_existing_volume
} }
updateWarning()
} }
private fun onDirectoryPicked(uri: Uri) { private fun onDirectoryPicked(uri: Uri) {
val path = PathUtils.getFullPathFromTreeUri(uri, requireContext()) val path = PathUtils.getFullPathFromTreeUri(uri, requireContext())
if (path != null) if (path == null) {
binding.editVolumeName.setText(path)
else
CustomAlertDialogBuilder(requireContext(), theme) CustomAlertDialogBuilder(requireContext(), theme)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.path_error) .setMessage(R.string.path_error)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
} else {
binding.editVolumeName.setText(path)
inputViewModel.showEditText = true
updateUi(path)
}
} }
private fun getCurrentVolumePath(): String { private fun getCurrentVolumePath(): String {
@ -243,13 +281,7 @@ class SelectPathFragment: Fragment() {
if (currentVolumeData == null) { // volume not known if (currentVolumeData == null) { // volume not known
val currentVolumeValue = binding.editVolumeName.text.toString() val currentVolumeValue = binding.editVolumeName.text.toString()
val isHidden = binding.switchHiddenVolume.isChecked val isHidden = binding.switchHiddenVolume.isChecked
if (currentVolumeValue.isEmpty()) { if (isHidden && currentVolumeValue.contains(PathUtils.SEPARATOR)) {
Toast.makeText(
requireContext(),
if (isHidden) R.string.enter_volume_name else R.string.enter_volume_path,
Toast.LENGTH_SHORT
).show()
} else if (isHidden && currentVolumeValue.contains(PathUtils.SEPARATOR)) {
Toast.makeText(requireContext(), R.string.error_slash_in_name, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.error_slash_in_name, Toast.LENGTH_SHORT).show()
} else if (isHidden && volumeAction == Action.CREATE) { } else if (isHidden && volumeAction == Action.CREATE) {
CustomAlertDialogBuilder(requireContext(), theme) CustomAlertDialogBuilder(requireContext(), theme)
@ -263,71 +295,83 @@ class SelectPathFragment: Fragment() {
onNewVolumeSelected(currentVolumeValue, isHidden) onNewVolumeSelected(currentVolumeValue, isHidden)
} }
} else { } else {
(activity as AddVolumeActivity).onVolumeSelected(currentVolumeData!!, true) with (activity as AddVolumeActivity) {
if (volumeAction!! == Action.OPEN) {
openVolume(currentVolumeData!!, true)
} else {
onVolumeAdded()
}
}
} }
} }
private fun onNewVolumeSelected(currentVolumeValue: String, isHidden: Boolean) { private fun onNewVolumeSelected(currentVolumeValue: String, isHidden: Boolean) {
val volumePath = getCurrentVolumePath() val volumePath = getCurrentVolumePath()
when (volumeAction!!) { if (volumeAction!! == Action.CREATE) {
Action.CREATE -> { val volumeFile = File(volumePath)
val volumeFile = File(volumePath) var goodDirectory = false
var goodDirectory = false if (volumeFile.isFile) {
if (volumeFile.isFile) { Toast.makeText(requireContext(), R.string.error_is_file, Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), R.string.error_is_file, Toast.LENGTH_SHORT).show() } else if (volumeFile.isDirectory) {
} else if (volumeFile.isDirectory) { val dirContent = volumeFile.list()
val dirContent = volumeFile.list() if (dirContent != null) {
if (dirContent != null) { if (dirContent.isEmpty()) {
if (dirContent.isEmpty()) { if (volumeFile.canWrite()) {
if (volumeFile.canWrite()) { goodDirectory = true
goodDirectory = true
} else {
errorDirectoryNotWritable(volumePath)
}
} else { } else {
Toast.makeText(requireContext(), R.string.dir_not_empty, Toast.LENGTH_SHORT).show() errorDirectoryNotWritable(volumePath)
} }
} else { } else {
Toast.makeText(requireContext(), R.string.listdir_null_error_msg, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.dir_not_empty, Toast.LENGTH_SHORT)
.show()
} }
} else { } else {
if (File(PathUtils.getParentPath(volumePath)).canWrite()) { Toast.makeText(
goodDirectory = true requireContext(),
} else { R.string.listdir_null_error_msg,
errorDirectoryNotWritable(volumePath) Toast.LENGTH_SHORT
} ).show()
} }
if (goodDirectory) { } else {
(activity as AddVolumeActivity).createVolume(volumePath, isHidden, binding.switchRemember.isChecked) if (File(PathUtils.getParentPath(volumePath)).canWrite()) {
goodDirectory = true
} else {
errorDirectoryNotWritable(volumePath)
} }
} }
Action.ADD -> { if (goodDirectory) {
val volumeType = EncryptedVolume.getVolumeType(volumePath) (activity as AddVolumeActivity).createVolume(
if (volumeType < 0) { volumePath,
CustomAlertDialogBuilder(requireContext(), theme) isHidden,
.setTitle(R.string.error) binding.switchRemember.isChecked
.setMessage(R.string.error_not_a_volume) )
.setPositiveButton(R.string.ok, null) }
.show() } else {
} else if (!File(volumePath).canWrite()) { val volumeType = EncryptedVolume.getVolumeType(volumePath)
val dialog = CustomAlertDialogBuilder(requireContext(), theme) if (volumeType < 0) {
.setTitle(R.string.warning) CustomAlertDialogBuilder(requireContext(), theme)
.setCancelable(false) .setTitle(R.string.error)
.setPositiveButton(R.string.ok) { _, _ -> addVolume(if (isHidden) currentVolumeValue else volumePath, isHidden, volumeType) } .setMessage(R.string.error_not_a_volume)
if (PathUtils.isPathOnExternalStorage(volumePath, requireContext())) { .setPositiveButton(R.string.ok, null)
dialog.setView( .show()
DialogSdcardErrorBinding.inflate(layoutInflater).apply { } else if (!File(volumePath).canWrite()) {
path.text = PathUtils.getPackageDataFolder(requireContext()) val dialog = CustomAlertDialogBuilder(requireContext(), theme)
footer.text = getString(R.string.sdcard_error_add_footer) .setTitle(R.string.warning)
}.root .setCancelable(false)
) .setPositiveButton(R.string.ok) { _, _ -> onExistingVolumeSelected(if (isHidden) currentVolumeValue else volumePath, isHidden, volumeType) }
} else { if (PathUtils.isPathOnExternalStorage(volumePath, requireContext())) {
dialog.setMessage(R.string.add_cant_write_warning) dialog.setView(
} DialogSdcardErrorBinding.inflate(layoutInflater).apply {
dialog.show() path.text = PathUtils.getPackageDataFolder(requireContext())
footer.text = getString(R.string.sdcard_error_add_footer)
}.root
)
} else { } else {
addVolume(if (isHidden) currentVolumeValue else volumePath, isHidden, volumeType) dialog.setMessage(R.string.add_cant_write_warning)
} }
dialog.show()
} else {
onExistingVolumeSelected(if (isHidden) currentVolumeValue else volumePath, isHidden, volumeType)
} }
} }
} }
@ -349,11 +393,17 @@ class SelectPathFragment: Fragment() {
dialog.show() dialog.show()
} }
private fun addVolume(volumeName: String, isHidden: Boolean, volumeType: Byte) { private fun onExistingVolumeSelected(volumeName: String, isHidden: Boolean, volumeType: Byte) {
val volumeData = VolumeData(VolumeData.newUuid(), volumeName, isHidden, volumeType) val volumeData = VolumeData(VolumeData.newUuid(), volumeName, isHidden, volumeType)
if (binding.switchRemember.isChecked) { if (binding.switchRemember.isChecked) {
volumeDatabase.saveVolume(volumeData) volumeDatabase.saveVolume(volumeData)
} }
(activity as AddVolumeActivity).onVolumeSelected(volumeData, binding.switchRemember.isChecked) with (activity as AddVolumeActivity) {
if (volumeAction!! == Action.OPEN) {
openVolume(volumeData, binding.switchRemember.isChecked)
} else {
onVolumeAdded()
}
}
} }
} }

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="?attr/buttonBackgroundColor"/>
<corners android:radius="50dp"/>
</shape>
</item>
</selector>

View File

@ -55,44 +55,24 @@
</RelativeLayout> </RelativeLayout>
<LinearLayout <TextView
android:id="@+id/text_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"
android:text="@string/volume_path_label"
android:layout_marginBottom="10dp"/>
<EditText
android:id="@+id/edit_volume_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"> android:hint="@string/volume_path_hint"
android:importantForAutofill="no"
<TextView android:inputType="textNoSuggestions"
android:id="@+id/text_label" android:maxLines="1"
android:layout_width="wrap_content" android:visibility="gone" />
android:layout_height="wrap_content"
android:text="@string/volume_path_label"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit_volume_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:inputType="text"
android:maxLines="1"
android:importantForAutofill="no"
android:hint="@string/volume_path_hint"/>
<ImageButton
android:id="@+id/button_pick_directory"
android:layout_width="@dimen/image_button_size"
android:layout_height="@dimen/image_button_size"
android:scaleType="fitCenter"
android:background="#00000000"
android:src="@drawable/icon_folder"
android:contentDescription="@string/pick_directory" />
</LinearLayout>
</LinearLayout>
<TextView <TextView
android:id="@+id/text_warning" android:id="@+id/text_warning"
@ -102,12 +82,40 @@
android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap" android:layout_marginHorizontal="@dimen/volume_operation_horizontal_gap"
android:visibility="gone"/> android:visibility="gone"/>
<Button
android:id="@+id/button_pick_directory"
android:layout_width="wrap_content"
style="@style/RoundButton"
android:drawableStart="@drawable/icon_folder_search"
android:text="@string/pick_directory"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"/>
<TextView
android:id="@+id/text_or"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/or"
android:layout_marginBottom="10dp"/>
<Button
android:id="@+id/button_enter_path"
android:layout_width="wrap_content"
style="@style/RoundButton"
android:drawableStart="@drawable/icon_edit"
android:text="@string/enter_volume_path"
android:layout_gravity="center_horizontal"/>
<androidx.appcompat.widget.SwitchCompat <androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_remember" android:id="@+id/switch_remember"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/remember_volume" android:text="@string/remember_volume"
android:checked="true" android:checked="true"
android:visibility="gone"
android:layout_marginTop="20dp"
android:layout_gravity="center"/> android:layout_gravity="center"/>
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
@ -117,6 +125,7 @@
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginHorizontal="@dimen/volume_operation_button_horizontal_margin" android:layout_marginHorizontal="@dimen/volume_operation_button_horizontal_margin"
android:layout_marginTop="@dimen/volume_operation_vertical_gap" android:layout_marginTop="@dimen/volume_operation_vertical_gap"
android:visibility="gone"
android:text="@string/create_volume" /> android:text="@string/create_volume" />
</LinearLayout> </LinearLayout>

View File

@ -32,8 +32,8 @@
<string name="storage_perm_denied_msg">إن DroidFS لا يمكنه العمل بدون صلاحبات التخزين.</string> <string name="storage_perm_denied_msg">إن DroidFS لا يمكنه العمل بدون صلاحبات التخزين.</string>
<string name="get_size_failed">لقد فشلت عملية استرداد حجم الملف.</string> <string name="get_size_failed">لقد فشلت عملية استرداد حجم الملف.</string>
<string name="parent_folder">المجلد الأصلي</string> <string name="parent_folder">المجلد الأصلي</string>
<string name="enter_volume_path">رجاءاً أدخل مسار المجلد المشفر</string> <string name="empty_volume_path">رجاءاً أدخل مسار المجلد المشفر</string>
<string name="enter_volume_name">رجاءاً أدخل اسم المجلد المشفر</string> <string name="empty_volume_name">رجاءاً أدخل اسم المجلد المشفر</string>
<string name="external_open">فتح بتطبيق خارجي</string> <string name="external_open">فتح بتطبيق خارجي</string>
<string name="single_delete_confirm">هل أنت متأكد من حذف %s ?</string> <string name="single_delete_confirm">هل أنت متأكد من حذف %s ?</string>
<string name="multiple_delete_confirm">هل أنت متأكد من حذف هذه %s العناصر ?</string> <string name="multiple_delete_confirm">هل أنت متأكد من حذف هذه %s العناصر ?</string>

View File

@ -33,8 +33,8 @@
<string name="storage_perm_denied_msg">DroidFS kann ohne Speicherberechtigung nicht funktionieren</string> <string name="storage_perm_denied_msg">DroidFS kann ohne Speicherberechtigung nicht funktionieren</string>
<string name="get_size_failed">Fehlgeschlagen beim Abrufen der Dateigröße.</string> <string name="get_size_failed">Fehlgeschlagen beim Abrufen der Dateigröße.</string>
<string name="parent_folder">Übergeordneter Ordner</string> <string name="parent_folder">Übergeordneter Ordner</string>
<string name="enter_volume_path">Bitte geben Sie den Volume-Pfad ein</string> <string name="empty_volume_path">Bitte geben Sie den Volume-Pfad ein</string>
<string name="enter_volume_name">Bitte geben Sie den Datenträgernamen ein</string> <string name="empty_volume_name">Bitte geben Sie den Datenträgernamen ein</string>
<string name="external_open">Öffnen mit externer Anwendung</string> <string name="external_open">Öffnen mit externer Anwendung</string>
<string name="single_delete_confirm">Sind Sie sicher, dass Sie %s löschen wollen?</string> <string name="single_delete_confirm">Sind Sie sicher, dass Sie %s löschen wollen?</string>
<string name="multiple_delete_confirm">Sind Sie sicher, dass Sie diese %s Elemente löschen wollen?</string> <string name="multiple_delete_confirm">Sind Sie sicher, dass Sie diese %s Elemente löschen wollen?</string>

View File

@ -33,8 +33,8 @@
<string name="storage_perm_denied_msg">DroidFS no puede funcionar sin permisos de almacenamiento.</string> <string name="storage_perm_denied_msg">DroidFS no puede funcionar sin permisos de almacenamiento.</string>
<string name="get_size_failed">No se ha podido recuperar el tamaño del archivo.</string> <string name="get_size_failed">No se ha podido recuperar el tamaño del archivo.</string>
<string name="parent_folder">Carpeta superior</string> <string name="parent_folder">Carpeta superior</string>
<string name="enter_volume_path">Por favor, introduce la ruta del volumen</string> <string name="empty_volume_path">Por favor, introduce la ruta del volumen</string>
<string name="enter_volume_name">Por favor, introduce el nombre del volumen</string> <string name="empty_volume_name">Por favor, introduce el nombre del volumen</string>
<string name="external_open">Abrir con una aplicación externa</string> <string name="external_open">Abrir con una aplicación externa</string>
<string name="single_delete_confirm">¿Estás seguro de que quieres borrar %s ?</string> <string name="single_delete_confirm">¿Estás seguro de que quieres borrar %s ?</string>
<string name="multiple_delete_confirm">¿Estás seguro de que quiere borrar %s objetos?</string> <string name="multiple_delete_confirm">¿Estás seguro de que quiere borrar %s objetos?</string>

View File

@ -32,8 +32,8 @@
<string name="storage_perm_denied_msg">DroidFS não pode funcionar sem permissão ao armazenamento.</string> <string name="storage_perm_denied_msg">DroidFS não pode funcionar sem permissão ao armazenamento.</string>
<string name="get_size_failed">Falha na recuperação do tamanho do arquivo.</string> <string name="get_size_failed">Falha na recuperação do tamanho do arquivo.</string>
<string name="parent_folder">Pasta principal</string> <string name="parent_folder">Pasta principal</string>
<string name="enter_volume_path">Por favor, digite a localização do volume</string> <string name="empty_volume_path">Por favor, digite a localização do volume</string>
<string name="enter_volume_name">Por favor, digite o nome do volume</string> <string name="empty_volume_name">Por favor, digite o nome do volume</string>
<string name="external_open">Abrir com app externo</string> <string name="external_open">Abrir com app externo</string>
<string name="single_delete_confirm">Você tem certeza que quer excluir %s?</string> <string name="single_delete_confirm">Você tem certeza que quer excluir %s?</string>
<string name="multiple_delete_confirm">Você realmente deseja excluir estos %s itens?</string> <string name="multiple_delete_confirm">Você realmente deseja excluir estos %s itens?</string>

View File

@ -31,8 +31,8 @@
<string name="storage_perm_denied_msg">DroidFS не может работать без разрешения на доступ к хранилищу.</string> <string name="storage_perm_denied_msg">DroidFS не может работать без разрешения на доступ к хранилищу.</string>
<string name="get_size_failed">Невозможно получить размер файла.</string> <string name="get_size_failed">Невозможно получить размер файла.</string>
<string name="parent_folder">Родительская папка</string> <string name="parent_folder">Родительская папка</string>
<string name="enter_volume_path">Введите путь тома</string> <string name="empty_volume_path">Введите путь тома</string>
<string name="enter_volume_name">Введите название тома</string> <string name="empty_volume_name">Введите название тома</string>
<string name="external_open">Открыть внешним приложением</string> <string name="external_open">Открыть внешним приложением</string>
<string name="single_delete_confirm">Удалить %s?</string> <string name="single_delete_confirm">Удалить %s?</string>
<string name="multiple_delete_confirm">Удалить %s элементов?</string> <string name="multiple_delete_confirm">Удалить %s элементов?</string>

View File

@ -33,8 +33,8 @@
<string name="storage_perm_denied_msg">DroidFS, depolama izinleri olmadan çalışamaz.</string> <string name="storage_perm_denied_msg">DroidFS, depolama izinleri olmadan çalışamaz.</string>
<string name="get_size_failed">Dosya boyutu alınamadı.</string> <string name="get_size_failed">Dosya boyutu alınamadı.</string>
<string name="parent_folder">Ana klasör</string> <string name="parent_folder">Ana klasör</string>
<string name="enter_volume_path">Lütfen birim yolunu girin</string> <string name="empty_volume_path">Lütfen birim yolunu girin</string>
<string name="enter_volume_name">Lütfen birim adını girin</string> <string name="empty_volume_name">Lütfen birim adını girin</string>
<string name="external_open">Harici uygulamayla aç</string> <string name="external_open">Harici uygulamayla aç</string>
<string name="single_delete_confirm">Silmek istediğimizden emin misiniz: %s</string> <string name="single_delete_confirm">Silmek istediğimizden emin misiniz: %s</string>
<string name="multiple_delete_confirm">Bunları silmek istediğinizden emin misiniz: %s</string> <string name="multiple_delete_confirm">Bunları silmek istediğinizden emin misiniz: %s</string>

View File

@ -34,8 +34,8 @@
<string name="storage_perm_denied_msg">没有存储权限时DroidFS无法工作</string> <string name="storage_perm_denied_msg">没有存储权限时DroidFS无法工作</string>
<string name="get_size_failed">无法获取文件的大小</string> <string name="get_size_failed">无法获取文件的大小</string>
<string name="parent_folder">上一级文件夹</string> <string name="parent_folder">上一级文件夹</string>
<string name="enter_volume_path">输入卷的路径</string> <string name="empty_volume_path">输入卷的路径</string>
<string name="enter_volume_name">输入卷的名称</string> <string name="empty_volume_name">输入卷的名称</string>
<string name="external_open">使用外部软件打开</string> <string name="external_open">使用外部软件打开</string>
<string name="single_delete_confirm">确认删除%s?</string> <string name="single_delete_confirm">确认删除%s?</string>
<string name="multiple_delete_confirm">确认删除多个文件:%s</string> <string name="multiple_delete_confirm">确认删除多个文件:%s</string>

View File

@ -33,8 +33,8 @@
<string name="storage_perm_denied_msg">DroidFS can\'t work without storage permissions.</string> <string name="storage_perm_denied_msg">DroidFS can\'t work without storage permissions.</string>
<string name="get_size_failed">Failed to retrieve file size.</string> <string name="get_size_failed">Failed to retrieve file size.</string>
<string name="parent_folder">Parent Folder</string> <string name="parent_folder">Parent Folder</string>
<string name="enter_volume_path">Please enter the volume path</string> <string name="empty_volume_path">Please enter the volume path</string>
<string name="enter_volume_name">Please enter the volume name</string> <string name="empty_volume_name">Please enter the volume name</string>
<string name="external_open">Open with external app</string> <string name="external_open">Open with external app</string>
<string name="single_delete_confirm">Are you sure you want to delete %s ?</string> <string name="single_delete_confirm">Are you sure you want to delete %s ?</string>
<string name="multiple_delete_confirm">Are you sure you want to delete these %s items ?</string> <string name="multiple_delete_confirm">Are you sure you want to delete these %s items ?</string>
@ -174,7 +174,7 @@
<string name="seek_seconds_forward">+%d seconds</string> <string name="seek_seconds_forward">+%d seconds</string>
<string name="seek_seconds_backward">-%d seconds</string> <string name="seek_seconds_backward">-%d seconds</string>
<string name="add_volume">Add volume</string> <string name="add_volume">Add volume</string>
<string name="pick_directory">Pick directory</string> <string name="pick_directory">Pick a directory</string>
<string name="volume_alread_saved">Volume already saved</string> <string name="volume_alread_saved">Volume already saved</string>
<string name="open_dialog_title">Opening %s:</string> <string name="open_dialog_title">Opening %s:</string>
<string name="remove">Remove</string> <string name="remove">Remove</string>
@ -289,4 +289,6 @@
<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="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> <string name="cryfs_details">Slower, but protects metadata and prevents replacement attacks</string>
<string name="or">or</string>
<string name="enter_volume_path">Enter volume path</string>
</resources> </resources>

View File

@ -8,7 +8,7 @@
<item name="android:statusBarColor">@color/primary</item> <item name="android:statusBarColor">@color/primary</item>
<item name="infoBarBackgroundColor">#181818</item> <item name="infoBarBackgroundColor">#181818</item>
<item name="buttonBackgroundColor">#5B5A5C</item> <item name="buttonBackgroundColor">#5B5A5C</item>
<item name="buttonStyle">@style/DarkButton</item> <item name="buttonStyle">@style/Button</item>
</style> </style>
<style name="DarkRed" parent="BaseTheme"> <style name="DarkRed" parent="BaseTheme">
@ -35,7 +35,6 @@
<item name="android:navigationBarColor">@color/black</item> <item name="android:navigationBarColor">@color/black</item>
<item name="infoBarBackgroundColor">@color/black</item> <item name="infoBarBackgroundColor">@color/black</item>
<item name="buttonBackgroundColor">#3B3A3C</item> <item name="buttonBackgroundColor">#3B3A3C</item>
<item name="buttonStyle">@style/BlackButton</item>
</style> </style>
<style name="BlackRed" parent="BlackGreen"> <style name="BlackRed" parent="BlackGreen">
<item name="colorAccent">@color/red</item> <item name="colorAccent">@color/red</item>
@ -56,11 +55,17 @@
<item name="colorAccent">@color/pink</item> <item name="colorAccent">@color/pink</item>
</style> </style>
<style name="DarkButton" parent="Widget.AppCompat.Button"> <style name="Button" parent="Widget.AppCompat.Button">
<item name="android:background">@drawable/button_background</item> <item name="android:background">@drawable/button_background</item>
</style> </style>
<style name="BlackButton" parent="Widget.AppCompat.Button">
<item name="android:background">@drawable/button_background</item> <style name="RoundButton" parent="Widget.AppCompat.Button">
<item name="android:background">@drawable/round_button_background</item>
<item name="textAllCaps">false</item>
<item name="android:layout_height">35sp</item>
<item name="android:paddingStart">15dp</item>
<item name="android:paddingEnd">15dp</item>
<item name="android:drawablePadding">5dp</item>
</style> </style>
<style name="infoBarTextView"> <style name="infoBarTextView">