Use local account instead of sso account until sync

This commit is contained in:
Stefan Niedermann 2020-10-16 18:20:20 +02:00
parent fafca46f90
commit fbd3b63052
10 changed files with 149 additions and 164 deletions

View file

@ -44,7 +44,7 @@ public class AppendToNoteActivity extends MainActivity {
} else {
newContent = receivedText;
}
mainViewModel.updateNoteAndSync(ssoAccount, localAccount, note, newContent, null, () -> Toast.makeText(this, getString(R.string.added_content, receivedText), Toast.LENGTH_SHORT).show());
mainViewModel.updateNoteAndSync(localAccount, note, newContent, null, () -> Toast.makeText(this, getString(R.string.added_content, receivedText), Toast.LENGTH_SHORT).show());
} else {
Toast.makeText(this, R.string.shared_text_empty, Toast.LENGTH_SHORT).show();
}

View file

@ -119,7 +119,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
note = new NoteWithCategory(new Note(-1, -1L, null, NoteUtil.generateNoteTitle(content), content, false, getString(R.string.category_readonly), DBStatus.VOID, -1, "", 0));
}
} else {
final LiveData<NoteWithCategory> createLiveData = db.addNoteAndSync(ssoAccount, localAccount.getId(), cloudNote);
final LiveData<NoteWithCategory> createLiveData = db.addNoteAndSync(localAccount, cloudNote);
createLiveData.observe(requireActivity(), (createdNote) -> {
note = createdNote;
originalNote = null;
@ -224,18 +224,18 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
switch (item.getItemId()) {
case R.id.menu_cancel:
if (originalNote == null) {
db.deleteNoteAndSync(ssoAccount, note.getId());
db.deleteNoteAndSync(localAccount, note.getId());
} else {
db.updateNoteAndSync(ssoAccount, localAccount, originalNote, null, null, null);
db.updateNoteAndSync(localAccount, originalNote, null, null, null);
}
listener.close();
return true;
case R.id.menu_delete:
db.deleteNoteAndSync(ssoAccount, note.getId());
db.deleteNoteAndSync(localAccount, note.getId());
listener.close();
return true;
case R.id.menu_favorite:
db.toggleFavoriteAndSync(ssoAccount, note.getId());
db.toggleFavoriteAndSync(localAccount, note.getId());
listener.onNoteUpdated(note);
prepareFavoriteOption(item);
return true;
@ -291,7 +291,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
public void onCloseNote() {
if (!titleModified && originalNote == null && getContent().isEmpty()) {
db.deleteNoteAndSync(ssoAccount, note.getId());
db.deleteNoteAndSync(localAccount, note.getId());
}
}
@ -312,7 +312,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
Log.v(TAG, "... not saving, since nothing has changed");
}
} else {
note = db.updateNoteAndSync(ssoAccount, localAccount, note, newContent, null, callback);
note = db.updateNoteAndSync(localAccount, note, newContent, null, callback);
listener.onNoteUpdated(note);
requireActivity().invalidateOptionsMenu();
}
@ -356,7 +356,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
@Override
public void onCategoryChosen(String category) {
db.setCategory(ssoAccount, note.getAccountId(), note.getId(), category);
db.setCategory(localAccount, note.getId(), category);
note.setCategory(category);
listener.onNoteUpdated(note);
}
@ -365,12 +365,12 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego
public void onTitleEdited(String newTitle) {
titleModified = true;
note.getNote().setTitle(newTitle);
note = db.updateNoteAndSync(ssoAccount, localAccount, note, note.getContent(), newTitle, null);
note = db.updateNoteAndSync(localAccount, note, note.getContent(), newTitle, null);
listener.onNoteUpdated(note);
}
public void moveNote(Account account) {
db.moveNoteToAnotherAccount(ssoAccount, note, account.getId());
db.moveNoteToAnotherAccount(account, note);
listener.close();
}

View file

@ -36,6 +36,7 @@ import java.util.HashSet;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.databinding.FragmentNotePreviewBinding;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.Note;
import it.niedermann.owncloud.notes.shared.util.MarkDownUtil;
import it.niedermann.owncloud.notes.shared.util.NoteLinksUtils;
@ -211,14 +212,14 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O
binding.swiperefreshlayout.setRefreshing(true);
try {
TextProcessorChain chain = defaultTextProcessorChain(note.getNote());
SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext());
db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, () -> {
Account account = db.getAccountDao().getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()).name);
db.getNoteServerSyncHelper().addCallbackPull(account, () -> {
note = db.getNoteDao().getNoteWithCategory(note.getAccountId(), note.getId());
changedText = note.getContent();
binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent())));
binding.swiperefreshlayout.setRefreshing(false);
});
db.getNoteServerSyncHelper().scheduleSync(ssoAccount, false);
db.getNoteServerSyncHelper().scheduleSync(account, false);
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
e.printStackTrace();
}

View file

@ -3,7 +3,6 @@ package it.niedermann.owncloud.notes.main;
import android.animation.AnimatorInflater;
import android.app.SearchManager;
import android.content.Intent;
import android.database.sqlite.SQLiteException;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.net.Uri;
@ -246,15 +245,10 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
.apply(RequestOptions.circleCropTransform())
.into(activityBinding.launchAccountSwitcher);
try {
ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext());
new NotesListViewItemTouchHelper(ssoAccount, this, mainViewModel, this, adapter, swipeRefreshLayout, coordinatorLayout, gridView)
.attachToRecyclerView(listView);
if (!mainViewModel.synchronize(ssoAccount)) {
BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
}
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
Log.i(TAG, "Tried to select account, but got an " + e.getClass().getSimpleName() + ". Asking for importing an account...");
new NotesListViewItemTouchHelper(a, this, mainViewModel, this, adapter, swipeRefreshLayout, coordinatorLayout, gridView)
.attachToRecyclerView(listView);
if (!mainViewModel.synchronize(a)) {
BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
}
fabCreate.show();
activityBinding.launchAccountSwitcher.setOnClickListener((v) -> {
@ -281,7 +275,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
protected void onResume() {
// refresh and sync every time the activity gets
if (!mainViewModel.synchronize(ssoAccount)) {
if (!mainViewModel.synchronize(localAccount)) {
BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
}
super.onResume();
@ -607,7 +601,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
public void onNoteFavoriteClick(int position, View view) {
mainViewModel.toggleFavoriteAndSync(ssoAccount, ((NoteWithCategory) adapter.getItem(position)).getId());
mainViewModel.toggleFavoriteAndSync(localAccount, ((NoteWithCategory) adapter.getItem(position)).getId());
adapter.notifyItemChanged(position);
}
@ -616,7 +610,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
boolean selected = adapter.select(position);
if (selected) {
v.setSelected(true);
mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(this, coordinatorLayout, mainViewModel, this, localAccount.getId(), canMoveNoteToAnotherAccounts, adapter, listView, getSupportFragmentManager(), activityBinding.searchView));
mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(this, coordinatorLayout, mainViewModel, this, localAccount, canMoveNoteToAnotherAccounts, adapter, listView, getSupportFragmentManager(), activityBinding.searchView));
int checkedItemCount = adapter.getSelected().size();
mActionMode.setTitle(getResources().getQuantityString(R.plurals.ab_selected, checkedItemCount, checkedItemCount));
}
@ -678,7 +672,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
public void onAccountPicked(@NonNull Account account) {
for (Integer i : adapter.getSelected()) {
final LiveData<NoteWithCategory> moveLiveData = mainViewModel.moveNoteToAnotherAccount(ssoAccount, (NoteWithCategory) adapter.getItem(i), account.getId());
final LiveData<NoteWithCategory> moveLiveData = mainViewModel.moveNoteToAnotherAccount(account, (NoteWithCategory) adapter.getItem(i));
moveLiveData.observe(this, (v) -> moveLiveData.removeObservers(this));
}
}
@ -686,7 +680,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
public void onCategoryChosen(String category) {
for (Integer i : adapter.getSelected()) {
mainViewModel.setCategory(ssoAccount, localAccount.getId(), ((NoteWithCategory) adapter.getItem(i)).getId(), category);
mainViewModel.setCategory(localAccount, ((NoteWithCategory) adapter.getItem(i)).getId(), category);
}
}
}

View file

@ -321,13 +321,13 @@ public class MainViewModel extends AndroidViewModel {
/**
* @return <code>true</code>, if a synchronization could successfully be triggered.
*/
public boolean synchronize(SingleSignOnAccount ssoAccount) {
public boolean synchronize(Account account) {
NoteServerSyncHelper syncHelper = db.getNoteServerSyncHelper();
if (!syncHelper.isSyncPossible()) {
syncHelper.updateNetworkStatus();
}
if (syncHelper.isSyncPossible()) {
syncHelper.scheduleSync(ssoAccount, false);
syncHelper.scheduleSync(account, false);
return true;
} else { // Sync is not possible
if (syncHelper.isNetworkConnected() && syncHelper.isSyncOnlyOnWifi()) {
@ -353,25 +353,25 @@ public class MainViewModel extends AndroidViewModel {
public LiveData<Boolean> performFullSynchronizationForCurrentAccount() {
final MutableLiveData<Boolean> insufficientInformation = new MutableLiveData<>();
return switchMap(getCurrentAccount(), currentAccount -> {
Log.v(TAG, "[performFullSynchronizationForCurrentAccount] - currentAccount: " + currentAccount);
if (currentAccount == null) {
return switchMap(getCurrentAccount(), localAccount -> {
Log.v(TAG, "[performFullSynchronizationForCurrentAccount] - currentAccount: " + localAccount);
if (localAccount == null) {
return insufficientInformation;
} else {
Log.i(TAG, "[performFullSynchronizationForCurrentAccount] Refreshing capabilities for " + currentAccount.getAccountName());
Log.i(TAG, "[performFullSynchronizationForCurrentAccount] Refreshing capabilities for " + localAccount.getAccountName());
MutableLiveData<Boolean> syncSuccess = new MutableLiveData<>();
new Thread(() -> {
try {
SingleSignOnAccount ssoAccount = AccountImporter.getSingleSignOnAccount(getApplication(), currentAccount.getAccountName());
SingleSignOnAccount ssoAccount = AccountImporter.getSingleSignOnAccount(getApplication(), localAccount.getAccountName());
final Capabilities capabilities;
try {
capabilities = CapabilitiesClient.getCapabilities(getApplication(), ssoAccount, currentAccount.getCapabilitiesETag());
db.getAccountDao().updateCapabilitiesETag(currentAccount.getId(), capabilities.getETag());
db.getAccountDao().updateBrand(currentAccount.getId(), capabilities.getColor(), capabilities.getTextColor());
currentAccount.setColor(capabilities.getColor());
currentAccount.setTextColor(capabilities.getTextColor());
BrandingUtil.saveBrandColors(getApplication(), currentAccount.getColor(), currentAccount.getTextColor());
db.updateApiVersion(currentAccount.getId(), capabilities.getApiVersion());
capabilities = CapabilitiesClient.getCapabilities(getApplication(), ssoAccount, localAccount.getCapabilitiesETag());
db.getAccountDao().updateCapabilitiesETag(localAccount.getId(), capabilities.getETag());
db.getAccountDao().updateBrand(localAccount.getId(), capabilities.getColor(), capabilities.getTextColor());
localAccount.setColor(capabilities.getColor());
localAccount.setTextColor(capabilities.getTextColor());
BrandingUtil.saveBrandColors(getApplication(), localAccount.getColor(), localAccount.getTextColor());
db.updateApiVersion(localAccount.getId(), capabilities.getApiVersion());
Log.i(TAG, capabilities.toString());
} catch (Exception e) {
if (e instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) e).getStatusCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
@ -381,7 +381,7 @@ public class MainViewModel extends AndroidViewModel {
}
}
// Even if the capabilities endpoint makes trouble, we can still try to synchronize the notes
syncSuccess.postValue(synchronize(ssoAccount));
syncSuccess.postValue(synchronize(localAccount));
} catch (NextcloudFilesAppAccountNotFoundException e) {
e.printStackTrace();
// TODO should we just remove this account from the database?
@ -403,12 +403,12 @@ public class MainViewModel extends AndroidViewModel {
return db.getAccountDao().getAccounts();
}
public void setCategory(SingleSignOnAccount ssoAccount, long accountId, Long noteId, @NonNull String category) {
db.setCategory(ssoAccount, accountId, noteId, category);
public void setCategory(Account account, Long noteId, @NonNull String category) {
db.setCategory(account, noteId, category);
}
public LiveData<NoteWithCategory> moveNoteToAnotherAccount(SingleSignOnAccount ssoAccount, NoteWithCategory note, long newAccountId) {
return db.moveNoteToAnotherAccount(ssoAccount, note, newAccountId);
public LiveData<NoteWithCategory> moveNoteToAnotherAccount(Account account, NoteWithCategory note) {
return db.moveNoteToAnotherAccount(account, note);
}
@WorkerThread
@ -420,12 +420,12 @@ public class MainViewModel extends AndroidViewModel {
return db.deleteAccount(account);
}
public void toggleFavoriteAndSync(SingleSignOnAccount ssoAccount, long noteId) {
db.toggleFavoriteAndSync(ssoAccount, noteId);
public void toggleFavoriteAndSync(Account account, long noteId) {
db.toggleFavoriteAndSync(account, noteId);
}
public void deleteNoteAndSync(SingleSignOnAccount ssoAccount, long id) {
db.deleteNoteAndSync(ssoAccount, id);
public void deleteNoteAndSync(Account account, long id) {
db.deleteNoteAndSync(account, id);
}
public LiveData<Account> addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities) {
@ -437,13 +437,13 @@ public class MainViewModel extends AndroidViewModel {
return db.getNoteDao().getNoteWithCategory(accountId, id);
}
public LiveData<NoteWithCategory> addNoteAndSync(SingleSignOnAccount ssoAccount, long accountId, NoteWithCategory note) {
return db.addNoteAndSync(ssoAccount, accountId, note);
public LiveData<NoteWithCategory> addNoteAndSync(Account account, long accountId, NoteWithCategory note) {
return db.addNoteAndSync(account, note);
}
@WorkerThread
public void updateNoteAndSync(SingleSignOnAccount ssoAccount, @NonNull Account localAccount, @NonNull NoteWithCategory oldNote, @Nullable String newContent, @Nullable String newTitle, @Nullable ISyncCallback callback) {
db.updateNoteAndSync(ssoAccount, localAccount, oldNote, newContent, newTitle, callback);
public void updateNoteAndSync(@NonNull Account localAccount, @NonNull NoteWithCategory oldNote, @Nullable String newContent, @Nullable String newTitle, @Nullable ISyncCallback callback) {
db.updateNoteAndSync(localAccount, oldNote, newContent, newTitle, callback);
}
public void createOrUpdateSingleNoteWidgetData(SingleNoteWidgetData data) {

View file

@ -34,6 +34,7 @@ import it.niedermann.owncloud.notes.branding.BrandedSnackbar;
import it.niedermann.owncloud.notes.edit.category.CategoryDialogFragment;
import it.niedermann.owncloud.notes.main.items.ItemAdapter;
import it.niedermann.owncloud.notes.persistence.NotesDatabase;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory;
import it.niedermann.owncloud.notes.shared.util.ShareUtil;
@ -49,7 +50,7 @@ public class MultiSelectedActionModeCallback implements Callback {
private final MainViewModel mainViewModel;
@NonNull
private final LifecycleOwner lifecycleOwner;
private final long currentLocalAccountId;
private final Account account;
private final boolean canMoveNoteToAnotherAccounts;
private final ItemAdapter adapter;
private final RecyclerView recyclerView;
@ -57,12 +58,12 @@ public class MultiSelectedActionModeCallback implements Callback {
private final SearchView searchView;
public MultiSelectedActionModeCallback(
@NonNull Context context, @NonNull View view, @NonNull MainViewModel mainViewModel, @NonNull LifecycleOwner lifecycleOwner, long currentLocalAccountId, boolean canMoveNoteToAnotherAccounts, ItemAdapter adapter, RecyclerView recyclerView, FragmentManager fragmentManager, SearchView searchView) {
@NonNull Context context, @NonNull View view, @NonNull MainViewModel mainViewModel, @NonNull LifecycleOwner lifecycleOwner, @NonNull Account account, boolean canMoveNoteToAnotherAccounts, ItemAdapter adapter, RecyclerView recyclerView, FragmentManager fragmentManager, SearchView searchView) {
this.context = context;
this.view = view;
this.mainViewModel = mainViewModel;
this.lifecycleOwner = lifecycleOwner;
this.currentLocalAccountId = currentLocalAccountId;
this.account = account;
this.canMoveNoteToAnotherAccounts = canMoveNoteToAnotherAccounts;
this.adapter = adapter;
this.recyclerView = recyclerView;
@ -104,14 +105,12 @@ public class MultiSelectedActionModeCallback implements Callback {
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.menu_delete) {
try {
SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);
List<NoteWithCategory> deletedNotes = new ArrayList<>();
List<Integer> selection = adapter.getSelected();
for (Integer i : selection) {
NoteWithCategory note = (NoteWithCategory) adapter.getItem(i);
deletedNotes.add(mainViewModel.getNoteWithCategory(note.getAccountId(), note.getId()));
mainViewModel.deleteNoteAndSync(ssoAccount, note.getId());
mainViewModel.deleteNoteAndSync(account, note.getId());
}
mode.finish(); // Action picked, so close the CAB
//after delete selection has to be cleared
@ -122,7 +121,7 @@ public class MultiSelectedActionModeCallback implements Callback {
BrandedSnackbar.make(view, deletedSnackbarTitle, Snackbar.LENGTH_LONG)
.setAction(R.string.action_undo, (View v) -> {
for (NoteWithCategory deletedNote : deletedNotes) {
final LiveData<NoteWithCategory> undoLiveData = mainViewModel.addNoteAndSync(ssoAccount, deletedNote.getAccountId(), deletedNote);
final LiveData<NoteWithCategory> undoLiveData = mainViewModel.addNoteAndSync(account, deletedNote.getAccountId(), deletedNote);
undoLiveData.observe(lifecycleOwner, (o) -> undoLiveData.removeObservers(lifecycleOwner));
}
String restoreSnackbarTitle = deletedNotes.size() == 1
@ -132,13 +131,10 @@ public class MultiSelectedActionModeCallback implements Callback {
.show();
})
.show();
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
e.printStackTrace();
}
return true;
} else if (itemId == R.id.menu_move) {
AccountPickerDialogFragment
.newInstance(currentLocalAccountId)
.newInstance(account.getId())
.show(fragmentManager, MainActivity.class.getSimpleName());
return true;
} else if (itemId == R.id.menu_share) {
@ -160,7 +156,7 @@ public class MultiSelectedActionModeCallback implements Callback {
return true;
} else if (itemId == R.id.menu_category) {// TODO detect whether all selected notes do have the same category - in this case preselect it
CategoryDialogFragment
.newInstance(currentLocalAccountId, "")
.newInstance(account.getId(), "")
.show(fragmentManager, CategoryDialogFragment.class.getSimpleName());
return false;

View file

@ -16,7 +16,6 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.snackbar.Snackbar;
import com.nextcloud.android.sso.model.SingleSignOnAccount;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.BrandedSnackbar;
@ -24,7 +23,7 @@ import it.niedermann.owncloud.notes.main.MainViewModel;
import it.niedermann.owncloud.notes.main.items.ItemAdapter;
import it.niedermann.owncloud.notes.main.items.NoteViewHolder;
import it.niedermann.owncloud.notes.main.items.section.SectionViewHolder;
import it.niedermann.owncloud.notes.persistence.NotesDatabase;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory;
public class NotesListViewItemTouchHelper extends ItemTouchHelper {
@ -33,7 +32,7 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper {
private static final int UNDO_DURATION = 12_000;
public NotesListViewItemTouchHelper(
@NonNull SingleSignOnAccount ssoAccount,
@NonNull Account account,
@NonNull Context context,
@NonNull MainViewModel mainViewModel,
@NonNull LifecycleOwner lifecycleOwner,
@ -75,14 +74,14 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper {
case ItemTouchHelper.LEFT:
final NoteWithCategory dbNoteWithoutContent = (NoteWithCategory) adapter.getItem(viewHolder.getAdapterPosition());
final NoteWithCategory dbNote = mainViewModel.getNoteWithCategory(dbNoteWithoutContent.getAccountId(), dbNoteWithoutContent.getId());
mainViewModel.deleteNoteAndSync(ssoAccount, dbNote.getId());
mainViewModel.deleteNoteAndSync(account, dbNote.getId());
Log.v(TAG, "Item deleted through swipe ----------------------------------------------");
if (view == null) {
Toast.makeText(context, context.getString(R.string.action_note_deleted, dbNote.getTitle()), Toast.LENGTH_LONG).show();
} else {
BrandedSnackbar.make(view, context.getString(R.string.action_note_deleted, dbNote.getTitle()), UNDO_DURATION)
.setAction(R.string.action_undo, (View v) -> {
final LiveData<NoteWithCategory> undoLiveData = mainViewModel.addNoteAndSync(ssoAccount, dbNote.getAccountId(), dbNote);
final LiveData<NoteWithCategory> undoLiveData = mainViewModel.addNoteAndSync(account, dbNote.getAccountId(), dbNote);
undoLiveData.observe(lifecycleOwner, (o) -> undoLiveData.removeObservers(lifecycleOwner));
BrandedSnackbar.make(view, context.getString(R.string.action_note_restored, dbNote.getTitle()), Snackbar.LENGTH_SHORT)
.show();
@ -92,7 +91,7 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper {
break;
case ItemTouchHelper.RIGHT:
final NoteWithCategory adapterNote = (NoteWithCategory) adapter.getItem(viewHolder.getAdapterPosition());
mainViewModel.toggleFavoriteAndSync(ssoAccount, adapterNote.getId());
mainViewModel.toggleFavoriteAndSync(account, adapterNote.getId());
break;
default:
//NoOp

View file

@ -16,6 +16,7 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.preference.PreferenceManager;
import com.nextcloud.android.sso.AccountImporter;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException;
import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
@ -69,7 +70,7 @@ public class NoteServerSyncHelper {
* @see <a href="https://stackoverflow.com/a/3104265">Do not make this a local variable.</a>
*/
@SuppressWarnings("FieldCanBeLocal")
private SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener = (SharedPreferences prefs, String key) -> {
private final SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener = (SharedPreferences prefs, String key) -> {
if (syncOnlyOnWifiKey.equals(key)) {
syncOnlyOnWifi = prefs.getBoolean(syncOnlyOnWifiKey, false);
updateNetworkStatus();
@ -81,22 +82,24 @@ public class NoteServerSyncHelper {
public void onReceive(Context context, Intent intent) {
updateNetworkStatus();
if (isSyncPossible() && SSOUtil.isConfigured(context)) {
try {
scheduleSync(SingleAccountHelper.getCurrentSingleSignOnAccount(context), false);
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
Log.v(TAG, "Can not select current SingleSignOn account after network changed, do not sync.");
}
new Thread(() -> {
try {
scheduleSync(db.getAccountDao().getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(context).name), false);
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
Log.v(TAG, "Can not select current SingleSignOn account after network changed, do not sync.");
}
}).start();
}
}
};
// current state of the synchronization
private final Map<String, Boolean> syncActive = new HashMap<>();
private final Map<String, Boolean> syncScheduled = new HashMap<>();
private final Map<Long, Boolean> syncActive = new HashMap<>();
private final Map<Long, Boolean> syncScheduled = new HashMap<>();
// list of callbacks for both parts of synchronziation
private final Map<String, List<ISyncCallback>> callbacksPush = new HashMap<>();
private final Map<String, List<ISyncCallback>> callbacksPull = new HashMap<>();
private final Map<Long, List<ISyncCallback>> callbacksPush = new HashMap<>();
private final Map<Long, List<ISyncCallback>> callbacksPull = new HashMap<>();
private NoteServerSyncHelper(NotesDatabase db) {
this.db = db;
@ -164,16 +167,16 @@ public class NoteServerSyncHelper {
*
* @param callback Implementation of ISyncCallback, contains one method that shall be executed.
*/
public void addCallbackPush(SingleSignOnAccount ssoAccount, ISyncCallback callback) {
if (ssoAccount == null) {
public void addCallbackPush(Account account, ISyncCallback callback) {
if (account == null) {
Log.i(TAG, "ssoAccount is null. Is this a local account?");
callback.onScheduled();
callback.onFinish();
} else {
if (!callbacksPush.containsKey(ssoAccount.name)) {
callbacksPush.put(ssoAccount.name, new ArrayList<>());
if (!callbacksPush.containsKey(account.getId())) {
callbacksPush.put(account.getId(), new ArrayList<>());
}
Objects.requireNonNull(callbacksPush.get(ssoAccount.name)).add(callback);
Objects.requireNonNull(callbacksPush.get(account.getId())).add(callback);
}
}
@ -185,16 +188,16 @@ public class NoteServerSyncHelper {
*
* @param callback Implementation of ISyncCallback, contains one method that shall be executed.
*/
public void addCallbackPull(SingleSignOnAccount ssoAccount, ISyncCallback callback) {
if (ssoAccount == null) {
public void addCallbackPull(Account account, ISyncCallback callback) {
if (account == null) {
Log.i(TAG, "ssoAccount is null. Is this a local account?");
callback.onScheduled();
callback.onFinish();
} else {
if (!callbacksPull.containsKey(ssoAccount.name)) {
callbacksPull.put(ssoAccount.name, new ArrayList<>());
if (!callbacksPull.containsKey(account.getId())) {
callbacksPull.put(account.getId(), new ArrayList<>());
}
Objects.requireNonNull(callbacksPull.get(ssoAccount.name)).add(callback);
Objects.requireNonNull(callbacksPull.get(account.getId())).add(callback);
}
}
@ -204,53 +207,54 @@ public class NoteServerSyncHelper {
*
* @param onlyLocalChanges Whether to only push local changes to the server or to also load the whole list of notes from the server.
*/
public void scheduleSync(SingleSignOnAccount ssoAccount, boolean onlyLocalChanges) {
if (ssoAccount == null) {
public void scheduleSync(Account account, boolean onlyLocalChanges) {
if (account == null) {
Log.i(TAG, SingleSignOnAccount.class.getSimpleName() + " is null. Is this a local account?");
} else {
if (syncActive.get(ssoAccount.name) == null) {
syncActive.put(ssoAccount.name, false);
if (syncActive.get(account.getId()) == null) {
syncActive.put(account.getId(), false);
}
Log.d(TAG, "Sync requested (" + (onlyLocalChanges ? "onlyLocalChanges" : "full") + "; " + (Boolean.TRUE.equals(syncActive.get(ssoAccount.name)) ? "sync active" : "sync NOT active") + ") ...");
if (isSyncPossible() && (!Boolean.TRUE.equals(syncActive.get(ssoAccount.name)) || onlyLocalChanges)) {
Log.d(TAG, "... starting now");
final Account localAccount = db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name);
if (localAccount == null) {
Log.e(TAG, Account.class.getSimpleName() + " for ssoAccount \"" + ssoAccount.name + "\" is null. Cannot synchronize.", new IllegalStateException());
return;
Log.d(TAG, "Sync requested (" + (onlyLocalChanges ? "onlyLocalChanges" : "full") + "; " + (Boolean.TRUE.equals(syncActive.get(account.getId())) ? "sync active" : "sync NOT active") + ") ...");
if (isSyncPossible() && (!Boolean.TRUE.equals(syncActive.get(account.getId())) || onlyLocalChanges)) {
try {
SingleSignOnAccount ssoAccount = AccountImporter.getSingleSignOnAccount(context, account.getAccountName());
Log.d(TAG, "... starting now");
final NotesClient notesClient = NotesClient.newInstance(account.getPreferredApiVersion(), context);
final SyncTask syncTask = new SyncTask(notesClient, account, ssoAccount, onlyLocalChanges);
syncTask.addCallbacks(account, callbacksPush.get(account.getId()));
callbacksPush.put(account.getId(), new ArrayList<>());
if (!onlyLocalChanges) {
syncTask.addCallbacks(account, callbacksPull.get(account.getId()));
callbacksPull.put(account.getId(), new ArrayList<>());
}
syncTask.execute();
} catch (NextcloudFilesAppAccountNotFoundException e) {
Log.e(TAG, "... Could not find " + SingleSignOnAccount.class.getSimpleName() + " for account name " + account.getAccountName());
e.printStackTrace();
}
final NotesClient notesClient = NotesClient.newInstance(localAccount.getPreferredApiVersion(), context);
final SyncTask syncTask = new SyncTask(notesClient, localAccount, ssoAccount, onlyLocalChanges);
syncTask.addCallbacks(ssoAccount, callbacksPush.get(ssoAccount.name));
callbacksPush.put(ssoAccount.name, new ArrayList<>());
if (!onlyLocalChanges) {
syncTask.addCallbacks(ssoAccount, callbacksPull.get(ssoAccount.name));
callbacksPull.put(ssoAccount.name, new ArrayList<>());
}
syncTask.execute();
} else if (!onlyLocalChanges) {
Log.d(TAG, "... scheduled");
syncScheduled.put(ssoAccount.name, true);
if (callbacksPush.containsKey(ssoAccount.name) && callbacksPush.get(ssoAccount.name) != null) {
final List<ISyncCallback> callbacks = callbacksPush.get(ssoAccount.name);
syncScheduled.put(account.getId(), true);
if (callbacksPush.containsKey(account.getId()) && callbacksPush.get(account.getId()) != null) {
final List<ISyncCallback> callbacks = callbacksPush.get(account.getId());
if (callbacks != null) {
for (ISyncCallback callback : callbacks) {
callback.onScheduled();
}
} else {
Log.w(TAG, "List of push-callbacks was set for account \"" + ssoAccount.name + "\" but it was null");
Log.w(TAG, "List of push-callbacks was set for account \"" + account.getAccountName() + "\" but it was null");
}
}
} else {
Log.d(TAG, "... do nothing");
if (callbacksPush.containsKey(ssoAccount.name) && callbacksPush.get(ssoAccount.name) != null) {
final List<ISyncCallback> callbacks = callbacksPush.get(ssoAccount.name);
if (callbacksPush.containsKey(account.getId()) && callbacksPush.get(account.getId()) != null) {
final List<ISyncCallback> callbacks = callbacksPush.get(account.getId());
if (callbacks != null) {
for (ISyncCallback callback : callbacks) {
callback.onScheduled();
}
} else {
Log.w(TAG, "List of push-callbacks was set for account \"" + ssoAccount.name + "\" but it was null");
Log.w(TAG, "List of push-callbacks was set for account \"" + account.getAccountName() + "\" but it was null");
}
}
}
@ -312,7 +316,7 @@ public class NoteServerSyncHelper {
private final SingleSignOnAccount ssoAccount;
private final boolean onlyLocalChanges;
@NonNull
private final Map<String, List<ISyncCallback>> callbacks = new HashMap<>();
private final Map<Long, List<ISyncCallback>> callbacks = new HashMap<>();
@NonNull
private final ArrayList<Throwable> exceptions = new ArrayList<>();
@ -323,21 +327,21 @@ public class NoteServerSyncHelper {
this.onlyLocalChanges = onlyLocalChanges;
}
private void addCallbacks(SingleSignOnAccount ssoAccount, List<ISyncCallback> callbacks) {
this.callbacks.put(ssoAccount.name, callbacks);
private void addCallbacks(Account account, List<ISyncCallback> callbacks) {
this.callbacks.put(account.getId(), callbacks);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
syncStatus.postValue(true);
if (!syncScheduled.containsKey(ssoAccount.name) || syncScheduled.get(ssoAccount.name) == null) {
syncScheduled.put(ssoAccount.name, false);
if (!syncScheduled.containsKey(localAccount.getId()) || syncScheduled.get(localAccount.getId()) == null) {
syncScheduled.put(localAccount.getId(), false);
}
if (!onlyLocalChanges && Boolean.TRUE.equals(syncScheduled.get(ssoAccount.name))) {
syncScheduled.put(ssoAccount.name, false);
if (!onlyLocalChanges && Boolean.TRUE.equals(syncScheduled.get(localAccount.getId()))) {
syncScheduled.put(localAccount.getId(), false);
}
syncActive.put(ssoAccount.name, true);
syncActive.put(localAccount.getId(), true);
}
@Override
@ -504,18 +508,18 @@ public class NoteServerSyncHelper {
if (!status.pullSuccessful || !status.pushSuccessful) {
syncErrors.postValue(exceptions);
}
syncActive.put(ssoAccount.name, false);
syncActive.put(localAccount.getId(), false);
// notify callbacks
if (callbacks.containsKey(ssoAccount.name) && callbacks.get(ssoAccount.name) != null) {
for (ISyncCallback callback : Objects.requireNonNull(callbacks.get(ssoAccount.name))) {
if (callbacks.containsKey(localAccount.getId()) && callbacks.get(localAccount.getId()) != null) {
for (ISyncCallback callback : Objects.requireNonNull(callbacks.get(localAccount.getId()))) {
callback.onFinish();
}
}
db.notifyWidgets();
db.updateDynamicShortcuts(localAccount.getId());
// start next sync if scheduled meanwhile
if (syncScheduled.containsKey(ssoAccount.name) && syncScheduled.get(ssoAccount.name) != null && Boolean.TRUE.equals(syncScheduled.get(ssoAccount.name))) {
scheduleSync(ssoAccount, false);
if (syncScheduled.containsKey(localAccount.getId()) && syncScheduled.get(localAccount.getId()) != null && Boolean.TRUE.equals(syncScheduled.get(localAccount.getId()))) {
scheduleSync(localAccount, false);
}
syncStatus.postValue(false);
}

View file

@ -161,13 +161,13 @@ public abstract class NotesDatabase extends RoomDatabase {
*/
@NonNull
@MainThread
public LiveData<NoteWithCategory> addNoteAndSync(SingleSignOnAccount ssoAccount, long accountId, NoteWithCategory note) {
NoteWithCategory entity = new NoteWithCategory(new Note(0, null, note.getModified(), note.getTitle(), note.getContent(), note.getFavorite(), note.getETag(), DBStatus.LOCAL_EDITED, accountId, generateNoteExcerpt(note.getContent(), note.getTitle()), 0), note.getCategory());
public LiveData<NoteWithCategory> addNoteAndSync(Account account, NoteWithCategory note) {
NoteWithCategory entity = new NoteWithCategory(new Note(0, null, note.getModified(), note.getTitle(), note.getContent(), note.getFavorite(), note.getETag(), DBStatus.LOCAL_EDITED, account.getId(), generateNoteExcerpt(note.getContent(), note.getTitle()), 0), note.getCategory());
final MutableLiveData<NoteWithCategory> ret = new MutableLiveData<>();
new Thread(() -> ret.postValue(addNote(accountId, entity))).start();
new Thread(() -> ret.postValue(addNote(account.getId(), entity))).start();
return map(ret, newNoteWithCategory -> {
notifyWidgets();
serverSyncHelper.scheduleSync(ssoAccount, true);
serverSyncHelper.scheduleSync(account, true);
return newNoteWithCategory;
});
}
@ -207,10 +207,10 @@ public abstract class NotesDatabase extends RoomDatabase {
}
@AnyThread
public LiveData<NoteWithCategory> moveNoteToAnotherAccount(SingleSignOnAccount ssoAccount, NoteWithCategory note, long newAccountId) {
public LiveData<NoteWithCategory> moveNoteToAnotherAccount(Account account, NoteWithCategory note) {
NoteWithCategory noteWithCategory = new NoteWithCategory(new Note(null, note.getModified(), note.getTitle(), getNoteDao().getContent(note.getId()), note.getFavorite(), null), note.getCategory());
deleteNoteAndSync(ssoAccount, note.getId());
return addNoteAndSync(ssoAccount, newAccountId, noteWithCategory);
deleteNoteAndSync(account, note.getId());
return addNoteAndSync(account, noteWithCategory);
}
@NonNull
@ -225,10 +225,10 @@ public abstract class NotesDatabase extends RoomDatabase {
}
@AnyThread
public void toggleFavoriteAndSync(SingleSignOnAccount ssoAccount, long noteId) {
public void toggleFavoriteAndSync(Account account, long noteId) {
new Thread(() -> {
getNoteDao().toggleFavorite(noteId);
serverSyncHelper.scheduleSync(ssoAccount, true);
serverSyncHelper.scheduleSync(account, true);
}).start();
}
@ -237,17 +237,17 @@ public abstract class NotesDatabase extends RoomDatabase {
* This method will search in the database to find out the category id in the db.
* If there is no such category existing, this method will create it and search again.
*
* @param ssoAccount The single sign on account
* @param account The single sign on account
* @param accountId The account where the note is
* @param noteId The note which will be updated
* @param category The category title which should be used to find the category id.
*/
@AnyThread
public void setCategory(SingleSignOnAccount ssoAccount, long accountId, long noteId, @NonNull String category) {
public void setCategory(@NonNull Account account, long noteId, @NonNull String category) {
new Thread(() -> {
getNoteDao().updateStatus(noteId, DBStatus.LOCAL_EDITED);
getNoteDao().updateCategory(noteId, getOrCreateCategoryIdByTitle(accountId, category));
serverSyncHelper.scheduleSync(ssoAccount, true);
getNoteDao().updateCategory(noteId, getOrCreateCategoryIdByTitle(account.getId(), category));
serverSyncHelper.scheduleSync(account, true);
}).start();
}
@ -262,7 +262,7 @@ public abstract class NotesDatabase extends RoomDatabase {
* @return changed {@link Note} if differs from database, otherwise the old {@link Note}.
*/
@WorkerThread
public NoteWithCategory updateNoteAndSync(SingleSignOnAccount ssoAccount, @NonNull Account localAccount, @NonNull NoteWithCategory oldNote, @Nullable String newContent, @Nullable String newTitle, @Nullable ISyncCallback callback) {
public NoteWithCategory updateNoteAndSync(Account localAccount, @NonNull NoteWithCategory oldNote, @Nullable String newContent, @Nullable String newTitle, @Nullable ISyncCallback callback) {
final NoteWithCategory newNote;
if (newContent == null) {
newNote = new NoteWithCategory(new Note(oldNote.getId(), oldNote.getRemoteId(), oldNote.getModified(), oldNote.getTitle(), oldNote.getContent(), oldNote.getFavorite(), oldNote.getETag(), DBStatus.LOCAL_EDITED, localAccount.getId(), oldNote.getExcerpt(), oldNote.getScrollY()), oldNote.getCategory());
@ -285,9 +285,9 @@ public abstract class NotesDatabase extends RoomDatabase {
if (rows > 0) {
notifyWidgets();
if (callback != null) {
serverSyncHelper.addCallbackPush(ssoAccount, callback);
serverSyncHelper.addCallbackPush(localAccount, callback);
}
serverSyncHelper.scheduleSync(ssoAccount, true);
serverSyncHelper.scheduleSync(localAccount, true);
return newNote;
} else {
if (callback != null) {
@ -304,11 +304,11 @@ public abstract class NotesDatabase extends RoomDatabase {
* @param id long - ID of the Note that should be deleted
*/
@AnyThread
public void deleteNoteAndSync(SingleSignOnAccount ssoAccount, long id) {
public void deleteNoteAndSync(Account account, long id) {
new Thread(() -> {
getNoteDao().updateStatus(id, DBStatus.LOCAL_DELETED);
notifyWidgets();
serverSyncHelper.scheduleSync(ssoAccount, true);
serverSyncHelper.scheduleSync(account, true);
if (SDK_INT >= O) {
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);

View file

@ -12,10 +12,6 @@ import androidx.work.WorkManager;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import com.nextcloud.android.sso.AccountImporter;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
import com.nextcloud.android.sso.model.SingleSignOnAccount;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@ -40,14 +36,9 @@ public class SyncWorker extends Worker {
public Result doWork() {
NotesDatabase db = NotesDatabase.getInstance(getApplicationContext());
for (Account account : db.getAccountDao().getAccounts()) {
try {
SingleSignOnAccount ssoAccount = AccountImporter.getSingleSignOnAccount(getApplicationContext(), account.getAccountName());
Log.v(TAG, "Starting background synchronization for " + ssoAccount.name);
db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, () -> Log.v(TAG, "Finished background synchronization for " + ssoAccount.name));
db.getNoteServerSyncHelper().scheduleSync(ssoAccount, false);
} catch (NextcloudFilesAppAccountNotFoundException e) {
e.printStackTrace();
}
Log.v(TAG, "Starting background synchronization for " + account.getAccountName());
db.getNoteServerSyncHelper().addCallbackPull(account, () -> Log.v(TAG, "Finished background synchronization for " + account.getAccountName()));
db.getNoteServerSyncHelper().scheduleSync(account, false);
}
// TODO return result depending on callbackPull
return Result.success();