Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.cryptomator.presentation.model

enum class VaultListSortOption {
NAME,
LOCATION
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import org.cryptomator.presentation.intent.UnlockVaultIntent
import org.cryptomator.presentation.model.CloudModel
import org.cryptomator.presentation.model.CloudTypeModel
import org.cryptomator.presentation.model.ProgressModel
import org.cryptomator.presentation.model.VaultListSortOption
import org.cryptomator.presentation.model.VaultModel
import org.cryptomator.presentation.model.mappers.CloudFolderModelMapper
import org.cryptomator.presentation.ui.activity.LicenseCheckActivity
Expand All @@ -66,6 +67,7 @@ import org.cryptomator.presentation.workflow.PermissionsResult
import org.cryptomator.presentation.workflow.Workflow
import org.cryptomator.util.SharedPreferencesHandler
import org.cryptomator.util.crypto.CryptoMode
import java.util.Locale
import javax.inject.Inject
import timber.log.Timber

Expand Down Expand Up @@ -97,6 +99,7 @@ class VaultListPresenter @Inject constructor( //
) : Presenter<VaultListView>(exceptionMappings) {

private var vaultAction: VaultAction? = null
private var cachedVaults: List<Vault> = emptyList()

override fun workflows(): Iterable<Workflow<*>> {
return listOf(addExistingVaultWorkflow, createNewVaultWorkflow)
Expand Down Expand Up @@ -391,6 +394,7 @@ class VaultListPresenter @Inject constructor( //
get() {
getVaultListUseCase.run(object : DefaultResultHandler<List<Vault>>() {
override fun onSuccess(vaults: List<Vault>) {
cachedVaults = vaults
val vaultModels = vaults.mapTo(ArrayList()) { VaultModel(it) }
if (vaultModels.isEmpty()) {
view?.showVaultCreationHint()
Expand Down Expand Up @@ -601,6 +605,7 @@ class VaultListPresenter @Inject constructor( //
.andToPosition(toPosition) //
.run(object : DefaultResultHandler<List<Vault>>() {
override fun onSuccess(vaults: List<Vault>) {
cachedVaults = vaults
view?.vaultMoved(vaults.mapTo(ArrayList()) { VaultModel(it) })
}

Expand All @@ -610,6 +615,51 @@ class VaultListPresenter @Inject constructor( //
})
}

fun onSortOverrideConfirmed(sortOption: VaultListSortOption) {
if (cachedVaults.isEmpty()) {
loadVaultList()
return
}

val sortedVaults = when (sortOption) {
VaultListSortOption.NAME -> cachedVaults.sortedWith(nameComparator())
VaultListSortOption.LOCATION -> cachedVaults.sortedWith(locationComparator())
}

if (sortedVaults.map { it.id } == cachedVaults.map { it.id }) {
return
}

val reindexedVaults = sortedVaults.mapIndexed { index, vault ->
Vault.aCopyOf(vault).withPosition(index).build()
}

saveVaultsUseCase //
.withVaults(reindexedVaults) //
.run(object : DefaultResultHandler<List<Vault>>() {
override fun onSuccess(vaults: List<Vault>) {
cachedVaults = vaults
val vaultModels = vaults.mapTo(ArrayList()) { VaultModel(it) }
view?.vaultMoved(vaultModels)
}

override fun onError(e: Throwable) {
showError(e)
}
})
}

private fun nameComparator(): Comparator<Vault> {
return compareBy<Vault> { it.name.lowercase(Locale.getDefault()) }
.thenBy { it.name }
}

private fun locationComparator(): Comparator<Vault> {
return compareBy<Vault> { it.cloudType.ordinal }
.thenBy { it.name.lowercase(Locale.getDefault()) }
.thenBy { it.name }
}

private enum class VaultAction {
UNLOCK, RENAME
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.cryptomator.presentation.intent.Intents.settingsIntent
import org.cryptomator.presentation.intent.VaultListIntent
import org.cryptomator.presentation.model.CloudFolderModel
import org.cryptomator.presentation.model.ProgressModel
import org.cryptomator.presentation.model.VaultListSortOption
import org.cryptomator.presentation.model.VaultModel
import org.cryptomator.presentation.presenter.VaultListPresenter
import org.cryptomator.presentation.service.OpenWritableFileNotification
Expand All @@ -27,6 +28,7 @@ import org.cryptomator.presentation.ui.callback.VaultListCallback
import org.cryptomator.presentation.ui.dialog.AskForLockScreenDialog
import org.cryptomator.presentation.ui.dialog.BetaConfirmationDialog
import org.cryptomator.presentation.ui.dialog.CBCPasswordVaultsMigrationDialog
import org.cryptomator.presentation.ui.dialog.SortOverrideConfirmationDialog
import org.cryptomator.presentation.ui.dialog.UpdateAppAvailableDialog
import org.cryptomator.presentation.ui.dialog.UpdateAppDialog
import org.cryptomator.presentation.ui.dialog.VaultDeleteConfirmationDialog
Expand All @@ -45,7 +47,8 @@ class VaultListActivity : BaseActivity<ActivityLayoutObscureAwareBinding>(Activi
UpdateAppDialog.Callback, //
BetaConfirmationDialog.Callback, //
CBCPasswordVaultsMigrationDialog.Callback, //
BiometricAuthenticationMigration.Callback {
BiometricAuthenticationMigration.Callback, //
SortOverrideConfirmationDialog.Callback {

@Inject
lateinit var vaultListPresenter: VaultListPresenter
Expand Down Expand Up @@ -98,13 +101,21 @@ class VaultListActivity : BaseActivity<ActivityLayoutObscureAwareBinding>(Activi
override fun getCustomMenuResource(): Int = R.menu.menu_vault_list

override fun onMenuItemSelected(itemId: Int): Boolean = when (itemId) {
R.id.action_sort_by -> {
showDialog(SortOverrideConfirmationDialog.newInstance())
true
}
R.id.action_settings -> {
vaultListPresenter.startIntent(settingsIntent())
true
}
else -> super.onMenuItemSelected(itemId)
}

override fun onSortOverrideConfirmed(sortOption: VaultListSortOption) {
vaultListPresenter.onSortOverrideConfirmed(sortOption)
}

override fun isVaultLocked(vaultModel: VaultModel): Boolean {
return vaultListFragment().isVaultLocked(vaultModel)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.cryptomator.presentation.ui.dialog

import android.content.DialogInterface
import androidx.appcompat.app.AlertDialog
import org.cryptomator.generator.Dialog
import org.cryptomator.presentation.R
import org.cryptomator.presentation.databinding.DialogSortOverrideConfirmationBinding
import org.cryptomator.presentation.model.VaultListSortOption

@Dialog
class SortOverrideConfirmationDialog :
BaseDialog<SortOverrideConfirmationDialog.Callback, DialogSortOverrideConfirmationBinding>(DialogSortOverrideConfirmationBinding::inflate) {

interface Callback {

fun onSortOverrideConfirmed(sortOption: VaultListSortOption)
}

public override fun setupDialog(builder: AlertDialog.Builder): android.app.Dialog {
return builder //
.setPositiveButton(getString(R.string.dialog_sort_override_positive_button)) { _: DialogInterface, _: Int -> callback?.onSortOverrideConfirmed(selectedSortOption()) } //
.setNegativeButton(getString(R.string.dialog_sort_override_negative_button)) { _: DialogInterface, _: Int -> } //
.create()
}

public override fun setupView() {
binding.tvMessage.text = getString(R.string.dialog_sort_override_message)
binding.rbSortByName.isChecked = true
}

private fun selectedSortOption(): VaultListSortOption {
return when (binding.rgSortOptions.checkedRadioButtonId) {
R.id.rb_sort_by_location -> VaultListSortOption.LOCATION
else -> VaultListSortOption.NAME
}
}

companion object {

fun newInstance(): SortOverrideConfirmationDialog = SortOverrideConfirmationDialog()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin">

<TextView
android:id="@+id/tv_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp" />

<RadioGroup
android:id="@+id/rg_sort_options"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:orientation="vertical">

<RadioButton
android:id="@+id/rb_sort_by_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dialog_sort_override_option_name" />

<RadioButton
android:id="@+id/rb_sort_by_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dialog_sort_override_option_location" />
</RadioGroup>

</LinearLayout>
</androidx.core.widget.NestedScrollView>
5 changes: 5 additions & 0 deletions presentation/src/main/res/menu/menu_vault_list.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_sort_by"
android:orderInCategory="90"
android:title="@string/menu_vault_list_sort_by"
app:showAsAction="never" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
Expand Down
6 changes: 6 additions & 0 deletions presentation/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
<string name="snack_bar_action_title_search_next">Next</string>

<string name="snack_bar_action_title_sort">Sort</string>
<string name="menu_vault_list_sort_by">Sort by...</string>
<string name="snack_bar_action_title_sort_az">A - Z</string>
<string name="snack_bar_action_title_sort_za">Z - A</string>
<string name="snack_bar_action_title_sort_newest">Newest first</string>
Expand Down Expand Up @@ -334,6 +335,11 @@

<!-- # dialogs -->
<string name="dialog_button_cancel">Cancel</string>
<string name="dialog_sort_override_message">This will override any existing sorting. Are you sure?</string>
<string name="dialog_sort_override_negative_button">Close</string>
<string name="dialog_sort_override_positive_button">Proceed</string>
<string name="dialog_sort_override_option_name">Sort by vault name</string>
<string name="dialog_sort_override_option_location">Sort by vault location</string>

<string name="dialog_create_folder_title" translatable="false">@string/screen_file_browser_action_create_folder</string>
<string name="dialog_create_folder_positive_button" translatable="false">@string/screen_enter_vault_name_button_text</string>
Expand Down