From 38909fde268119d22fcc7021ef733e5bd2776168 Mon Sep 17 00:00:00 2001 From: Stefan Niedermann Date: Sat, 24 Apr 2021 12:13:27 +0200 Subject: Improve handling of account deletions --- .../owncloud/notes/main/MainActivity.java | 13 ++-- .../owncloud/notes/main/MainViewModel.java | 16 ++--- .../notes/manageaccounts/ManageAccountAdapter.java | 17 +----- .../manageaccounts/ManageAccountViewHolder.java | 2 +- .../manageaccounts/ManageAccountsActivity.java | 67 +++++++++----------- .../manageaccounts/ManageAccountsViewModel.java | 71 ++++++++++++++++++++++ .../owncloud/notes/persistence/NotesDatabase.java | 5 +- .../notes/persistence/NotesServerSyncTask.java | 4 ++ .../notes/shared/model/IResponseCallback.java | 4 +- 9 files changed, 126 insertions(+), 73 deletions(-) create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsViewModel.java diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java index 3600b0d6..da93e648 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java @@ -267,9 +267,9 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A .apply(RequestOptions.circleCropTransform()) .into(activityBinding.launchAccountSwitcher); - mainViewModel.synchronizeNotes(nextAccount, new IResponseCallback() { + mainViewModel.synchronizeNotes(nextAccount, new IResponseCallback() { @Override - public void onSuccess() { + public void onSuccess(Void v) { Log.d(TAG, "Successfully synchronized notes for " + nextAccount.getAccountName()); } @@ -309,9 +309,9 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A final LiveData accountLiveData = mainViewModel.getCurrentAccount(); accountLiveData.observe(this, (currentAccount) -> { accountLiveData.removeObservers(this); - mainViewModel.synchronizeNotes(currentAccount, new IResponseCallback() { + mainViewModel.synchronizeNotes(currentAccount, new IResponseCallback() { @Override - public void onSuccess() { + public void onSuccess(Void v) { Log.d(TAG, "Successfully synchronized notes for " + currentAccount.getAccountName()); } @@ -428,9 +428,9 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A final LiveData syncLiveData = mainViewModel.getCurrentAccount(); final Observer syncObserver = currentAccount -> { syncLiveData.removeObservers(this); - mainViewModel.synchronizeCapabilitiesAndNotes(currentAccount, new IResponseCallback() { + mainViewModel.synchronizeCapabilitiesAndNotes(currentAccount, new IResponseCallback() { @Override - public void onSuccess() { + public void onSuccess(Void v) { Log.d(TAG, "Successfully synchronized capabilities and notes for " + currentAccount.getAccountName()); } @@ -633,6 +633,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A final Capabilities capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, null); LiveData createLiveData = mainViewModel.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities); runOnUiThread(() -> createLiveData.observe(this, (account) -> { + createLiveData.removeObservers(this); new Thread(() -> { Log.i(TAG, capabilities.toString()); final Account a = mainViewModel.getLocalAccountByAccountName(ssoAccount.name); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java index 0241951e..77104856 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java @@ -369,11 +369,11 @@ public class MainViewModel extends AndroidViewModel { return items; } - public void synchronizeCapabilitiesAndNotes(@NonNull Account localAccount, @NonNull IResponseCallback callback) { + public void synchronizeCapabilitiesAndNotes(@NonNull Account localAccount, @NonNull IResponseCallback callback) { Log.i(TAG, "[synchronizeCapabilitiesAndNotes] Synchronize capabilities for " + localAccount.getAccountName()); - synchronizeCapabilities(localAccount, new IResponseCallback() { + synchronizeCapabilities(localAccount, new IResponseCallback() { @Override - public void onSuccess() { + public void onSuccess(Void v) { Log.i(TAG, "[synchronizeCapabilitiesAndNotes] Synchronize notes for " + localAccount.getAccountName()); synchronizeNotes(localAccount, callback); } @@ -388,7 +388,7 @@ public class MainViewModel extends AndroidViewModel { /** * Updates the network status if necessary and pulls the latest {@link Capabilities} of the given {@param localAccount} */ - public void synchronizeCapabilities(@NonNull Account localAccount, @NonNull IResponseCallback callback) { + public void synchronizeCapabilities(@NonNull Account localAccount, @NonNull IResponseCallback callback) { new Thread(() -> { final NotesServerSyncHelper syncHelper = db.getNoteServerSyncHelper(); if (!syncHelper.isSyncPossible()) { @@ -403,14 +403,14 @@ public class MainViewModel extends AndroidViewModel { localAccount.setTextColor(capabilities.getTextColor()); BrandingUtil.saveBrandColors(getApplication(), localAccount.getColor(), localAccount.getTextColor()); db.updateApiVersion(localAccount.getId(), capabilities.getApiVersion()); - callback.onSuccess(); + callback.onSuccess(null); } catch (NextcloudFilesAppAccountNotFoundException e) { db.getAccountDao().deleteAccount(localAccount); callback.onError(e); } catch (Exception e) { if (e instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) e).getStatusCode() == HttpURLConnection.HTTP_NOT_MODIFIED) { Log.i(TAG, "[synchronizeCapabilities] Capabilities not modified."); - callback.onSuccess(); + callback.onSuccess(null); } else { callback.onError(e); } @@ -428,7 +428,7 @@ public class MainViewModel extends AndroidViewModel { /** * Updates the network status if necessary and pulls the latest notes of the given {@param localAccount} */ - public void synchronizeNotes(@NonNull Account currentAccount, @NonNull IResponseCallback callback) { + public void synchronizeNotes(@NonNull Account currentAccount, @NonNull IResponseCallback callback) { new Thread(() -> { Log.v(TAG, "[synchronize] - currentAccount: " + currentAccount.getAccountName()); final NotesServerSyncHelper syncHelper = db.getNoteServerSyncHelper(); @@ -437,7 +437,7 @@ public class MainViewModel extends AndroidViewModel { } if (syncHelper.isSyncPossible()) { syncHelper.scheduleSync(currentAccount, false); - callback.onSuccess(); + callback.onSuccess(null); } else { // Sync is not possible if (syncHelper.isNetworkConnected() && syncHelper.isSyncOnlyOnWifi()) { callback.onError(new IntendedOfflineException("Network is connected, but sync is not possible.")); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountAdapter.java index 1c84a7fc..87cffe2e 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountAdapter.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountAdapter.java @@ -22,10 +22,10 @@ public class ManageAccountAdapter extends RecyclerView.Adapter localAccounts = new ArrayList<>(); @NonNull private final Consumer onAccountClick; - @Nullable + @NonNull private final Consumer onAccountDelete; - public ManageAccountAdapter(@NonNull Consumer onAccountClick, @Nullable Consumer onAccountDelete) { + public ManageAccountAdapter(@NonNull Consumer onAccountClick, @NonNull Consumer onAccountDelete) { this.onAccountClick = onAccountClick; this.onAccountDelete = onAccountDelete; setHasStableIds(true); @@ -48,18 +48,7 @@ public class ManageAccountAdapter extends RecyclerView.Adapter { setCurrentLocalAccount(localAccountClicked); onAccountClick.accept(localAccountClicked); - }, (localAccountToDelete -> { - if (onAccountDelete != null) { - for (int i = 0; i < localAccounts.size(); i++) { - if (localAccounts.get(i).getId() == localAccountToDelete.getId()) { - localAccounts.remove(i); - notifyItemRemoved(i); - break; - } - } - onAccountDelete.accept(localAccountToDelete); - } - }), currentLocalAccount != null && currentLocalAccount.getId() == localAccount.getId()); + }, onAccountDelete, currentLocalAccount != null && currentLocalAccount.getId() == localAccount.getId()); } @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java index eacb3a88..fcd8c953 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountViewHolder.java @@ -24,7 +24,7 @@ import static it.niedermann.owncloud.notes.branding.BrandingUtil.applyBrandToLay public class ManageAccountViewHolder extends RecyclerView.ViewHolder { - private ItemAccountChooseBinding binding; + private final ItemAccountChooseBinding binding; public ManageAccountViewHolder(@NonNull View itemView) { super(itemView); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java index a8107726..5e230a74 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java @@ -2,24 +2,22 @@ package it.niedermann.owncloud.notes.manageaccounts; import android.os.Bundle; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; -import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; -import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; import com.nextcloud.android.sso.helper.SingleAccountHelper; -import com.nextcloud.android.sso.model.SingleSignOnAccount; import it.niedermann.owncloud.notes.LockedActivity; import it.niedermann.owncloud.notes.databinding.ActivityManageAccountsBinding; import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.persistence.entity.Account; - -import static androidx.lifecycle.Transformations.distinctUntilChanged; +import it.niedermann.owncloud.notes.shared.model.IResponseCallback; public class ManageAccountsActivity extends LockedActivity { private ActivityManageAccountsBinding binding; + private ManageAccountsViewModel viewModel; private ManageAccountAdapter adapter; private NotesDatabase db = null; @@ -28,45 +26,38 @@ public class ManageAccountsActivity extends LockedActivity { super.onCreate(savedInstanceState); binding = ActivityManageAccountsBinding.inflate(getLayoutInflater()); + viewModel = new ViewModelProvider(this).get(ManageAccountsViewModel.class); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); db = NotesDatabase.getInstance(this); - distinctUntilChanged(db.getAccountDao().getAccounts$()).observe(this, (localAccounts) -> { - adapter = new ManageAccountAdapter((localAccount) -> SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccount.getAccountName()), (localAccount) -> { - LiveData deleteLiveData = db.deleteAccount(localAccount); - deleteLiveData.observe(this, (v) -> { - for (Account temp : localAccounts) { - if (temp.getId() == localAccount.getId()) { - localAccounts.remove(temp); - break; - } - } - if (localAccounts.size() > 0) { - SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccounts.get(0).getAccountName()); - adapter.setCurrentLocalAccount(localAccounts.get(0)); - } else { - SingleAccountHelper.setCurrentAccount(getApplicationContext(), null); - finish(); - } - deleteLiveData.removeObservers(this); - }); - }); - adapter.setLocalAccounts(localAccounts); - try { - final SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(this); - if (ssoAccount != null) { - new Thread(() -> { - final Account account = db.getAccountDao().getAccountByName(ssoAccount.name); - runOnUiThread(() -> adapter.setCurrentLocalAccount(account)); - }).start(); - } - } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); + adapter = new ManageAccountAdapter( + (accountToSelect) -> viewModel.selectAccount(accountToSelect, this), + (accountToDelete) -> viewModel.deleteAccount(accountToDelete, this) + ); + + binding.accounts.setAdapter(adapter); + + viewModel.getAccounts$().observe(this, (accounts) -> { + if (accounts == null || accounts.size() < 1) { + finish(); + return; } - binding.accounts.setAdapter(adapter); + this.adapter.setLocalAccounts(accounts); + viewModel.getCurrentAccount(this, new IResponseCallback() { + @Override + public void onSuccess(Account result) { + runOnUiThread(() -> adapter.setCurrentLocalAccount(result)); + } + + @Override + public void onError(@NonNull Throwable t) { + runOnUiThread(() -> adapter.setCurrentLocalAccount(null)); + t.printStackTrace(); + } + }); }); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsViewModel.java new file mode 100644 index 00000000..b86b6fac --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsViewModel.java @@ -0,0 +1,71 @@ +package it.niedermann.owncloud.notes.manageaccounts; + +import android.app.Application; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; + +import com.nextcloud.android.sso.AccountImporter; +import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; +import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; +import com.nextcloud.android.sso.helper.SingleAccountHelper; + +import java.util.List; + +import it.niedermann.owncloud.notes.persistence.NotesDatabase; +import it.niedermann.owncloud.notes.persistence.SSOClient; +import it.niedermann.owncloud.notes.persistence.entity.Account; +import it.niedermann.owncloud.notes.shared.model.IResponseCallback; + +import static androidx.lifecycle.Transformations.distinctUntilChanged; + +public class ManageAccountsViewModel extends AndroidViewModel { + + private static final String TAG = ManageAccountsViewModel.class.getSimpleName(); + + @NonNull + private final NotesDatabase db; + + public ManageAccountsViewModel(@NonNull Application application) { + super(application); + this.db = NotesDatabase.getInstance(application); + } + + public void getCurrentAccount(@NonNull Context context, @NonNull IResponseCallback callback) { + try { + callback.onSuccess(db.getAccountDao().getAccountByName((SingleAccountHelper.getCurrentSingleSignOnAccount(context).name))); + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + callback.onError(e); + } + } + + public LiveData> getAccounts$() { + return distinctUntilChanged(db.getAccountDao().getAccounts$()); + } + + public void deleteAccount(@NonNull Account account, @NonNull Context context) { + new Thread(() -> { + final List accounts = db.getAccountDao().getAccounts(); + for (int i = 0; i < accounts.size(); i++) { + if (accounts.get(i).getId() == account.getId()) { + if (i > 0) { + selectAccount(accounts.get(i - 1), context); + } else if (accounts.size() > 1) { + selectAccount(accounts.get(i + 1), context); + } else { + selectAccount(null, context); + } + db.deleteAccount(accounts.get(i)); + break; + } + } + }).start(); + } + + public void selectAccount(@Nullable Account account, @NonNull Context context) { + SingleAccountHelper.setCurrentAccount(context, (account == null) ? null : account.getAccountName()); + } +} \ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java index 40d918df..c6f80280 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java @@ -409,9 +409,8 @@ public abstract class NotesDatabase extends RoomDatabase { * @throws IllegalArgumentException if no account has been deleted by the given accountId */ @AnyThread - public LiveData deleteAccount(@NonNull Account localAccount) throws IllegalArgumentException { + public void deleteAccount(@NonNull Account localAccount) throws IllegalArgumentException { validateAccountId(localAccount.getId()); - MutableLiveData ret = new MutableLiveData<>(); new Thread(() -> { int deletedAccounts = getAccountDao().deleteAccount(localAccount); if (deletedAccounts < 1) { @@ -431,9 +430,7 @@ public abstract class NotesDatabase extends RoomDatabase { // TODO this should already be handled by foreign key cascade, no? final int deletedNotes = getNoteDao().deleteByAccountId(localAccount.getId()); Log.v(TAG, "Deleted " + deletedNotes + " notes from account " + localAccount.getId()); - ret.postValue(null); }).start(); - return ret; } private static void validateAccountId(long accountId) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesServerSyncTask.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesServerSyncTask.java index ddc05981..dcc44979 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesServerSyncTask.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesServerSyncTask.java @@ -166,6 +166,10 @@ abstract class NotesServerSyncTask extends Thread { // FIXME re-reading the localAccount is only a workaround for a not-up-to-date eTag in localAccount. final Account accountFromDatabase = db.getAccountDao().getAccountById(localAccount.getId()); + if (accountFromDatabase == null) { + callbacks.remove(localAccount.getId()); + return true; + } localAccount.setModified(accountFromDatabase.getModified()); localAccount.setETag(accountFromDatabase.getETag()); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/IResponseCallback.java b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/IResponseCallback.java index 2c329727..707931b0 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/IResponseCallback.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/IResponseCallback.java @@ -2,8 +2,8 @@ package it.niedermann.owncloud.notes.shared.model; import androidx.annotation.NonNull; -public interface IResponseCallback { - void onSuccess(); +public interface IResponseCallback { + void onSuccess(T result); void onError(@NonNull Throwable t); } -- cgit v1.2.3