#168 implement backend for sorting vault list.
This commit is contained in:
parent
dc7cef7fb5
commit
00174fdf5a
@ -74,7 +74,7 @@ android {
|
||||
}
|
||||
|
||||
greendao {
|
||||
schemaVersion 3
|
||||
schemaVersion 4
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
@ -21,12 +21,14 @@ class DatabaseUpgrades {
|
||||
public DatabaseUpgrades( //
|
||||
Upgrade0To1 upgrade0To1, //
|
||||
Upgrade1To2 upgrade1To2, //
|
||||
Upgrade2To3 upgrade2To3) {
|
||||
Upgrade2To3 upgrade2To3, //
|
||||
Upgrade3To4 upgrade3To4) {
|
||||
|
||||
availableUpgrades = defineUpgrades( //
|
||||
upgrade0To1, //
|
||||
upgrade1To2, //
|
||||
upgrade2To3);
|
||||
upgrade2To3, //
|
||||
upgrade3To4);
|
||||
}
|
||||
|
||||
private Map<Integer, List<DatabaseUpgrade>> defineUpgrades(DatabaseUpgrade... upgrades) {
|
||||
|
66
data/src/main/java/org/cryptomator/data/db/Upgrade3To4.kt
Normal file
66
data/src/main/java/org/cryptomator/data/db/Upgrade3To4.kt
Normal file
@ -0,0 +1,66 @@
|
||||
package org.cryptomator.data.db
|
||||
|
||||
import org.cryptomator.data.db.Sql.SqlCreateTableBuilder.ForeignKeyBehaviour
|
||||
import org.cryptomator.data.db.entities.CloudEntityDao
|
||||
import org.cryptomator.data.db.entities.VaultEntityDao
|
||||
import org.greenrobot.greendao.database.Database
|
||||
import org.greenrobot.greendao.internal.DaoConfig
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class Upgrade3To4 @Inject constructor() : DatabaseUpgrade(3, 4) {
|
||||
|
||||
override fun internalApplyTo(db: Database, origin: Int) {
|
||||
db.beginTransaction()
|
||||
try {
|
||||
upgradeDatabaseScheme(db)
|
||||
updateVaultPositions(db)
|
||||
db.setTransactionSuccessful()
|
||||
} finally {
|
||||
db.endTransaction()
|
||||
}
|
||||
}
|
||||
|
||||
private fun upgradeDatabaseScheme(db: Database) {
|
||||
Sql.alterTable("VAULT_ENTITY").renameTo("VAULT_ENTITY_OLD").executeOn(db)
|
||||
Sql.createTable("VAULT_ENTITY") //
|
||||
.id() //
|
||||
.optionalInt("FOLDER_CLOUD_ID") //
|
||||
.optionalText("FOLDER_PATH") //
|
||||
.optionalText("FOLDER_NAME") //
|
||||
.requiredText("CLOUD_TYPE") //
|
||||
.optionalText("PASSWORD") //
|
||||
.optionalInt("POSITION") //
|
||||
.foreignKey("FOLDER_CLOUD_ID", "CLOUD_ENTITY", ForeignKeyBehaviour.ON_DELETE_SET_NULL) //
|
||||
.executeOn(db)
|
||||
|
||||
Sql.insertInto("VAULT_ENTITY") //
|
||||
.select("_id", "FOLDER_CLOUD_ID", "FOLDER_PATH", "FOLDER_NAME", "CLOUD_ENTITY.TYPE") //
|
||||
.columns("_id", "FOLDER_CLOUD_ID", "FOLDER_PATH", "FOLDER_NAME", "CLOUD_TYPE") //
|
||||
.from("VAULT_ENTITY_OLD") //
|
||||
.join("CLOUD_ENTITY", "VAULT_ENTITY_OLD.FOLDER_CLOUD_ID") //
|
||||
.executeOn(db)
|
||||
|
||||
Sql.dropIndex("IDX_VAULT_ENTITY_FOLDER_PATH_FOLDER_CLOUD_ID").executeOn(db)
|
||||
|
||||
Sql.createUniqueIndex("IDX_VAULT_ENTITY_FOLDER_PATH_FOLDER_CLOUD_ID") //
|
||||
.on("VAULT_ENTITY") //
|
||||
.asc("FOLDER_PATH") //
|
||||
.asc("FOLDER_CLOUD_ID") //
|
||||
.executeOn(db)
|
||||
|
||||
Sql.dropTable("VAULT_ENTITY_OLD").executeOn(db)
|
||||
}
|
||||
|
||||
private fun updateVaultPositions(db: Database) {
|
||||
CloudEntityDao(DaoConfig(db, VaultEntityDao::class.java)) //
|
||||
.loadAll() //
|
||||
.map {
|
||||
Sql.update("CLOUD_ENTITY") //
|
||||
.where("_id", Sql.eq(it.id)) //
|
||||
.set("POSITION", Sql.toInteger(it.id)) //
|
||||
.executeOn(db)
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,8 @@ public class VaultEntity extends DatabaseEntity {
|
||||
|
||||
private String password;
|
||||
|
||||
private Integer position;
|
||||
|
||||
/**
|
||||
* Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
|
||||
* Entity must attached to an entity context.
|
||||
@ -152,6 +154,14 @@ public class VaultEntity extends DatabaseEntity {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public Integer getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
public void setPosition(Integer position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/** called by internal mechanisms, do not call yourself. */
|
||||
@Generated(hash = 674742652)
|
||||
public void __setDaoSession(DaoSession daoSession) {
|
||||
@ -159,14 +169,16 @@ public class VaultEntity extends DatabaseEntity {
|
||||
myDao = daoSession != null ? daoSession.getVaultEntityDao() : null;
|
||||
}
|
||||
|
||||
@Generated(hash = 1196809909)
|
||||
public VaultEntity(Long id, Long folderCloudId, String folderPath, String folderName, @NotNull String cloudType, String password) {
|
||||
@Generated(hash = 825602374)
|
||||
public VaultEntity(Long id, Long folderCloudId, String folderPath, String folderName, @NotNull String cloudType, String password,
|
||||
Integer position) {
|
||||
this.id = id;
|
||||
this.folderCloudId = folderCloudId;
|
||||
this.folderPath = folderPath;
|
||||
this.folderName = folderName;
|
||||
this.cloudType = cloudType;
|
||||
this.password = password;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Generated(hash = 691253864)
|
||||
|
@ -30,6 +30,7 @@ public class VaultEntityMapper extends EntityMapper<VaultEntity, Vault> {
|
||||
.withCloud(cloudFrom(entity)) //
|
||||
.withCloudType(CloudType.valueOf(entity.getCloudType())) //
|
||||
.withSavedPassword(entity.getPassword()) //
|
||||
.withPosition(entity.getPosition()) //
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -51,6 +52,7 @@ public class VaultEntityMapper extends EntityMapper<VaultEntity, Vault> {
|
||||
}
|
||||
entity.setCloudType(domainObject.getCloudType().name());
|
||||
entity.setPassword(domainObject.getPassword());
|
||||
entity.setPosition(domainObject.getPosition());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ public class Vault implements Serializable {
|
||||
.withPath(vault.getPath()) //
|
||||
.withUnlocked(vault.isUnlocked()) //
|
||||
.withSavedPassword(vault.getPassword()) //
|
||||
.withVersion(vault.getVersion());
|
||||
.withVersion(vault.getVersion())
|
||||
.withPosition(vault.getPosition());
|
||||
}
|
||||
|
||||
private final Long id;
|
||||
@ -30,6 +31,7 @@ public class Vault implements Serializable {
|
||||
private final boolean unlocked;
|
||||
private final String password;
|
||||
private final int version;
|
||||
private final int position;
|
||||
|
||||
private Vault(Builder builder) {
|
||||
this.id = builder.id;
|
||||
@ -40,6 +42,7 @@ public class Vault implements Serializable {
|
||||
this.cloudType = builder.cloudType;
|
||||
this.password = builder.password;
|
||||
this.version = builder.version;
|
||||
this.position = builder.position;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
@ -74,6 +77,10 @@ public class Vault implements Serializable {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private Long id = NOT_SET;
|
||||
@ -84,6 +91,7 @@ public class Vault implements Serializable {
|
||||
private boolean unlocked;
|
||||
private String password;
|
||||
private int version = -1;
|
||||
private int position = -1;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
@ -154,6 +162,11 @@ public class Vault implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withPosition(int position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vault build() {
|
||||
validate();
|
||||
return new Vault(this);
|
||||
@ -172,6 +185,9 @@ public class Vault implements Serializable {
|
||||
if (cloudType == null) {
|
||||
throw new IllegalStateException("cloudtype must be set");
|
||||
}
|
||||
if (position == -1) {
|
||||
throw new IllegalStateException("position must be set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
package org.cryptomator.domain.usecases.vault;
|
||||
|
||||
import org.cryptomator.domain.Vault;
|
||||
import org.cryptomator.domain.exception.BackendException;
|
||||
import org.cryptomator.domain.repository.VaultRepository;
|
||||
import org.cryptomator.generator.Parameter;
|
||||
import org.cryptomator.generator.UseCase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@UseCase
|
||||
class MoveVault {
|
||||
|
||||
private final VaultRepository vaultRepository;
|
||||
private final int from;
|
||||
private final int to;
|
||||
|
||||
public MoveVault(VaultRepository vaultRepository, @Parameter Integer from, @Parameter Integer to) {
|
||||
this.vaultRepository = vaultRepository;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public List<Vault> execute() throws BackendException {
|
||||
List<Vault> vaults = MoveVaultHelper.Companion.updateVaultPosition(from, to, vaultRepository);
|
||||
return MoveVaultHelper.Companion.updateVaultsInDatabase(vaults, vaultRepository);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package org.cryptomator.domain.usecases.vault;
|
||||
|
||||
import org.cryptomator.domain.Vault
|
||||
import org.cryptomator.domain.repository.VaultRepository
|
||||
import java.util.*
|
||||
|
||||
class MoveVaultHelper {
|
||||
|
||||
companion object {
|
||||
fun updateVaultPosition(from: Int, to: Int, vaultRepository: VaultRepository): List<Vault> {
|
||||
val vaults = vaultRepository.vaults()
|
||||
|
||||
vaults.sortWith(VaultComparator())
|
||||
|
||||
if (from < to) {
|
||||
for (i in from until to) {
|
||||
Collections.swap(vaults, i, i + 1)
|
||||
}
|
||||
} else {
|
||||
for (i in from downTo to + 1) {
|
||||
Collections.swap(vaults, i, i - 1)
|
||||
}
|
||||
}
|
||||
|
||||
for (i in 0 until vaults.size) {
|
||||
vaults[i] = Vault.aCopyOf(vaults[i]).withPosition(i + 1).build()
|
||||
}
|
||||
|
||||
vaults.forEach { vault -> vaultRepository.store(vault) }
|
||||
|
||||
return vaults
|
||||
}
|
||||
|
||||
fun updateVaultsInDatabase(vaults: List<Vault>, vaultRepository: VaultRepository): List<Vault> {
|
||||
vaults.forEach { vault -> vaultRepository.store(vault) }
|
||||
return vaultRepository.vaults()
|
||||
}
|
||||
}
|
||||
|
||||
internal class VaultComparator : Comparator<Vault> {
|
||||
override fun compare(o1: Vault, o2: Vault): Int {
|
||||
return o1.position - o2.position
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,8 @@ class VaultModel(private val vault: Vault) : Serializable {
|
||||
get() = vault.path
|
||||
val isLocked: Boolean
|
||||
get() = !vault.isUnlocked
|
||||
val position: Int
|
||||
get() = vault.position
|
||||
|
||||
fun toVault(): Vault {
|
||||
return vault
|
||||
|
@ -53,6 +53,7 @@ class VaultListPresenter @Inject constructor( //
|
||||
private val addExistingVaultWorkflow: AddExistingVaultWorkflow, //
|
||||
private val createNewVaultWorkflow: CreateNewVaultWorkflow, //
|
||||
private val saveVaultUseCase: SaveVaultUseCase, //
|
||||
private val moveVaultUseCase: MoveVaultUseCase, //
|
||||
private val changePasswordUseCase: ChangePasswordUseCase, //
|
||||
private val removeStoredVaultPasswordsUseCase: RemoveStoredVaultPasswordsUseCase, //
|
||||
private val licenseCheckUseCase: DoLicenseCheckUseCase, //
|
||||
@ -603,17 +604,33 @@ class VaultListPresenter @Inject constructor( //
|
||||
view?.showDialog(AppIsObscuredInfoDialog.newInstance())
|
||||
}
|
||||
|
||||
fun onVaultMoved(fromPosition: Int, toPosition: Int) {
|
||||
// FIXME insert position int into the db and update here
|
||||
|
||||
fun onRowMoved(fromPosition: Int, toPosition: Int) {
|
||||
getVaultListUseCase.run(object : DefaultResultHandler<List<Vault>>() {
|
||||
override fun onSuccess(vaults: List<Vault>) {
|
||||
val vaultModels = vaults.mapTo(ArrayList()) { VaultModel(it) }
|
||||
view?.vaultMoved(fromPosition, toPosition, vaultModels)
|
||||
view?.rowMoved(fromPosition, toPosition)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Timber.tag("VaultListPresenter").e(e, "Failed to query vault list while row moving")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun onVaultMoved(fromPosition: Int, toPosition: Int) {
|
||||
moveVaultUseCase
|
||||
.withFrom(fromPosition) //
|
||||
.andTo(toPosition) //
|
||||
.run(object : DefaultResultHandler<List<Vault>>() {
|
||||
override fun onSuccess(vaults: List<Vault>) {
|
||||
view?.vaultMoved(vaults.mapTo(ArrayList()) { VaultModel(it) })
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Timber.tag("VaultListPresenter").e(e, "Failed to execute MoveVaultUseCase")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun onBiometricAuthenticationSucceeded(vaultModel: VaultModel) {
|
||||
if (changedVaultPassword) {
|
||||
changedVaultPassword = false
|
||||
|
@ -150,8 +150,12 @@ class VaultListActivity : BaseActivity(), //
|
||||
return biometricAuthentication?.stoppedBiometricAuthDuringCloudAuthentication() == true
|
||||
}
|
||||
|
||||
override fun vaultMoved(fromPosition: Int, toPosition: Int, vaultModelCollection: List<VaultModel>) {
|
||||
vaultListFragment().vaultMoved(fromPosition, toPosition, vaultModelCollection)
|
||||
override fun vaultMoved(vaults: List<VaultModel>) {
|
||||
vaultListFragment().vaultMoved(vaults)
|
||||
}
|
||||
|
||||
override fun rowMoved(fromPosition: Int, toPosition: Int) {
|
||||
vaultListFragment().rowMoved(fromPosition, toPosition)
|
||||
}
|
||||
|
||||
override fun showVaultSettingsDialog(vaultModel: VaultModel) {
|
||||
|
@ -23,5 +23,7 @@ interface VaultListView : View {
|
||||
fun isVaultLocked(vaultModel: VaultModel): Boolean
|
||||
fun cancelBasicAuthIfRunning()
|
||||
fun stoppedBiometricAuthDuringCloudAuthentication(): Boolean
|
||||
fun vaultMoved(fromPosition: Int, toPosition: Int, vaultModelCollection: List<VaultModel>)
|
||||
fun vaultMoved(vaults: List<VaultModel>)
|
||||
fun rowMoved(fromPosition: Int, toPosition: Int)
|
||||
|
||||
}
|
||||
|
@ -8,8 +8,7 @@ import org.cryptomator.presentation.ui.adapter.VaultsAdapter.VaultViewHolder
|
||||
import javax.inject.Inject
|
||||
|
||||
class VaultsAdapter @Inject
|
||||
internal constructor() : RecyclerViewBaseAdapter<VaultModel, VaultsAdapter.OnItemInteractionListener, VaultViewHolder>(), VaultsMoveListener.Listener {
|
||||
|
||||
internal constructor() : RecyclerViewBaseAdapter<VaultModel, VaultsAdapter.OnItemInteractionListener, VaultViewHolder>(VaultModelComparator()), VaultsMoveListener.Listener {
|
||||
interface OnItemInteractionListener {
|
||||
fun onVaultClicked(vaultModel: VaultModel)
|
||||
|
||||
@ -17,6 +16,8 @@ internal constructor() : RecyclerViewBaseAdapter<VaultModel, VaultsAdapter.OnIte
|
||||
|
||||
fun onVaultLockClicked(vaultModel: VaultModel)
|
||||
|
||||
fun onRowMoved(fromPosition: Int, toPosition: Int)
|
||||
|
||||
fun onVaultMoved(fromPosition: Int, toPosition: Int)
|
||||
}
|
||||
|
||||
@ -68,7 +69,17 @@ internal constructor() : RecyclerViewBaseAdapter<VaultModel, VaultsAdapter.OnIte
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRowMoved(fromPosition: Int, toPosition: Int) {
|
||||
override fun onVaultMoved(fromPosition: Int, toPosition: Int) {
|
||||
callback.onVaultMoved(fromPosition, toPosition)
|
||||
}
|
||||
|
||||
override fun onRowMoved(fromPosition: Int, toPosition: Int) {
|
||||
callback.onRowMoved(fromPosition, toPosition)
|
||||
}
|
||||
|
||||
internal class VaultModelComparator : java.util.Comparator<VaultModel> {
|
||||
override fun compare(o1: VaultModel, o2: VaultModel): Int {
|
||||
return o1.position - o2.position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class VaultsMoveListener(val adapter: VaultsAdapter) : ItemTouchHelper.Callback() {
|
||||
|
||||
var dragFrom = -1
|
||||
var dragTo = -1
|
||||
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
|
||||
return makeMovementFlags(dragFlags, 0)
|
||||
@ -19,14 +22,36 @@ class VaultsMoveListener(val adapter: VaultsAdapter) : ItemTouchHelper.Callback(
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
val fromPosition = viewHolder.adapterPosition
|
||||
val toPosition = target.adapterPosition
|
||||
|
||||
if (dragFrom == -1) {
|
||||
dragFrom = fromPosition;
|
||||
}
|
||||
|
||||
dragTo = toPosition;
|
||||
|
||||
adapter.onRowMoved(viewHolder.adapterPosition, target.adapterPosition)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
|
||||
if (dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) {
|
||||
adapter.onVaultMoved(dragFrom, dragTo)
|
||||
}
|
||||
|
||||
dragTo = -1
|
||||
dragFrom = -1
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onRowMoved(fromPosition: Int, toPosition: Int)
|
||||
fun onVaultMoved(fromPosition: Int, toPosition: Int)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ import org.cryptomator.presentation.model.VaultModel
|
||||
import org.cryptomator.presentation.presenter.VaultListPresenter
|
||||
import org.cryptomator.presentation.ui.adapter.VaultsAdapter
|
||||
import org.cryptomator.presentation.ui.adapter.VaultsMoveListener
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@Fragment(R.layout.fragment_vault_list)
|
||||
@ -40,6 +39,10 @@ class VaultListFragment : BaseFragment() {
|
||||
vaultListPresenter.onVaultLockClicked(vaultModel)
|
||||
}
|
||||
|
||||
override fun onRowMoved(fromPosition: Int, toPosition: Int) {
|
||||
vaultListPresenter.onRowMoved(fromPosition, toPosition)
|
||||
}
|
||||
|
||||
override fun onVaultMoved(fromPosition: Int, toPosition: Int) {
|
||||
vaultListPresenter.onVaultMoved(fromPosition, toPosition)
|
||||
}
|
||||
@ -96,17 +99,12 @@ class VaultListFragment : BaseFragment() {
|
||||
vaultsAdapter.addOrUpdateVault(vaultModel)
|
||||
}
|
||||
|
||||
fun vaultMoved(fromPosition: Int, toPosition: Int, vaultModelCollection: List<VaultModel>?) {
|
||||
if (fromPosition < toPosition) {
|
||||
for (i in fromPosition until toPosition) {
|
||||
Collections.swap(vaultModelCollection, i, i + 1)
|
||||
}
|
||||
} else {
|
||||
for (i in fromPosition downTo toPosition + 1) {
|
||||
Collections.swap(vaultModelCollection, i, i - 1)
|
||||
}
|
||||
}
|
||||
fun vaultMoved(vaults: List<VaultModel>) {
|
||||
vaultsAdapter.clear()
|
||||
vaultsAdapter.addAll(vaults)
|
||||
}
|
||||
|
||||
fun rowMoved(fromPosition: Int, toPosition: Int) {
|
||||
vaultsAdapter.notifyItemMoved(fromPosition, toPosition)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user