From 393d35e103be98a372e98446f8d0b71d31a79b30 Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Sat, 14 Dec 2019 14:37:22 +0100 Subject: SyncWorker for background synchronization --- .../owncloud/notes/persistence/SyncWorker.java | 85 ++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java (limited to 'app/src') diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java new file mode 100644 index 00000000..9e18b606 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java @@ -0,0 +1,85 @@ +package it.niedermann.owncloud.notes.persistence; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.Constraints; +import androidx.work.NetworkType; +import androidx.work.WorkManager; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import it.niedermann.owncloud.notes.util.ICallback; + +public class SyncWorker extends Worker { + + private static final String WORKER_TAG = "background_synchronization"; + private static final String TAG = SyncWorker.class.getCanonicalName(); + private static final Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); + + public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + synchronized public Result doWork() { + Log.v(TAG, "Starting background synchronization"); + NoteSQLiteOpenHelper db = NoteSQLiteOpenHelper.getInstance(getApplicationContext()); + db.getNoteServerSyncHelper().addCallbackPull(new ICallback() { + @Override + synchronized public void onFinish() { + SyncWorker.this.notify(); + } + + @Override + public void onScheduled() { + + } + }); + + db.getNoteServerSyncHelper().scheduleSync(false); + + try { + wait(); + Log.v(TAG, "Finished background synchronization"); + return Result.success(); + } catch (InterruptedException e) { + e.printStackTrace(); + return Result.failure(); + } + } + +// public static void update(@NonNull Context context) { +// SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); +// update(context, sharedPreferences.getString(context.getString(R.string.pref_key_background_sync), context.getString(R.string.pref_value_background_15_minutes))); +// } +// +// public static void update(@NonNull Context context, String preferenceValue) { +// if (context.getString(R.string.pref_value_background_sync_off).equals(preferenceValue)) { +// deregister(context); +// } else { +// int repeatInterval = 15; +// TimeUnit unit = TimeUnit.MINUTES; +// if (context.getString(R.string.pref_value_background_1_hour).equals(preferenceValue)) { +// repeatInterval = 1; +// unit = TimeUnit.HOURS; +// } else if (context.getString(R.string.pref_value_background_6_hours).equals(preferenceValue)) { +// repeatInterval = 6; +// unit = TimeUnit.HOURS; +// } +// PeriodicWorkRequest work = new PeriodicWorkRequest.Builder(SyncWorker.class, repeatInterval, unit) +// .setConstraints(constraints).build(); +// Log.v(TAG, "Registering worker running each " + repeatInterval + " " + unit); +// WorkManager.getInstance(context.getApplicationContext()).enqueueUniquePeriodicWork(WORKER_TAG, ExistingPeriodicWorkPolicy.REPLACE, work); +// } +// } + + private static void deregister(@NonNull Context context) { + Log.v(TAG, "Deregistering all workers with tag \"" + WORKER_TAG + "\""); + WorkManager.getInstance(context.getApplicationContext()).cancelAllWorkByTag(WORKER_TAG); + } +} -- cgit v1.2.3 From 2c408e83ddffd97092cce9ca0addad9e6248e52a Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Wed, 29 Jan 2020 19:42:04 +0100 Subject: #656 Background synchronization --- .../android/activity/NotesListViewActivity.java | 3 ++ .../owncloud/notes/persistence/SyncWorker.java | 47 ++++++---------------- 2 files changed, 16 insertions(+), 34 deletions(-) (limited to 'app/src') diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java index d201444a..aedb4b51 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java @@ -68,6 +68,7 @@ import it.niedermann.owncloud.notes.persistence.LoadNotesListTask; import it.niedermann.owncloud.notes.persistence.LoadNotesListTask.NotesLoadedListener; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper; +import it.niedermann.owncloud.notes.persistence.SyncWorker; import it.niedermann.owncloud.notes.util.ExceptionHandler; import it.niedermann.owncloud.notes.util.NoteUtil; @@ -180,6 +181,8 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap setupNavigationList(categoryAdapterSelectedItem); setupNavigationMenu(); setupNotesList(); + + SyncWorker.register(this); } @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java index 9e18b606..707b7fd2 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java @@ -5,20 +5,21 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.work.Constraints; +import androidx.work.ExistingPeriodicWorkPolicy; import androidx.work.NetworkType; +import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; import androidx.work.Worker; import androidx.work.WorkerParameters; -import it.niedermann.owncloud.notes.util.ICallback; +import it.niedermann.owncloud.notes.model.ISyncCallback; + +import static java.util.concurrent.TimeUnit.MINUTES; public class SyncWorker extends Worker { private static final String WORKER_TAG = "background_synchronization"; private static final String TAG = SyncWorker.class.getCanonicalName(); - private static final Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build(); public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); @@ -29,16 +30,11 @@ public class SyncWorker extends Worker { synchronized public Result doWork() { Log.v(TAG, "Starting background synchronization"); NoteSQLiteOpenHelper db = NoteSQLiteOpenHelper.getInstance(getApplicationContext()); - db.getNoteServerSyncHelper().addCallbackPull(new ICallback() { + db.getNoteServerSyncHelper().addCallbackPull(new ISyncCallback() { @Override synchronized public void onFinish() { SyncWorker.this.notify(); } - - @Override - public void onScheduled() { - - } }); db.getNoteServerSyncHelper().scheduleSync(false); @@ -53,30 +49,13 @@ public class SyncWorker extends Worker { } } -// public static void update(@NonNull Context context) { -// SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); -// update(context, sharedPreferences.getString(context.getString(R.string.pref_key_background_sync), context.getString(R.string.pref_value_background_15_minutes))); -// } -// -// public static void update(@NonNull Context context, String preferenceValue) { -// if (context.getString(R.string.pref_value_background_sync_off).equals(preferenceValue)) { -// deregister(context); -// } else { -// int repeatInterval = 15; -// TimeUnit unit = TimeUnit.MINUTES; -// if (context.getString(R.string.pref_value_background_1_hour).equals(preferenceValue)) { -// repeatInterval = 1; -// unit = TimeUnit.HOURS; -// } else if (context.getString(R.string.pref_value_background_6_hours).equals(preferenceValue)) { -// repeatInterval = 6; -// unit = TimeUnit.HOURS; -// } -// PeriodicWorkRequest work = new PeriodicWorkRequest.Builder(SyncWorker.class, repeatInterval, unit) -// .setConstraints(constraints).build(); -// Log.v(TAG, "Registering worker running each " + repeatInterval + " " + unit); -// WorkManager.getInstance(context.getApplicationContext()).enqueueUniquePeriodicWork(WORKER_TAG, ExistingPeriodicWorkPolicy.REPLACE, work); -// } -// } + public static void register(@NonNull Context context) { + Log.v(TAG, "Registering worker running each " + 15 + " " + MINUTES); + WorkManager + .getInstance(context.getApplicationContext()) + .enqueueUniquePeriodicWork(WORKER_TAG, ExistingPeriodicWorkPolicy.REPLACE, new PeriodicWorkRequest.Builder(SyncWorker.class, 15, MINUTES) + .setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.UNMETERED).build()).build()); + } private static void deregister(@NonNull Context context) { Log.v(TAG, "Deregistering all workers with tag \"" + WORKER_TAG + "\""); -- cgit v1.2.3 From 72f53ef7b4b30547507a09323bc8e7e51e7288e4 Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Wed, 29 Jan 2020 20:49:49 +0100 Subject: Disable synchronized return of Result on background sync --- .../owncloud/notes/persistence/SyncWorker.java | 23 ++++------------------ 1 file changed, 4 insertions(+), 19 deletions(-) (limited to 'app/src') diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java index 707b7fd2..9289346f 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java @@ -12,8 +12,6 @@ import androidx.work.WorkManager; import androidx.work.Worker; import androidx.work.WorkerParameters; -import it.niedermann.owncloud.notes.model.ISyncCallback; - import static java.util.concurrent.TimeUnit.MINUTES; public class SyncWorker extends Worker { @@ -27,26 +25,13 @@ public class SyncWorker extends Worker { @NonNull @Override - synchronized public Result doWork() { + public Result doWork() { Log.v(TAG, "Starting background synchronization"); NoteSQLiteOpenHelper db = NoteSQLiteOpenHelper.getInstance(getApplicationContext()); - db.getNoteServerSyncHelper().addCallbackPull(new ISyncCallback() { - @Override - synchronized public void onFinish() { - SyncWorker.this.notify(); - } - }); - + db.getNoteServerSyncHelper().addCallbackPull(() -> Log.v(TAG, "Finished background synchronization")); db.getNoteServerSyncHelper().scheduleSync(false); - - try { - wait(); - Log.v(TAG, "Finished background synchronization"); - return Result.success(); - } catch (InterruptedException e) { - e.printStackTrace(); - return Result.failure(); - } + // TODO return result depending on callbackPull + return Result.success(); } public static void register(@NonNull Context context) { -- cgit v1.2.3 From 37795f5cdff64728d54e7e54199ed351c67ef903 Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Wed, 29 Jan 2020 22:38:55 +0100 Subject: #656 Background synchronization Make everything from database to sync multi-account-able --- .../android/MultiSelectedActionModeCallback.java | 63 +++++----- .../android/NotesListViewItemTouchHelper.java | 59 ++++++---- .../android/activity/NotesListViewActivity.java | 31 ++--- .../notes/android/fragment/BaseNoteFragment.java | 30 +++-- .../android/fragment/NotePreviewFragment.java | 21 +++- .../notes/persistence/NoteSQLiteOpenHelper.java | 36 +++--- .../notes/persistence/NoteServerSyncHelper.java | 131 +++++++++++---------- .../owncloud/notes/persistence/NotesClient.java | 79 ++++++------- .../owncloud/notes/persistence/SyncWorker.java | 17 ++- 9 files changed, 256 insertions(+), 211 deletions(-) (limited to 'app/src') diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/MultiSelectedActionModeCallback.java b/app/src/main/java/it/niedermann/owncloud/notes/android/MultiSelectedActionModeCallback.java index 46b0fa69..96ccb878 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/MultiSelectedActionModeCallback.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/MultiSelectedActionModeCallback.java @@ -12,6 +12,10 @@ import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.snackbar.Snackbar; +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 java.util.ArrayList; import java.util.List; @@ -70,34 +74,39 @@ public class MultiSelectedActionModeCallback implements Callback { public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case R.id.menu_delete: { - List deletedNotes = new ArrayList<>(); - List selection = adapter.getSelected(); - for (Integer i : selection) { - DBNote note = (DBNote) adapter.getItem(i); - deletedNotes.add(db.getNote(note.getAccountId(), note.getId())); - db.deleteNoteAndSync(note.getId()); + try { + SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context); + List deletedNotes = new ArrayList<>(); + List selection = adapter.getSelected(); + for (Integer i : selection) { + DBNote note = (DBNote) adapter.getItem(i); + deletedNotes.add(db.getNote(note.getAccountId(), note.getId())); + db.deleteNoteAndSync(ssoAccount, note.getId()); + } + mode.finish(); // Action picked, so close the CAB + //after delete selection has to be cleared + searchView.setIconified(true); + refreshLists.run(); + String deletedSnackbarTitle = deletedNotes.size() == 1 + ? context.getString(R.string.action_note_deleted, deletedNotes.get(0).getTitle()) + : context.getString(R.string.bulk_notes_deleted, deletedNotes.size()); + Snackbar.make(viewProvider.getView(), deletedSnackbarTitle, Snackbar.LENGTH_LONG) + .setAction(R.string.action_undo, (View v) -> { + db.getNoteServerSyncHelper().addCallbackPush(ssoAccount, refreshLists::run); + for (DBNote deletedNote : deletedNotes) { + db.addNoteAndSync(ssoAccount, deletedNote.getAccountId(), deletedNote); + } + refreshLists.run(); + String restoreSnackbarTitle = deletedNotes.size() == 1 + ? context.getString(R.string.action_note_restored, deletedNotes.get(0).getTitle()) + : context.getString(R.string.bulk_notes_restored, deletedNotes.size()); + Snackbar.make(viewProvider.getView(), restoreSnackbarTitle, Snackbar.LENGTH_SHORT) + .show(); + }) + .show(); + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); } - mode.finish(); // Action picked, so close the CAB - //after delete selection has to be cleared - searchView.setIconified(true); - refreshLists.run(); - String deletedSnackbarTitle = deletedNotes.size() == 1 - ? context.getString(R.string.action_note_deleted, deletedNotes.get(0).getTitle()) - : context.getString(R.string.bulk_notes_deleted, deletedNotes.size()); - Snackbar.make(viewProvider.getView(), deletedSnackbarTitle, Snackbar.LENGTH_LONG) - .setAction(R.string.action_undo, (View v) -> { - db.getNoteServerSyncHelper().addCallbackPush(refreshLists::run); - for (DBNote deletedNote : deletedNotes) { - db.addNoteAndSync(deletedNote.getAccountId(), deletedNote); - } - refreshLists.run(); - String restoreSnackbarTitle = deletedNotes.size() == 1 - ? context.getString(R.string.action_note_restored, deletedNotes.get(0).getTitle()) - : context.getString(R.string.bulk_notes_restored, deletedNotes.size()); - Snackbar.make(viewProvider.getView(), restoreSnackbarTitle, Snackbar.LENGTH_SHORT) - .show(); - }) - .show(); return true; } case R.id.menu_move: { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java index 63de4b70..ba239af4 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java @@ -10,13 +10,17 @@ import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.snackbar.Snackbar; +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.R; import it.niedermann.owncloud.notes.model.DBNote; +import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.model.ItemAdapter; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper.ViewProvider; -import it.niedermann.owncloud.notes.model.ISyncCallback; public class NotesListViewItemTouchHelper extends ItemTouchHelper { @@ -57,31 +61,36 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper { */ @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { - switch (direction) { - case ItemTouchHelper.LEFT: { - final DBNote dbNoteWithoutContent = (DBNote) adapter.getItem(viewHolder.getAdapterPosition()); - final DBNote dbNote = db.getNote(dbNoteWithoutContent.getAccountId(), dbNoteWithoutContent.getId()); - db.deleteNoteAndSync(dbNote.getId()); - adapter.remove(dbNote); - refreshLists.run(); - Log.v(TAG, "Item deleted through swipe ----------------------------------------------"); - Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_deleted, dbNote.getTitle()), Snackbar.LENGTH_LONG) - .setAction(R.string.action_undo, (View v) -> { - db.getNoteServerSyncHelper().addCallbackPush(refreshLists::run); - db.addNoteAndSync(dbNote.getAccountId(), dbNote); - refreshLists.run(); - Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_restored, dbNote.getTitle()), Snackbar.LENGTH_SHORT) - .show(); - }) - .show(); - break; - } - case ItemTouchHelper.RIGHT: { - final DBNote dbNote = (DBNote) adapter.getItem(viewHolder.getAdapterPosition()); - db.toggleFavorite(dbNote, syncCallBack); - refreshLists.run(); - break; + try { + SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context); + switch (direction) { + case ItemTouchHelper.LEFT: { + final DBNote dbNoteWithoutContent = (DBNote) adapter.getItem(viewHolder.getAdapterPosition()); + final DBNote dbNote = db.getNote(dbNoteWithoutContent.getAccountId(), dbNoteWithoutContent.getId()); + db.deleteNoteAndSync(ssoAccount, dbNote.getId()); + adapter.remove(dbNote); + refreshLists.run(); + Log.v(TAG, "Item deleted through swipe ----------------------------------------------"); + Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_deleted, dbNote.getTitle()), Snackbar.LENGTH_LONG) + .setAction(R.string.action_undo, (View v) -> { + db.getNoteServerSyncHelper().addCallbackPush(ssoAccount, refreshLists::run); + db.addNoteAndSync(ssoAccount, dbNote.getAccountId(), dbNote); + refreshLists.run(); + Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_restored, dbNote.getTitle()), Snackbar.LENGTH_SHORT) + .show(); + }) + .show(); + break; + } + case ItemTouchHelper.RIGHT: { + final DBNote dbNote = (DBNote) adapter.getItem(viewHolder.getAdapterPosition()); + db.toggleFavorite(ssoAccount, dbNote, syncCallBack); + refreshLists.run(); + break; + } } + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java index aedb4b51..79a68891 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java @@ -99,6 +99,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap */ private boolean notAuthorizedAccountHandled = false; + private SingleSignOnAccount ssoAccount; private LocalAccount localAccount; @BindView(R.id.coordinatorLayout) @@ -188,9 +189,9 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap @Override protected void onResume() { try { - String ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()).name; - if (localAccount == null || !localAccount.getAccountName().equals(ssoAccount)) { - selectAccount(SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()).name); + ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()); + if (localAccount == null || !localAccount.getAccountName().equals(ssoAccount.name)) { + selectAccount(ssoAccount.name); } } catch (NoCurrentAccountSelectedException | NextcloudFilesAppAccountNotFoundException e) { if (!notAuthorizedAccountHandled) { @@ -201,7 +202,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap // refresh and sync every time the activity gets if (localAccount != null) { refreshLists(); - db.getNoteServerSyncHelper().addCallbackPull(syncCallBack); + db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, syncCallBack); if (db.getNoteServerSyncHelper().isSyncPossible()) { synchronize(); } @@ -234,18 +235,18 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap private void selectAccount(String accountName) { fabCreate.hide(); SingleAccountHelper.setCurrentAccount(getApplicationContext(), accountName); - localAccount = db.getLocalAccountByAccountName(accountName); try { - db.getNoteServerSyncHelper().updateAccount(); + ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()); + localAccount = db.getLocalAccountByAccountName(accountName); synchronize(); refreshLists(); fabCreate.show(); - } catch (NextcloudFilesAppAccountNotFoundException e) { - handleNotAuthorizedAccount(); + setupHeader(); + setupNavigationList(ADAPTER_KEY_RECENT); + updateUsernameInDrawer(); + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); } - setupHeader(); - setupNavigationList(ADAPTER_KEY_RECENT); - updateUsernameInDrawer(); } private void handleNotAuthorizedAccount() { @@ -776,7 +777,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap public void onNoteFavoriteClick(int position, View view) { DBNote note = (DBNote) adapter.getItem(position); NoteSQLiteOpenHelper db = NoteSQLiteOpenHelper.getInstance(view.getContext()); - db.toggleFavorite(note, syncCallBack); + db.toggleFavorite(ssoAccount, note, syncCallBack); adapter.notifyItemChanged(position); refreshLists(); } @@ -806,8 +807,8 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap private void synchronize() { swipeRefreshLayout.setRefreshing(true); - db.getNoteServerSyncHelper().addCallbackPull(syncCallBack); - db.getNoteServerSyncHelper().scheduleSync(false); + db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, syncCallBack); + db.getNoteServerSyncHelper().scheduleSync(ssoAccount, false); } @Override @@ -817,7 +818,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap adapter.deselect(0); for (Integer i : selection) { DBNote note = (DBNote) adapter.getItem(i); - db.moveNoteToAnotherAccount(note.getAccountId(), db.getNote(note.getAccountId(), note.getId()), account.getId()); + db.moveNoteToAnotherAccount(ssoAccount, note.getAccountId(), db.getNote(note.getAccountId(), note.getId()), account.getId()); RecyclerView.ViewHolder viewHolder = listView.findViewHolderForAdapterPosition(i); if (viewHolder != null) { viewHolder.itemView.setSelected(false); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java index 529e5c03..2f42bc8d 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java @@ -24,6 +24,7 @@ import androidx.fragment.app.FragmentManager; 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 java.util.Objects; @@ -33,9 +34,9 @@ import it.niedermann.owncloud.notes.android.fragment.CategoryDialogFragment.Cate import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.DBStatus; +import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; -import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.util.NoteUtil; import static androidx.core.content.pm.ShortcutManagerCompat.isRequestPinShortcutSupported; @@ -54,6 +55,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo private static final String SAVEDKEY_ORIGINAL_NOTE = "original_note"; private LocalAccount localAccount; + private SingleSignOnAccount ssoAccount; protected DBNote note; @Nullable @@ -67,7 +69,8 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { - this.localAccount = db.getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(getActivity().getApplicationContext()).name); + this.ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getActivity().getApplicationContext()); + this.localAccount = db.getLocalAccountByAccountName(ssoAccount.name); if (savedInstanceState == null) { long id = getArguments().getLong(PARAM_NOTE_ID); @@ -77,11 +80,6 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo /* Switch account if account id has been provided */ this.localAccount = db.getAccount(accountId); SingleAccountHelper.setCurrentAccount(getActivity().getApplicationContext(), localAccount.getAccountName()); - try { - db.getNoteServerSyncHelper().updateAccount(); - } catch (NextcloudFilesAppAccountNotFoundException e) { - e.printStackTrace(); - } } isNew = false; note = originalNote = db.getNote(localAccount.getId(), id); @@ -95,7 +93,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo note = new DBNote(-1, -1, null, NoteUtil.generateNoteTitle(content), content, false, getString(R.string.category_readonly), null, DBStatus.VOID, -1, ""); } } else { - note = db.getNote(localAccount.getId(), db.addNoteAndSync(localAccount.getId(), cloudNote)); + note = db.getNote(localAccount.getId(), db.addNoteAndSync(ssoAccount, localAccount.getId(), cloudNote)); originalNote = null; } } @@ -177,18 +175,18 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo switch (item.getItemId()) { case R.id.menu_cancel: if (originalNote == null) { - db.deleteNoteAndSync(note.getId()); + db.deleteNoteAndSync(ssoAccount, note.getId()); } else { - db.updateNoteAndSync(localAccount.getId(), originalNote, null, null); + db.updateNoteAndSync(ssoAccount, localAccount.getId(), originalNote, null, null); } listener.close(); return true; case R.id.menu_delete: - db.deleteNoteAndSync(note.getId()); + db.deleteNoteAndSync(ssoAccount, note.getId()); listener.close(); return true; case R.id.menu_favorite: - db.toggleFavorite(note, null); + db.toggleFavorite(ssoAccount, note, null); listener.onNoteUpdated(note); prepareFavoriteOption(item); return true; @@ -248,7 +246,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo public void onCloseNote() { if (originalNote == null && getContent().isEmpty()) { - db.deleteNoteAndSync(note.getId()); + db.deleteNoteAndSync(ssoAccount, note.getId()); } } @@ -264,7 +262,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo if (note.getContent().equals(newContent)) { Log.v(TAG, "... not saving, since nothing has changed"); } else { - note = db.updateNoteAndSync(localAccount.getId(), note, newContent, callback); + note = db.updateNoteAndSync(ssoAccount, localAccount.getId(), note, newContent, callback); listener.onNoteUpdated(note); } } else { @@ -310,12 +308,12 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo @Override public void onCategoryChosen(String category) { - db.setCategory(note, category, null); + db.setCategory(ssoAccount, note, category, null); listener.onNoteUpdated(note); } public void moveNote(LocalAccount account) { - db.moveNoteToAnotherAccount(note.getAccountId(), note, account.getId()); + db.moveNoteToAnotherAccount(ssoAccount, note.getAccountId(), note, account.getId()); listener.close(); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java index 5cdeb25f..d2a5f288 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java @@ -26,6 +26,10 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener; import com.google.android.material.floatingactionbutton.FloatingActionButton; +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 com.yydcdut.markdown.MarkdownProcessor; import com.yydcdut.markdown.MarkdownTextView; import com.yydcdut.markdown.syntax.text.TextFactory; @@ -208,12 +212,17 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O public void onRefresh() { if (db.getNoteServerSyncHelper().isSyncPossible()) { swipeRefreshLayout.setRefreshing(true); - db.getNoteServerSyncHelper().addCallbackPull(() -> { - note = db.getNote(note.getAccountId(), note.getId()); - noteContent.setText(markdownProcessor.parse(NoteLinksUtils.replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId())))); - swipeRefreshLayout.setRefreshing(false); - }); - db.getNoteServerSyncHelper().scheduleSync(false); + try { + SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getContext()); + db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, () -> { + note = db.getNote(note.getAccountId(), note.getId()); + noteContent.setText(markdownProcessor.parse(NoteLinksUtils.replaceNoteLinksWithDummyUrls(note.getContent(), db.getRemoteIds(note.getAccountId())))); + swipeRefreshLayout.setRefreshing(false); + }); + db.getNoteServerSyncHelper().scheduleSync(ssoAccount, false); + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); + } } else { swipeRefreshLayout.setRefreshing(false); Toast.makeText(getContext(), getString(R.string.error_sync, getString(LoginStatus.NO_NETWORK.str)), Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java index c0562c73..be6782a5 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java @@ -24,6 +24,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import com.nextcloud.android.sso.model.SingleSignOnAccount; + import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -318,11 +320,11 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { * * @param note Note */ - public long addNoteAndSync(long accountId, CloudNote note) { + public long addNoteAndSync(SingleSignOnAccount ssoAccount, long accountId, CloudNote note) { DBNote dbNote = new DBNote(0, 0, note.getModified(), note.getTitle(), note.getContent(), note.isFavorite(), note.getCategory(), note.getEtag(), DBStatus.LOCAL_EDITED, accountId, NoteUtil.generateNoteExcerpt(note.getContent())); long id = addNote(accountId, dbNote); notifyNotesChanged(); - getNoteServerSyncHelper().scheduleSync(true); + getNoteServerSyncHelper().scheduleSync(ssoAccount, true); return id; } @@ -360,13 +362,13 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { return db.insert(table_notes, null, values); } - public void moveNoteToAnotherAccount(long oldAccountId, DBNote note, long newAccountId) { + public void moveNoteToAnotherAccount(SingleSignOnAccount ssoAccount, long oldAccountId, DBNote note, long newAccountId) { // Add new note - addNoteAndSync(newAccountId, new CloudNote(0, note.getModified(), note.getTitle(), note.getContent(), note.isFavorite(), note.getCategory(), null)); - deleteNoteAndSync(note.getId()); + addNoteAndSync(ssoAccount, newAccountId, new CloudNote(0, note.getModified(), note.getTitle(), note.getContent(), note.isFavorite(), note.getCategory(), null)); + deleteNoteAndSync(ssoAccount, note.getId()); notifyNotesChanged(); - getNoteServerSyncHelper().scheduleSync(true); + getNoteServerSyncHelper().scheduleSync(ssoAccount,true); } /** @@ -658,7 +660,7 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { return categories; } - public void toggleFavorite(@NonNull DBNote note, @Nullable ISyncCallback callback) { + public void toggleFavorite(SingleSignOnAccount ssoAccount, @NonNull DBNote note, @Nullable ISyncCallback callback) { note.setFavorite(!note.isFavorite()); note.setStatus(DBStatus.LOCAL_EDITED); SQLiteDatabase db = this.getWritableDatabase(); @@ -667,12 +669,12 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { values.put(key_favorite, note.isFavorite() ? "1" : "0"); db.update(table_notes, values, key_id + " = ?", new String[]{String.valueOf(note.getId())}); if (callback != null) { - serverSyncHelper.addCallbackPush(callback); + serverSyncHelper.addCallbackPush(ssoAccount, callback); } - serverSyncHelper.scheduleSync(true); + serverSyncHelper.scheduleSync(ssoAccount, true); } - public void setCategory(@NonNull DBNote note, @NonNull String category, @Nullable ISyncCallback callback) { + public void setCategory(SingleSignOnAccount ssoAccount, @NonNull DBNote note, @NonNull String category, @Nullable ISyncCallback callback) { note.setCategory(category); note.setStatus(DBStatus.LOCAL_EDITED); SQLiteDatabase db = this.getWritableDatabase(); @@ -681,9 +683,9 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { values.put(key_category, note.getCategory()); db.update(table_notes, values, key_id + " = ?", new String[]{String.valueOf(note.getId())}); if (callback != null) { - serverSyncHelper.addCallbackPush(callback); + serverSyncHelper.addCallbackPush(ssoAccount, callback); } - serverSyncHelper.scheduleSync(true); + serverSyncHelper.scheduleSync(ssoAccount,true); } /** @@ -695,7 +697,7 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { * @param callback When the synchronization is finished, this callback will be invoked (optional). * @return changed note if differs from database, otherwise the old note. */ - public DBNote updateNoteAndSync(long accountId, @NonNull DBNote oldNote, @Nullable String newContent, @Nullable ISyncCallback callback) { + public DBNote updateNoteAndSync(SingleSignOnAccount ssoAccount, long accountId, @NonNull DBNote oldNote, @Nullable String newContent, @Nullable ISyncCallback callback) { //debugPrintFullDB(); DBNote newNote; if (newContent == null) { @@ -716,9 +718,9 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { if (rows > 0) { notifyNotesChanged(); if (callback != null) { - serverSyncHelper.addCallbackPush(callback); + serverSyncHelper.addCallbackPush(ssoAccount, callback); } - serverSyncHelper.scheduleSync(true); + serverSyncHelper.scheduleSync(ssoAccount, true); return newNote; } else { if (callback != null) { @@ -781,7 +783,7 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { * * @param id long - ID of the Note that should be deleted */ - public void deleteNoteAndSync(long id) { + public void deleteNoteAndSync(SingleSignOnAccount ssoAccount, long id) { SQLiteDatabase db = this.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(key_status, DBStatus.LOCAL_DELETED.getTitle()); @@ -790,7 +792,7 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { key_id + " = ?", new String[]{String.valueOf(id)}); notifyNotesChanged(); - getNoteServerSyncHelper().scheduleSync(true); + getNoteServerSyncHelper().scheduleSync(ssoAccount, true); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java index ebea9cb9..b68b4b56 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java @@ -25,25 +25,28 @@ import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedExcepti import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; import com.nextcloud.android.sso.helper.SingleAccountHelper; +import com.nextcloud.android.sso.model.SingleSignOnAccount; import org.json.JSONException; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.DBStatus; +import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.model.LoginStatus; import it.niedermann.owncloud.notes.util.ExceptionUtil; -import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.util.ServerResponse; import static android.content.Context.CLIPBOARD_SERVICE; @@ -59,7 +62,6 @@ public class NoteServerSyncHelper { private NoteSQLiteOpenHelper db; private Context context; - private LocalAccount localAccount; // Track network connection changes using a BroadcastReceiver private boolean networkConnected = false; @@ -81,29 +83,29 @@ public class NoteServerSyncHelper { public void onReceive(Context context, Intent intent) { updateNetworkStatus(); if (isSyncPossible()) { - scheduleSync(false); + 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."); + } } } }; // current state of the synchronization - private boolean syncActive = false; - private boolean syncScheduled = false; + private Map syncActive = new HashMap<>(); + private Map syncScheduled = new HashMap<>(); private NotesClient notesClient; // list of callbacks for both parts of synchronziation - private List callbacksPush = new ArrayList<>(); - private List callbacksPull = new ArrayList<>(); + private Map> callbacksPush = new HashMap<>(); + private Map> callbacksPull = new HashMap<>(); private NoteServerSyncHelper(NoteSQLiteOpenHelper db) { this.db = db; this.context = db.getContext(); - try { - updateAccount(); - } catch (NextcloudFilesAppAccountNotFoundException e) { - e.printStackTrace(); - } + notesClient = new NotesClient(context.getApplicationContext()); this.syncOnlyOnWifiKey = context.getApplicationContext().getResources().getString(R.string.pref_key_wifi_only); // Registers BroadcastReceiver to track network connection changes. @@ -131,23 +133,6 @@ public class NoteServerSyncHelper { return instance; } - public void updateAccount() throws NextcloudFilesAppAccountNotFoundException { - try { - this.localAccount = db.getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(context.getApplicationContext()).name); - if (notesClient == null) { - if (this.localAccount != null) { - notesClient = new NotesClient(context.getApplicationContext()); - } - } else { - notesClient.updateAccount(); - } - Log.v(TAG, "NextcloudRequest account: " + localAccount); - } catch (NoCurrentAccountSelectedException e) { - e.printStackTrace(); - } - Log.v(TAG, "Reinstanziation NotesClient because of SSO acc changed"); - } - @Override protected void finalize() throws Throwable { context.getApplicationContext().unregisterReceiver(networkReceiver); @@ -185,8 +170,11 @@ public class NoteServerSyncHelper { * * @param callback Implementation of ISyncCallback, contains one method that shall be executed. */ - public void addCallbackPush(ISyncCallback callback) { - callbacksPush.add(callback); + public void addCallbackPush(SingleSignOnAccount ssoAccount, ISyncCallback callback) { + if (!callbacksPush.containsKey(ssoAccount)) { + callbacksPush.put(ssoAccount, new ArrayList<>()); + } + Objects.requireNonNull(callbacksPush.get(ssoAccount)).add(callback); } /** @@ -197,8 +185,11 @@ public class NoteServerSyncHelper { * * @param callback Implementation of ISyncCallback, contains one method that shall be executed. */ - public void addCallbackPull(ISyncCallback callback) { - callbacksPull.add(callback); + public void addCallbackPull(SingleSignOnAccount ssoAccount, ISyncCallback callback) { + if (!callbacksPull.containsKey(ssoAccount)) { + callbacksPull.put(ssoAccount, new ArrayList<>()); + } + Objects.requireNonNull(callbacksPull.get(ssoAccount)).add(callback); } @@ -208,28 +199,35 @@ 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(boolean onlyLocalChanges) { - Log.d(TAG, "Sync requested (" + (onlyLocalChanges ? "onlyLocalChanges" : "full") + "; " + (syncActive ? "sync active" : "sync NOT active") + ") ..."); - if (isSyncPossible() && (!syncActive || onlyLocalChanges)) { + public void scheduleSync(SingleSignOnAccount ssoAccount, boolean onlyLocalChanges) { + if (syncActive.get(ssoAccount) == null) { + syncActive.put(ssoAccount, false); + } + Log.d(TAG, "Sync requested (" + (onlyLocalChanges ? "onlyLocalChanges" : "full") + "; " + (syncActive.get(ssoAccount) ? "sync active" : "sync NOT active") + ") ..."); + if (isSyncPossible() && (!syncActive.get(ssoAccount) || onlyLocalChanges)) { Log.d(TAG, "... starting now"); - SyncTask syncTask = new SyncTask(onlyLocalChanges); - syncTask.addCallbacks(callbacksPush); - callbacksPush = new ArrayList<>(); + SyncTask syncTask = new SyncTask(db.getLocalAccountByAccountName(ssoAccount.name), ssoAccount, onlyLocalChanges); + syncTask.addCallbacks(ssoAccount, callbacksPush.get(ssoAccount)); + callbacksPush.put(ssoAccount, new ArrayList<>()); if (!onlyLocalChanges) { - syncTask.addCallbacks(callbacksPull); - callbacksPull = new ArrayList<>(); + syncTask.addCallbacks(ssoAccount, callbacksPull.get(ssoAccount)); + callbacksPull.put(ssoAccount, new ArrayList<>()); } syncTask.execute(); } else if (!onlyLocalChanges) { Log.d(TAG, "... scheduled"); - syncScheduled = true; - for (ISyncCallback callback : callbacksPush) { - callback.onScheduled(); + syncScheduled.put(ssoAccount, true); + if(callbacksPush.containsKey(ssoAccount) && callbacksPush.get(ssoAccount) != null) { + for (ISyncCallback callback : callbacksPush.get(ssoAccount)) { + callback.onScheduled(); + } } } else { Log.d(TAG, "... do nothing"); - for (ISyncCallback callback : callbacksPush) { - callback.onScheduled(); + if(callbacksPull.containsKey(ssoAccount) && callbacksPull.get(ssoAccount) != null) { + for (ISyncCallback callback : callbacksPush.get(ssoAccount)) { + callback.onScheduled(); + } } } } @@ -260,25 +258,32 @@ public class NoteServerSyncHelper { * Synchronization consists of two parts: pushLocalChanges and pullRemoteChanges. */ private class SyncTask extends AsyncTask { + private final LocalAccount localAccount; + private final SingleSignOnAccount ssoAccount; private final boolean onlyLocalChanges; - private final List callbacks = new ArrayList<>(); + private final Map> callbacks = new HashMap<>(); private List exceptions = new ArrayList<>(); - SyncTask(boolean onlyLocalChanges) { + SyncTask(LocalAccount localAccount, SingleSignOnAccount ssoAccount, boolean onlyLocalChanges) { + this.localAccount = localAccount; + this.ssoAccount = ssoAccount; this.onlyLocalChanges = onlyLocalChanges; } - void addCallbacks(List callbacks) { - this.callbacks.addAll(callbacks); + void addCallbacks(SingleSignOnAccount ssoAccount, List callbacks) { + this.callbacks.put(ssoAccount, callbacks); } @Override protected void onPreExecute() { super.onPreExecute(); - if (!onlyLocalChanges && syncScheduled) { - syncScheduled = false; + if (!syncScheduled.containsKey(ssoAccount) || syncScheduled.get(ssoAccount) == null) { + syncScheduled.put(ssoAccount, false); } - syncActive = true; + if (!onlyLocalChanges && syncScheduled.get(ssoAccount)) { + syncScheduled.put(ssoAccount, false); + } + syncActive.put(ssoAccount, true); } @Override @@ -314,20 +319,20 @@ public class NoteServerSyncHelper { // if note is not new, try to edit it. if (note.getRemoteId() > 0) { Log.v(TAG, " ...try to edit"); - remoteNote = notesClient.editNote(note).getNote(); + remoteNote = notesClient.editNote(ssoAccount, note).getNote(); } // However, the note may be deleted on the server meanwhile; or was never synchronized -> (re)create // Please note, thas db.updateNote() realizes an optimistic conflict resolution, which is required for parallel changes of this Note from the UI. if (remoteNote == null) { Log.v(TAG, " ...Note does not exist on server -> (re)create"); - remoteNote = notesClient.createNote(note).getNote(); + remoteNote = notesClient.createNote(ssoAccount, note).getNote(); } db.updateNote(note.getId(), remoteNote, note); break; case LOCAL_DELETED: if (note.getRemoteId() > 0) { Log.v(TAG, " ...delete (from server and local)"); - notesClient.deleteNote(note.getRemoteId()); + notesClient.deleteNote(ssoAccount, note.getRemoteId()); } else { Log.v(TAG, " ...delete (only local, since it was not synchronized)"); } @@ -360,7 +365,7 @@ public class NoteServerSyncHelper { LoginStatus status; try { Map idMap = db.getIdMap(localAccount.getId()); - ServerResponse.NotesResponse response = notesClient.getNotes(localAccount.getModified(), localAccount.getEtag()); + ServerResponse.NotesResponse response = notesClient.getNotes(ssoAccount, localAccount.getModified(), localAccount.getEtag()); List remoteNotes = response.getNotes(); Set remoteIDs = new HashSet<>(); // pull remote changes: update or create each remote note @@ -453,16 +458,18 @@ public class NoteServerSyncHelper { } } } - syncActive = false; + syncActive.put(ssoAccount, false); // notify callbacks - for (ISyncCallback callback : callbacks) { - callback.onFinish(); + if (callbacks.containsKey(ssoAccount) && callbacks.get(ssoAccount) != null) { + for (ISyncCallback callback : callbacks.get(ssoAccount)) { + callback.onFinish(); + } } db.notifyNotesChanged(); db.updateDynamicShortcuts(localAccount.getId()); // start next sync if scheduled meanwhile - if (syncScheduled) { - scheduleSync(false); + if (syncScheduled.containsKey(ssoAccount) && syncScheduled.get(ssoAccount) != null && syncScheduled.get(ssoAccount)) { + scheduleSync(ssoAccount, false); } } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java index 9c3fda3f..d87b2196 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java @@ -11,10 +11,7 @@ import com.nextcloud.android.sso.aidl.NextcloudRequest; import com.nextcloud.android.sso.api.AidlNetworkRequest; import com.nextcloud.android.sso.api.NextcloudAPI; import com.nextcloud.android.sso.api.Response; -import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedException; -import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; -import com.nextcloud.android.sso.helper.SingleAccountHelper; import com.nextcloud.android.sso.model.SingleSignOnAccount; import org.json.JSONObject; @@ -38,7 +35,7 @@ public class NotesClient { private static final String TAG = NotesClient.class.getSimpleName(); private final Context appContext; - private NextcloudAPI mNextcloudAPI; + private Map mNextcloudAPIs = new HashMap<>(); /** * This entity class is used to return relevant data of the HTTP reponse. @@ -93,45 +90,21 @@ public class NotesClient { NotesClient(Context appContext) { this.appContext = appContext; - updateAccount(); } - void updateAccount() { - if (mNextcloudAPI != null) { - mNextcloudAPI.stop(); - } - try { - SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(appContext); - Log.v(TAG, "NextcloudRequest account: " + ssoAccount.name); - mNextcloudAPI = new NextcloudAPI(appContext, ssoAccount, new GsonBuilder().create(), new NextcloudAPI.ApiConnectedListener() { - @Override - public void onConnected() { - Log.v(TAG, "SSO API connected"); - } - - @Override - public void onError(Exception ex) { - ex.printStackTrace(); - } - }); - } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); - } - } - - NotesResponse getNotes(long lastModified, String lastETag) throws Exception { + NotesResponse getNotes(SingleSignOnAccount ssoAccount, long lastModified, String lastETag) throws Exception { Map parameter = new HashMap<>(); parameter.put(GET_PARAM_KEY_PRUNE_BEFORE, Long.toString(lastModified)); - return new NotesResponse(requestServer("notes", METHOD_GET, parameter, null, lastETag)); + return new NotesResponse(requestServer(ssoAccount, "notes", METHOD_GET, parameter, null, lastETag)); } - private NoteResponse putNote(CloudNote note, String path, String method) throws Exception { + private NoteResponse putNote(SingleSignOnAccount ssoAccount, CloudNote note, String path, String method) throws Exception { JSONObject paramObject = new JSONObject(); paramObject.accumulate(JSON_CONTENT, note.getContent()); paramObject.accumulate(JSON_MODIFIED, note.getModified().getTimeInMillis() / 1000); paramObject.accumulate(JSON_FAVORITE, note.isFavorite()); paramObject.accumulate(JSON_CATEGORY, note.getCategory()); - return new NoteResponse(requestServer(path, method, null, paramObject, null)); + return new NoteResponse(requestServer(ssoAccount, path, method, null, paramObject, null)); } /** @@ -141,17 +114,17 @@ public class NotesClient { * @return Created Note including generated Title, ID and lastModified-Date * @throws Exception */ - NoteResponse createNote(CloudNote note) throws Exception { - return putNote(note, "notes", METHOD_POST); + NoteResponse createNote(SingleSignOnAccount ssoAccount, CloudNote note) throws Exception { + return putNote(ssoAccount, note, "notes", METHOD_POST); } - NoteResponse editNote(CloudNote note) throws Exception { - return putNote(note, "notes/" + note.getRemoteId(), METHOD_PUT); + NoteResponse editNote(SingleSignOnAccount ssoAccount, CloudNote note) throws Exception { + return putNote(ssoAccount, note, "notes/" + note.getRemoteId(), METHOD_PUT); } - void deleteNote(long noteId) { + void deleteNote(SingleSignOnAccount ssoAccount, long noteId) { try { - this.requestServer("notes/" + noteId, METHOD_DELETE, null, null, null); + this.requestServer(ssoAccount, "notes/" + noteId, METHOD_DELETE, null, null, null); } catch (Exception e) { e.printStackTrace(); } @@ -167,7 +140,7 @@ public class NotesClient { * @param lastETag optional ETag of last response * @return Body of answer */ - private ResponseData requestServer(String target, String method, Map parameter, JSONObject requestBody, String lastETag) throws Exception { + private ResponseData requestServer(SingleSignOnAccount ssoAccount, String target, String method, Map parameter, JSONObject requestBody, String lastETag) throws Exception { NextcloudRequest.Builder requestBuilder = new NextcloudRequest.Builder() .setMethod(method) .setUrl(API_PATH + target); @@ -189,9 +162,9 @@ public class NotesClient { StringBuilder result = new StringBuilder(); - Log.v(TAG, "NextcloudRequest: " + nextcloudRequest.toString()); try { - Response response = mNextcloudAPI.performNetworkRequestV2(nextcloudRequest); + Log.v(TAG, ssoAccount.name + " => " + nextcloudRequest.getMethod() + " " + nextcloudRequest.getUrl() + " "); + Response response = getNextcloudAPI(ssoAccount).performNetworkRequestV2(nextcloudRequest); Log.v(TAG, "NextcloudRequest: " + nextcloudRequest.toString()); BufferedReader rd = new BufferedReader(new InputStreamReader(response.getBody())); String line; @@ -223,4 +196,28 @@ public class NotesClient { } } } + + private NextcloudAPI getNextcloudAPI(SingleSignOnAccount ssoAccount) { +// if (mNextcloudAPI != null) { +// mNextcloudAPI.stop(); +// } + if (mNextcloudAPIs.containsKey(ssoAccount)) { + return mNextcloudAPIs.get(ssoAccount); + } else { + Log.v(TAG, "NextcloudRequest account: " + ssoAccount.name); + NextcloudAPI nextcloudAPI = new NextcloudAPI(appContext, ssoAccount, new GsonBuilder().create(), new NextcloudAPI.ApiConnectedListener() { + @Override + public void onConnected() { + Log.v(TAG, "SSO API connected for " + ssoAccount); + } + + @Override + public void onError(Exception ex) { + ex.printStackTrace(); + } + }); + mNextcloudAPIs.put(ssoAccount, nextcloudAPI); + return nextcloudAPI; + } + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java index 9289346f..83b91d9e 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java @@ -12,6 +12,12 @@ 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 it.niedermann.owncloud.notes.model.LocalAccount; + import static java.util.concurrent.TimeUnit.MINUTES; public class SyncWorker extends Worker { @@ -28,8 +34,15 @@ public class SyncWorker extends Worker { public Result doWork() { Log.v(TAG, "Starting background synchronization"); NoteSQLiteOpenHelper db = NoteSQLiteOpenHelper.getInstance(getApplicationContext()); - db.getNoteServerSyncHelper().addCallbackPull(() -> Log.v(TAG, "Finished background synchronization")); - db.getNoteServerSyncHelper().scheduleSync(false); + for (LocalAccount account : db.getAccounts()) { + try { + SingleSignOnAccount ssoAccount = AccountImporter.getSingleSignOnAccount(getApplicationContext(), account.getAccountName()); + db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, () -> Log.v(TAG, "Finished background synchronization")); + db.getNoteServerSyncHelper().scheduleSync(ssoAccount, false); + } catch (NextcloudFilesAppAccountNotFoundException e) { + e.printStackTrace(); + } + } // TODO return result depending on callbackPull return Result.success(); } -- cgit v1.2.3 From a48fc3ce3e295aeb5fa7351bd8acfcfa999e7c59 Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Wed, 29 Jan 2020 23:29:02 +0100 Subject: #656 Background synchronization Use strings as hashmap keys --- .../notes/persistence/NoteServerSyncHelper.java | 68 +++++++++++----------- .../owncloud/notes/persistence/NotesClient.java | 14 ++--- .../owncloud/notes/persistence/SyncWorker.java | 4 +- 3 files changed, 43 insertions(+), 43 deletions(-) (limited to 'app/src') diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java index b68b4b56..84246185 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java @@ -93,13 +93,13 @@ public class NoteServerSyncHelper { }; // current state of the synchronization - private Map syncActive = new HashMap<>(); - private Map syncScheduled = new HashMap<>(); + private Map syncActive = new HashMap<>(); + private Map syncScheduled = new HashMap<>(); private NotesClient notesClient; // list of callbacks for both parts of synchronziation - private Map> callbacksPush = new HashMap<>(); - private Map> callbacksPull = new HashMap<>(); + private Map> callbacksPush = new HashMap<>(); + private Map> callbacksPull = new HashMap<>(); private NoteServerSyncHelper(NoteSQLiteOpenHelper db) { @@ -171,10 +171,10 @@ public class NoteServerSyncHelper { * @param callback Implementation of ISyncCallback, contains one method that shall be executed. */ public void addCallbackPush(SingleSignOnAccount ssoAccount, ISyncCallback callback) { - if (!callbacksPush.containsKey(ssoAccount)) { - callbacksPush.put(ssoAccount, new ArrayList<>()); + if (!callbacksPush.containsKey(ssoAccount.name)) { + callbacksPush.put(ssoAccount.name, new ArrayList<>()); } - Objects.requireNonNull(callbacksPush.get(ssoAccount)).add(callback); + Objects.requireNonNull(callbacksPush.get(ssoAccount.name)).add(callback); } /** @@ -186,10 +186,10 @@ public class NoteServerSyncHelper { * @param callback Implementation of ISyncCallback, contains one method that shall be executed. */ public void addCallbackPull(SingleSignOnAccount ssoAccount, ISyncCallback callback) { - if (!callbacksPull.containsKey(ssoAccount)) { - callbacksPull.put(ssoAccount, new ArrayList<>()); + if (!callbacksPull.containsKey(ssoAccount.name)) { + callbacksPull.put(ssoAccount.name, new ArrayList<>()); } - Objects.requireNonNull(callbacksPull.get(ssoAccount)).add(callback); + Objects.requireNonNull(callbacksPull.get(ssoAccount.name)).add(callback); } @@ -200,32 +200,32 @@ 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 (syncActive.get(ssoAccount) == null) { - syncActive.put(ssoAccount, false); + if (syncActive.get(ssoAccount.name) == null) { + syncActive.put(ssoAccount.name, false); } - Log.d(TAG, "Sync requested (" + (onlyLocalChanges ? "onlyLocalChanges" : "full") + "; " + (syncActive.get(ssoAccount) ? "sync active" : "sync NOT active") + ") ..."); - if (isSyncPossible() && (!syncActive.get(ssoAccount) || onlyLocalChanges)) { + Log.d(TAG, "Sync requested (" + (onlyLocalChanges ? "onlyLocalChanges" : "full") + "; " + (syncActive.get(ssoAccount.name) ? "sync active" : "sync NOT active") + ") ..."); + if (isSyncPossible() && (!syncActive.get(ssoAccount.name) || onlyLocalChanges)) { Log.d(TAG, "... starting now"); SyncTask syncTask = new SyncTask(db.getLocalAccountByAccountName(ssoAccount.name), ssoAccount, onlyLocalChanges); - syncTask.addCallbacks(ssoAccount, callbacksPush.get(ssoAccount)); - callbacksPush.put(ssoAccount, new ArrayList<>()); + syncTask.addCallbacks(ssoAccount, callbacksPush.get(ssoAccount.name)); + callbacksPush.put(ssoAccount.name, new ArrayList<>()); if (!onlyLocalChanges) { - syncTask.addCallbacks(ssoAccount, callbacksPull.get(ssoAccount)); - callbacksPull.put(ssoAccount, new ArrayList<>()); + 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, true); - if(callbacksPush.containsKey(ssoAccount) && callbacksPush.get(ssoAccount) != null) { - for (ISyncCallback callback : callbacksPush.get(ssoAccount)) { + syncScheduled.put(ssoAccount.name, true); + if(callbacksPush.containsKey(ssoAccount.name) && callbacksPush.get(ssoAccount.name) != null) { + for (ISyncCallback callback : callbacksPush.get(ssoAccount.name)) { callback.onScheduled(); } } } else { Log.d(TAG, "... do nothing"); - if(callbacksPull.containsKey(ssoAccount) && callbacksPull.get(ssoAccount) != null) { - for (ISyncCallback callback : callbacksPush.get(ssoAccount)) { + if(callbacksPull.containsKey(ssoAccount.name) && callbacksPull.get(ssoAccount.name) != null) { + for (ISyncCallback callback : callbacksPush.get(ssoAccount.name)) { callback.onScheduled(); } } @@ -261,7 +261,7 @@ public class NoteServerSyncHelper { private final LocalAccount localAccount; private final SingleSignOnAccount ssoAccount; private final boolean onlyLocalChanges; - private final Map> callbacks = new HashMap<>(); + private final Map> callbacks = new HashMap<>(); private List exceptions = new ArrayList<>(); SyncTask(LocalAccount localAccount, SingleSignOnAccount ssoAccount, boolean onlyLocalChanges) { @@ -271,19 +271,19 @@ public class NoteServerSyncHelper { } void addCallbacks(SingleSignOnAccount ssoAccount, List callbacks) { - this.callbacks.put(ssoAccount, callbacks); + this.callbacks.put(ssoAccount.name, callbacks); } @Override protected void onPreExecute() { super.onPreExecute(); - if (!syncScheduled.containsKey(ssoAccount) || syncScheduled.get(ssoAccount) == null) { - syncScheduled.put(ssoAccount, false); + if (!syncScheduled.containsKey(ssoAccount.name) || syncScheduled.get(ssoAccount.name) == null) { + syncScheduled.put(ssoAccount.name, false); } - if (!onlyLocalChanges && syncScheduled.get(ssoAccount)) { - syncScheduled.put(ssoAccount, false); + if (!onlyLocalChanges && syncScheduled.get(ssoAccount.name)) { + syncScheduled.put(ssoAccount.name, false); } - syncActive.put(ssoAccount, true); + syncActive.put(ssoAccount.name, true); } @Override @@ -458,17 +458,17 @@ public class NoteServerSyncHelper { } } } - syncActive.put(ssoAccount, false); + syncActive.put(ssoAccount.name, false); // notify callbacks - if (callbacks.containsKey(ssoAccount) && callbacks.get(ssoAccount) != null) { - for (ISyncCallback callback : callbacks.get(ssoAccount)) { + if (callbacks.containsKey(ssoAccount.name) && callbacks.get(ssoAccount.name) != null) { + for (ISyncCallback callback : callbacks.get(ssoAccount.name)) { callback.onFinish(); } } db.notifyNotesChanged(); db.updateDynamicShortcuts(localAccount.getId()); // start next sync if scheduled meanwhile - if (syncScheduled.containsKey(ssoAccount) && syncScheduled.get(ssoAccount) != null && syncScheduled.get(ssoAccount)) { + if (syncScheduled.containsKey(ssoAccount.name) && syncScheduled.get(ssoAccount.name) != null && syncScheduled.get(ssoAccount.name)) { scheduleSync(ssoAccount, false); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java index d87b2196..2907b323 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java @@ -35,7 +35,7 @@ public class NotesClient { private static final String TAG = NotesClient.class.getSimpleName(); private final Context appContext; - private Map mNextcloudAPIs = new HashMap<>(); + private static Map mNextcloudAPIs = new HashMap<>(); /** * This entity class is used to return relevant data of the HTTP reponse. @@ -45,7 +45,7 @@ public class NotesClient { private final String etag; private final long lastModified; - public ResponseData(String content, String etag, long lastModified) { + ResponseData(String content, String etag, long lastModified) { this.content = content; this.etag = etag; this.lastModified = lastModified; @@ -164,7 +164,7 @@ public class NotesClient { try { Log.v(TAG, ssoAccount.name + " => " + nextcloudRequest.getMethod() + " " + nextcloudRequest.getUrl() + " "); - Response response = getNextcloudAPI(ssoAccount).performNetworkRequestV2(nextcloudRequest); + Response response = getNextcloudAPI(appContext, ssoAccount).performNetworkRequestV2(nextcloudRequest); Log.v(TAG, "NextcloudRequest: " + nextcloudRequest.toString()); BufferedReader rd = new BufferedReader(new InputStreamReader(response.getBody())); String line; @@ -197,12 +197,12 @@ public class NotesClient { } } - private NextcloudAPI getNextcloudAPI(SingleSignOnAccount ssoAccount) { + private static NextcloudAPI getNextcloudAPI(Context appContext, SingleSignOnAccount ssoAccount) { // if (mNextcloudAPI != null) { // mNextcloudAPI.stop(); // } - if (mNextcloudAPIs.containsKey(ssoAccount)) { - return mNextcloudAPIs.get(ssoAccount); + if (mNextcloudAPIs.containsKey(ssoAccount.name)) { + return mNextcloudAPIs.get(ssoAccount.name); } else { Log.v(TAG, "NextcloudRequest account: " + ssoAccount.name); NextcloudAPI nextcloudAPI = new NextcloudAPI(appContext, ssoAccount, new GsonBuilder().create(), new NextcloudAPI.ApiConnectedListener() { @@ -216,7 +216,7 @@ public class NotesClient { ex.printStackTrace(); } }); - mNextcloudAPIs.put(ssoAccount, nextcloudAPI); + mNextcloudAPIs.put(ssoAccount.name, nextcloudAPI); return nextcloudAPI; } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java index 83b91d9e..273e6094 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java @@ -32,12 +32,12 @@ public class SyncWorker extends Worker { @NonNull @Override public Result doWork() { - Log.v(TAG, "Starting background synchronization"); NoteSQLiteOpenHelper db = NoteSQLiteOpenHelper.getInstance(getApplicationContext()); for (LocalAccount account : db.getAccounts()) { try { SingleSignOnAccount ssoAccount = AccountImporter.getSingleSignOnAccount(getApplicationContext(), account.getAccountName()); - db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, () -> Log.v(TAG, "Finished background synchronization")); + 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(); -- cgit v1.2.3 From 3b18b1b94f12339ec9da09e14309c4f64f3521b7 Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Thu, 30 Jan 2020 18:19:48 +0100 Subject: #656 Background synchronization Add preference to UI Signed-off-by: stefan-niedermann --- .../android/activity/NotesListViewActivity.java | 3 -- .../android/fragment/PreferencesFragment.java | 27 +++++++++++------ .../owncloud/notes/persistence/NotesClient.java | 3 -- .../owncloud/notes/persistence/SyncWorker.java | 34 ++++++++++++++++------ .../res/drawable/ic_network_wifi_grey600_24dp.xml | 6 ++++ app/src/main/res/values/arrays.xml | 6 ++++ app/src/main/res/values/strings.xml | 16 ++++++++++ app/src/main/res/xml/preferences.xml | 12 +++++++- 8 files changed, 82 insertions(+), 25 deletions(-) create mode 100644 app/src/main/res/drawable/ic_network_wifi_grey600_24dp.xml (limited to 'app/src') diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java index 79a68891..718194d4 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java @@ -68,7 +68,6 @@ import it.niedermann.owncloud.notes.persistence.LoadNotesListTask; import it.niedermann.owncloud.notes.persistence.LoadNotesListTask.NotesLoadedListener; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper; -import it.niedermann.owncloud.notes.persistence.SyncWorker; import it.niedermann.owncloud.notes.util.ExceptionHandler; import it.niedermann.owncloud.notes.util.NoteUtil; @@ -182,8 +181,6 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap setupNavigationList(categoryAdapterSelectedItem); setupNavigationMenu(); setupNotesList(); - - SyncWorker.register(this); } @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java index 757af1bc..ac24265a 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java @@ -5,11 +5,12 @@ import android.os.Bundle; import android.util.Log; import androidx.annotation.Nullable; -import androidx.preference.Preference; +import androidx.preference.ListPreference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.SwitchPreference; import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.persistence.SyncWorker; import it.niedermann.owncloud.notes.util.Notes; public class PreferencesFragment extends PreferenceFragmentCompat { @@ -25,19 +26,27 @@ public class PreferencesFragment extends PreferenceFragmentCompat { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.preferences); - final SwitchPreference themePref = (SwitchPreference) findPreference(getString(R.string.pref_key_theme)); - themePref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> { - Boolean darkTheme = (Boolean) newValue; - Notes.setAppTheme(darkTheme); + final SwitchPreference themePref = findPreference(getString(R.string.pref_key_theme)); + assert themePref != null; + themePref.setOnPreferenceChangeListener((preference, newValue) -> { + Notes.setAppTheme((Boolean) newValue); getActivity().setResult(Activity.RESULT_OK); getActivity().recreate(); return true; }); - final SwitchPreference wifiOnlyPref = (SwitchPreference) findPreference(getString(R.string.pref_key_wifi_only)); - wifiOnlyPref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> { - Boolean syncOnWifiOnly = (Boolean) newValue; - Log.v(TAG, "syncOnWifiOnly: " + syncOnWifiOnly); + final SwitchPreference wifiOnlyPref = findPreference(getString(R.string.pref_key_wifi_only)); + assert wifiOnlyPref != null; + wifiOnlyPref.setOnPreferenceChangeListener((preference, newValue) -> { + Log.v(TAG, "syncOnWifiOnly: " + newValue); + return true; + }); + + final ListPreference syncPref = findPreference(getString(R.string.pref_key_background_sync)); + assert syncPref != null; + syncPref.setOnPreferenceChangeListener((preference, newValue) -> { + Log.v(TAG, "syncPref: " + preference + " - newValue: " + newValue); + SyncWorker.update(getContext(), newValue.toString()); return true; }); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java index 2907b323..76d2f538 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java @@ -198,9 +198,6 @@ public class NotesClient { } private static NextcloudAPI getNextcloudAPI(Context appContext, SingleSignOnAccount ssoAccount) { -// if (mNextcloudAPI != null) { -// mNextcloudAPI.stop(); -// } if (mNextcloudAPIs.containsKey(ssoAccount.name)) { return mNextcloudAPIs.get(ssoAccount.name); } else { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java index 273e6094..05daacef 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SyncWorker.java @@ -16,14 +16,19 @@ import com.nextcloud.android.sso.AccountImporter; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.model.SingleSignOnAccount; -import it.niedermann.owncloud.notes.model.LocalAccount; +import java.util.concurrent.TimeUnit; -import static java.util.concurrent.TimeUnit.MINUTES; +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.model.LocalAccount; public class SyncWorker extends Worker { - private static final String WORKER_TAG = "background_synchronization"; private static final String TAG = SyncWorker.class.getCanonicalName(); + private static final String WORKER_TAG = "background_synchronization"; + + private static final Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); @@ -47,12 +52,23 @@ public class SyncWorker extends Worker { return Result.success(); } - public static void register(@NonNull Context context) { - Log.v(TAG, "Registering worker running each " + 15 + " " + MINUTES); - WorkManager - .getInstance(context.getApplicationContext()) - .enqueueUniquePeriodicWork(WORKER_TAG, ExistingPeriodicWorkPolicy.REPLACE, new PeriodicWorkRequest.Builder(SyncWorker.class, 15, MINUTES) - .setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.UNMETERED).build()).build()); + public static void update(@NonNull Context context, @NonNull String preferenceValue) { + deregister(context); + if (!context.getString(R.string.pref_value_sync_off).equals(preferenceValue)) { + int repeatInterval = 15; + TimeUnit unit = TimeUnit.MINUTES; + if (context.getString(R.string.pref_value_sync_1_hour).equals(preferenceValue)) { + repeatInterval = 1; + unit = TimeUnit.HOURS; + } else if (context.getString(R.string.pref_value_sync_6_hours).equals(preferenceValue)) { + repeatInterval = 6; + unit = TimeUnit.HOURS; + } + PeriodicWorkRequest work = new PeriodicWorkRequest.Builder(SyncWorker.class, repeatInterval, unit) + .setConstraints(constraints).build(); + WorkManager.getInstance(context.getApplicationContext()).enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, work); + Log.v(TAG, "Registering worker running each " + repeatInterval + " " + unit); + } } private static void deregister(@NonNull Context context) { diff --git a/app/src/main/res/drawable/ic_network_wifi_grey600_24dp.xml b/app/src/main/res/drawable/ic_network_wifi_grey600_24dp.xml new file mode 100644 index 00000000..1c3b9a41 --- /dev/null +++ b/app/src/main/res/drawable/ic_network_wifi_grey600_24dp.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 1d4ed668..cc342086 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -10,4 +10,10 @@ @string/pref_value_font_size_medium @string/pref_value_font_size_large + + @string/pref_value_sync_off + @string/pref_value_sync_15_minutes + @string/pref_value_sync_1_hour + @string/pref_value_sync_6_hours + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 78f63ec4..5ab97e3f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,6 +43,7 @@ Monospace font Font size Sync only on Wi-Fi + Background synchronization @@ -112,15 +113,22 @@ fontSize wifiOnly lastNoteMode + backgroundSync edit preview last small medium large + off + 15_minutes + 1_hour + 6_hours + Light Normal Sync on Wi-Fi and mobile data + Error Close Copy @@ -161,6 +169,14 @@ Large + + + Off + 15 minutes + 1 hour + 6 hours + + %d selected diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index d1668a87..b13422db 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -37,9 +37,19 @@ + + -- cgit v1.2.3 From f6876bc71ab6f69e0eee45564722340dfaaaf5da Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Fri, 31 Jan 2020 08:44:44 +0100 Subject: Enhanced migration from versions < 2.0.0 Signed-off-by: stefan-niedermann --- .../niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/src') diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java index be6782a5..06c5f0ed 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java @@ -201,7 +201,7 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); String username = sharedPreferences.getString("settingsUsername", ""); String url = sharedPreferences.getString("settingsUrl", ""); - if (url != null && url.endsWith("/")) { + if (!url.isEmpty() && url.endsWith("/")) { url = url.substring(0, url.length() - 1); try { String accountName = username + "@" + new URL(url).getHost(); @@ -274,7 +274,7 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { return; } } else { - Log.e(TAG, "Previous URL is null. Recreating database..."); + Log.e(TAG, "Previous URL is empty or does not end with a '/' character. Recreating database..."); recreateDatabase(db); return; } -- cgit v1.2.3 From a1b83d415b027fe25164c26157d0c2a6db766d99 Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Fri, 31 Jan 2020 08:45:15 +0100 Subject: Switch from deprecated singleLine="true" to maxLines="1" Signed-off-by: stefan-niedermann --- app/src/main/res/layout/item_notes_list_note_item.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/src') diff --git a/app/src/main/res/layout/item_notes_list_note_item.xml b/app/src/main/res/layout/item_notes_list_note_item.xml index 19ba07b4..2c6a0eed 100644 --- a/app/src/main/res/layout/item_notes_list_note_item.xml +++ b/app/src/main/res/layout/item_notes_list_note_item.xml @@ -79,7 +79,8 @@ android:id="@+id/noteTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:singleLine="true" + android:ellipsize="end" + android:maxLines="1" android:layout_weight="1" android:textSize="@dimen/primary_font_size" android:textColor="?android:textColorPrimary" -- cgit v1.2.3 From 822e12103953f048edc873996347627ac92bf67f Mon Sep 17 00:00:00 2001 From: stefan-niedermann Date: Fri, 31 Jan 2020 12:53:45 +0100 Subject: #656 Background synchronization Handle sync requests without proper SingleSignOn-Account Signed-off-by: stefan-niedermann --- .../android/NotesListViewItemTouchHelper.java | 57 +++++----- .../notes/android/activity/EditNoteActivity.java | 2 +- .../android/activity/NotesListViewActivity.java | 56 ++++++---- .../android/fragment/NotePreviewFragment.java | 3 +- .../notes/persistence/NoteServerSyncHelper.java | 119 ++++++++++++--------- .../it/niedermann/owncloud/notes/util/SSOUtil.java | 15 +++ 6 files changed, 148 insertions(+), 104 deletions(-) (limited to 'app/src') diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java index ba239af4..3e851ba5 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/NotesListViewItemTouchHelper.java @@ -10,9 +10,6 @@ import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.snackbar.Snackbar; -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.R; @@ -27,6 +24,7 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper { private static final String TAG = NotesListViewItemTouchHelper.class.getCanonicalName(); public NotesListViewItemTouchHelper( + SingleSignOnAccount ssoAccount, Context context, ViewProvider viewProvider, NoteSQLiteOpenHelper db, @@ -61,36 +59,31 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper { */ @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { - try { - SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context); - switch (direction) { - case ItemTouchHelper.LEFT: { - final DBNote dbNoteWithoutContent = (DBNote) adapter.getItem(viewHolder.getAdapterPosition()); - final DBNote dbNote = db.getNote(dbNoteWithoutContent.getAccountId(), dbNoteWithoutContent.getId()); - db.deleteNoteAndSync(ssoAccount, dbNote.getId()); - adapter.remove(dbNote); - refreshLists.run(); - Log.v(TAG, "Item deleted through swipe ----------------------------------------------"); - Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_deleted, dbNote.getTitle()), Snackbar.LENGTH_LONG) - .setAction(R.string.action_undo, (View v) -> { - db.getNoteServerSyncHelper().addCallbackPush(ssoAccount, refreshLists::run); - db.addNoteAndSync(ssoAccount, dbNote.getAccountId(), dbNote); - refreshLists.run(); - Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_restored, dbNote.getTitle()), Snackbar.LENGTH_SHORT) - .show(); - }) - .show(); - break; - } - case ItemTouchHelper.RIGHT: { - final DBNote dbNote = (DBNote) adapter.getItem(viewHolder.getAdapterPosition()); - db.toggleFavorite(ssoAccount, dbNote, syncCallBack); - refreshLists.run(); - break; - } + switch (direction) { + case ItemTouchHelper.LEFT: { + final DBNote dbNoteWithoutContent = (DBNote) adapter.getItem(viewHolder.getAdapterPosition()); + final DBNote dbNote = db.getNote(dbNoteWithoutContent.getAccountId(), dbNoteWithoutContent.getId()); + db.deleteNoteAndSync(ssoAccount, dbNote.getId()); + adapter.remove(dbNote); + refreshLists.run(); + Log.v(TAG, "Item deleted through swipe ----------------------------------------------"); + Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_deleted, dbNote.getTitle()), Snackbar.LENGTH_LONG) + .setAction(R.string.action_undo, (View v) -> { + db.getNoteServerSyncHelper().addCallbackPush(ssoAccount, refreshLists::run); + db.addNoteAndSync(ssoAccount, dbNote.getAccountId(), dbNote); + refreshLists.run(); + Snackbar.make(viewProvider.getView(), context.getString(R.string.action_note_restored, dbNote.getTitle()), Snackbar.LENGTH_SHORT) + .show(); + }) + .show(); + break; + } + case ItemTouchHelper.RIGHT: { + final DBNote dbNote = (DBNote) adapter.getItem(viewHolder.getAdapterPosition()); + db.toggleFavorite(ssoAccount, dbNote, syncCallBack); + refreshLists.run(); + break; } - } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java index 092ede4b..fee85762 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java @@ -265,7 +265,7 @@ public class EditNoteActivity extends AppCompatActivity implements BaseNoteFragm } } else { // Maybe account is not authenticated -> note == null - Log.e(TAG, "note is null, start NotesListViewActivity"); + Log.e(TAG, "note is null, start " + NotesListViewActivity.class.getSimpleName()); startActivity(new Intent(this, NotesListViewActivity.class)); finish(); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java index 718194d4..77333f51 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java @@ -191,18 +191,22 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap selectAccount(ssoAccount.name); } } catch (NoCurrentAccountSelectedException | NextcloudFilesAppAccountNotFoundException e) { + if (localAccount == null) { + List localAccounts = db.getAccounts(); + if (localAccounts.size() > 0) { + localAccount = localAccounts.get(0); + } + } if (!notAuthorizedAccountHandled) { handleNotAuthorizedAccount(); } } // refresh and sync every time the activity gets + refreshLists(); if (localAccount != null) { - refreshLists(); + synchronize(); db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, syncCallBack); - if (db.getNoteServerSyncHelper().isSyncPossible()) { - synchronize(); - } } super.onResume(); } @@ -232,18 +236,19 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap private void selectAccount(String accountName) { fabCreate.hide(); SingleAccountHelper.setCurrentAccount(getApplicationContext(), accountName); + localAccount = db.getLocalAccountByAccountName(accountName); try { ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()); - localAccount = db.getLocalAccountByAccountName(accountName); synchronize(); - refreshLists(); - fabCreate.show(); - setupHeader(); - setupNavigationList(ADAPTER_KEY_RECENT); - updateUsernameInDrawer(); } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); + Log.i(TAG, "Tried to select account, but got an " + e.getClass().getSimpleName() + ". Asking for importing an account..."); + handleNotAuthorizedAccount(); } + refreshLists(); + fabCreate.show(); + setupHeader(); + setupNavigationList(ADAPTER_KEY_RECENT); + updateUsernameInDrawer(); } private void handleNotAuthorizedAccount() { @@ -316,13 +321,12 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap } }); - // Pull to Refresh swipeRefreshLayout.setOnRefreshListener(() -> { - if (db.getNoteServerSyncHelper().isSyncPossible()) { - synchronize(); - } else { + if (ssoAccount == null) { swipeRefreshLayout.setRefreshing(false); - Snackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(LoginStatus.NO_NETWORK.str)), Snackbar.LENGTH_LONG).show(); + askForNewAccount(this); + } else { + synchronize(); } }); @@ -533,7 +537,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap adapter = new ItemAdapter(this); listView.setAdapter(adapter); listView.setLayoutManager(new LinearLayoutManager(this)); - new NotesListViewItemTouchHelper(this, this, db, adapter, syncCallBack, this::refreshLists).attachToRecyclerView(listView); + new NotesListViewItemTouchHelper(ssoAccount, this, this, db, adapter, syncCallBack, this::refreshLists).attachToRecyclerView(listView); } private void refreshLists() { @@ -803,11 +807,23 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap } private void synchronize() { - swipeRefreshLayout.setRefreshing(true); - db.getNoteServerSyncHelper().addCallbackPull(ssoAccount, syncCallBack); - db.getNoteServerSyncHelper().scheduleSync(ssoAccount, false); + NoteServerSyncHelper syncHelper = db.getNoteServerSyncHelper(); + if (syncHelper.isSyncPossible()) { + swipeRefreshLayout.setRefreshing(true); + syncHelper.addCallbackPull(ssoAccount, syncCallBack); + syncHelper.scheduleSync(ssoAccount, false); + } else { // Sync is not possible + swipeRefreshLayout.setRefreshing(false); + if (syncHelper.isNetworkConnected() && syncHelper.isSyncOnlyOnWifi()) { + Log.d(TAG, "Network is connected, but sync is not possible"); + } else { + Log.d(TAG, "Sync is not possible, because network is not connected"); + Snackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(LoginStatus.NO_NETWORK.str)), Snackbar.LENGTH_LONG).show(); + } + } } + @Override public void onAccountChosen(LocalAccount account) { List selection = new ArrayList<>(adapter.getSelected()); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java index d2a5f288..c85eb8a0 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java @@ -45,6 +45,7 @@ import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.util.DisplayUtils; import it.niedermann.owncloud.notes.util.MarkDownUtil; import it.niedermann.owncloud.notes.util.NoteLinksUtils; +import it.niedermann.owncloud.notes.util.SSOUtil; public class NotePreviewFragment extends SearchableBaseNoteFragment implements OnRefreshListener { @@ -210,7 +211,7 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O @Override public void onRefresh() { - if (db.getNoteServerSyncHelper().isSyncPossible()) { + if (db.getNoteServerSyncHelper().isSyncPossible() && SSOUtil.isConfigured(getContext())) { swipeRefreshLayout.setRefreshing(true); try { SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getContext()); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java index 84246185..6d5c3492 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java @@ -15,6 +15,7 @@ import android.util.Log; import android.view.View; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; @@ -47,6 +48,7 @@ import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.model.LoginStatus; import it.niedermann.owncloud.notes.util.ExceptionUtil; +import it.niedermann.owncloud.notes.util.SSOUtil; import it.niedermann.owncloud.notes.util.ServerResponse; import static android.content.Context.CLIPBOARD_SERVICE; @@ -64,6 +66,7 @@ public class NoteServerSyncHelper { private Context context; // Track network connection changes using a BroadcastReceiver + private boolean isSyncPossible = false; private boolean networkConnected = false; private String syncOnlyOnWifiKey; private boolean syncOnlyOnWifi; @@ -82,7 +85,7 @@ public class NoteServerSyncHelper { @Override public void onReceive(Context context, Intent intent) { updateNetworkStatus(); - if (isSyncPossible()) { + if (isSyncPossible() && SSOUtil.isConfigured(context)) { try { scheduleSync(SingleAccountHelper.getCurrentSingleSignOnAccount(context), false); } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { @@ -101,7 +104,6 @@ public class NoteServerSyncHelper { private Map> callbacksPush = new HashMap<>(); private Map> callbacksPull = new HashMap<>(); - private NoteServerSyncHelper(NoteSQLiteOpenHelper db) { this.db = db; this.context = db.getContext(); @@ -139,27 +141,26 @@ public class NoteServerSyncHelper { super.finalize(); } - private static boolean isConfigured(Context context) { - try { - SingleAccountHelper.getCurrentSingleSignOnAccount(context); - return true; - } catch (NextcloudFilesAppAccountNotFoundException e) { - return false; - } catch (NoCurrentAccountSelectedException e) { - return false; - } - } - /** - * Synchronization is only possible, if there is an active network connection and - * SingleSignOn is available + * Synchronization is only possible, if there is an active network connection. + * + * This method respects the user preference "Sync on Wi-Fi only". + * * NoteServerSyncHelper observes changes in the network connection. * The current state can be retrieved with this method. * * @return true if sync is possible, otherwise false. */ public boolean isSyncPossible() { - return networkConnected && isConfigured(context.getApplicationContext()); + return isSyncPossible; + } + + public boolean isNetworkConnected() { + return networkConnected; + } + + public boolean isSyncOnlyOnWifi() { + return syncOnlyOnWifi; } /** @@ -171,10 +172,16 @@ public class NoteServerSyncHelper { * @param callback Implementation of ISyncCallback, contains one method that shall be executed. */ public void addCallbackPush(SingleSignOnAccount ssoAccount, ISyncCallback callback) { - if (!callbacksPush.containsKey(ssoAccount.name)) { - callbacksPush.put(ssoAccount.name, new ArrayList<>()); + if (ssoAccount == 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<>()); + } + Objects.requireNonNull(callbacksPush.get(ssoAccount.name)).add(callback); } - Objects.requireNonNull(callbacksPush.get(ssoAccount.name)).add(callback); } /** @@ -186,10 +193,16 @@ public class NoteServerSyncHelper { * @param callback Implementation of ISyncCallback, contains one method that shall be executed. */ public void addCallbackPull(SingleSignOnAccount ssoAccount, ISyncCallback callback) { - if (!callbacksPull.containsKey(ssoAccount.name)) { - callbacksPull.put(ssoAccount.name, new ArrayList<>()); + if (ssoAccount == 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<>()); + } + Objects.requireNonNull(callbacksPull.get(ssoAccount.name)).add(callback); } - Objects.requireNonNull(callbacksPull.get(ssoAccount.name)).add(callback); } @@ -200,33 +213,37 @@ 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 (syncActive.get(ssoAccount.name) == null) { - syncActive.put(ssoAccount.name, false); - } - Log.d(TAG, "Sync requested (" + (onlyLocalChanges ? "onlyLocalChanges" : "full") + "; " + (syncActive.get(ssoAccount.name) ? "sync active" : "sync NOT active") + ") ..."); - if (isSyncPossible() && (!syncActive.get(ssoAccount.name) || onlyLocalChanges)) { - Log.d(TAG, "... starting now"); - SyncTask syncTask = new SyncTask(db.getLocalAccountByAccountName(ssoAccount.name), 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<>()); + if (ssoAccount == null) { + Log.i(TAG, "ssoAccount is null. Is this a local account?"); + } else { + if (syncActive.get(ssoAccount.name) == null) { + syncActive.put(ssoAccount.name, false); } - syncTask.execute(); - } else if (!onlyLocalChanges) { - Log.d(TAG, "... scheduled"); - syncScheduled.put(ssoAccount.name, true); - if(callbacksPush.containsKey(ssoAccount.name) && callbacksPush.get(ssoAccount.name) != null) { - for (ISyncCallback callback : callbacksPush.get(ssoAccount.name)) { - callback.onScheduled(); + Log.d(TAG, "Sync requested (" + (onlyLocalChanges ? "onlyLocalChanges" : "full") + "; " + (syncActive.get(ssoAccount.name) ? "sync active" : "sync NOT active") + ") ..."); + if (isSyncPossible() && (!syncActive.get(ssoAccount.name) || onlyLocalChanges)) { + Log.d(TAG, "... starting now"); + SyncTask syncTask = new SyncTask(db.getLocalAccountByAccountName(ssoAccount.name), 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<>()); } - } - } else { - Log.d(TAG, "... do nothing"); - if(callbacksPull.containsKey(ssoAccount.name) && callbacksPull.get(ssoAccount.name) != null) { - for (ISyncCallback callback : callbacksPush.get(ssoAccount.name)) { - callback.onScheduled(); + syncTask.execute(); + } else if (!onlyLocalChanges) { + Log.d(TAG, "... scheduled"); + syncScheduled.put(ssoAccount.name, true); + if (callbacksPush.containsKey(ssoAccount.name) && callbacksPush.get(ssoAccount.name) != null) { + for (ISyncCallback callback : callbacksPush.get(ssoAccount.name)) { + callback.onScheduled(); + } + } + } else { + Log.d(TAG, "... do nothing"); + if (callbacksPull.containsKey(ssoAccount.name) && callbacksPull.get(ssoAccount.name) != null) { + for (ISyncCallback callback : callbacksPush.get(ssoAccount.name)) { + callback.onScheduled(); + } } } } @@ -237,18 +254,20 @@ public class NoteServerSyncHelper { NetworkInfo activeInfo = connMgr.getActiveNetworkInfo(); if (activeInfo != null && activeInfo.isConnected()) { - networkConnected = + networkConnected = true; + isSyncPossible = !syncOnlyOnWifi || ((ConnectivityManager) context.getApplicationContext() .getSystemService(Context.CONNECTIVITY_SERVICE)) .getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected(); - if (networkConnected) { + if (isSyncPossible) { Log.d(TAG, "Network connection established."); } else { Log.d(TAG, "Network connected, but not used because only synced on wifi."); } } else { networkConnected = false; + isSyncPossible = false; Log.d(TAG, "No network connection."); } } @@ -264,7 +283,7 @@ public class NoteServerSyncHelper { private final Map> callbacks = new HashMap<>(); private List exceptions = new ArrayList<>(); - SyncTask(LocalAccount localAccount, SingleSignOnAccount ssoAccount, boolean onlyLocalChanges) { + SyncTask(@NonNull LocalAccount localAccount, @NonNull SingleSignOnAccount ssoAccount, boolean onlyLocalChanges) { this.localAccount = localAccount; this.ssoAccount = ssoAccount; this.onlyLocalChanges = onlyLocalChanges; diff --git a/app/src/main/java/it/niedermann/owncloud/notes/util/SSOUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/util/SSOUtil.java index 6759d223..5089f14d 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/util/SSOUtil.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/util/SSOUtil.java @@ -1,13 +1,17 @@ package it.niedermann.owncloud.notes.util; import android.app.Activity; +import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; import com.nextcloud.android.sso.AccountImporter; import com.nextcloud.android.sso.exceptions.AndroidGetAccountsPermissionNotGranted; +import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotInstalledException; +import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; +import com.nextcloud.android.sso.helper.SingleAccountHelper; import com.nextcloud.android.sso.ui.UiExceptionManager; public class SSOUtil { @@ -36,4 +40,15 @@ public class SSOUtil { AccountImporter.requestAndroidAccountPermissionsAndPickAccount(activity); } } + + public static boolean isConfigured(Context context) { + try { + SingleAccountHelper.getCurrentSingleSignOnAccount(context); + return true; + } catch (NextcloudFilesAppAccountNotFoundException e) { + return false; + } catch (NoCurrentAccountSelectedException e) { + return false; + } + } } -- cgit v1.2.3 From 0115cb3d8a699a77dc40e055380a10610d99bd26 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Fri, 31 Jan 2020 03:31:24 +0000 Subject: [tx-robot] updated from transifex --- app/src/main/res/values-sl/strings.xml | 1 + app/src/main/res/values-sr/strings.xml | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'app/src') diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index f5011dc4..c0899102 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -119,6 +119,7 @@ Izbrisane %1$d zabeležke Obnovljene %1$d zabeležke Le za branje + Brez kategorije Dodaj %1$s diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 50060128..ae276031 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -16,6 +16,8 @@ Подебљано Веза Курзив + Обрисано %1$s + Повраћено %1$s Опозови отвори навигацију затвори навигацију @@ -50,6 +52,7 @@ Синхронизација није успела%1$s Да ли је апликација Белешки активирана на серверу? нема мрежне везе + да ли имате инсталирану апликацију за фајлове? Десила се непозната грешка @@ -108,9 +111,15 @@ Налог је већ увезен Још нема белешки Притисните + дугме да направите нову белешку + Не могу да учитам преглед. Проверите да ли постоји двоцифрена ставка у листи без садржаја. Још Помери + Да ли је Ваша апликација за фајлове на најновијој верзији? + Поље за потврду не може да се укључи/искључи. + Обрисано %1$d белешки + Повраћено %1$d белешки Само за читање + Нема категорије Додај %1$s -- cgit v1.2.3