diff options
author | Stefan Niedermann <info@niedermann.it> | 2021-01-02 18:08:48 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2021-01-02 18:08:48 +0300 |
commit | d35ec5c80e1529453065927bebc34e091cb4261f (patch) | |
tree | 420827e2c865b4eeca1c6d2027fe1dbec66c2b47 /app/src/main/java/it/niedermann | |
parent | 69d383f9f87e1207850ffdc27c0fd406c8901cf5 (diff) |
Move more database queries away from the main thread
Diffstat (limited to 'app/src/main/java/it/niedermann')
17 files changed, 429 insertions, 343 deletions
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java index baecc774..92e0e6e8 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/accountswitcher/AccountSwitcherDialog.java @@ -63,7 +63,7 @@ public class AccountSwitcherDialog extends BrandedDialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { binding = DialogAccountSwitcherBinding.inflate(requireActivity().getLayoutInflater()); - LiveData<Account> account$ = db.getAccountDao().getAccountLiveData(currentAccountId); + final LiveData<Account> account$ = db.getAccountDao().getAccountLiveData(currentAccountId); account$.observe(requireActivity(), (currentLocalAccount) -> { account$.removeObservers(requireActivity()); @@ -76,12 +76,12 @@ public class AccountSwitcherDialog extends BrandedDialogFragment { .into(binding.currentAccountItemAvatar); binding.accountLayout.setOnClickListener((v) -> dismiss()); - AccountSwitcherAdapter adapter = new AccountSwitcherAdapter((localAccount -> { + final AccountSwitcherAdapter adapter = new AccountSwitcherAdapter((localAccount -> { accountSwitcherListener.onAccountChosen(localAccount); dismiss(); })); binding.accountsList.setAdapter(adapter); - LiveData<List<Account>> localAccounts$ = db.getAccountDao().getAccountsLiveData(); + final LiveData<List<Account>> localAccounts$ = db.getAccountDao().getAccountsLiveData(); localAccounts$.observe(requireActivity(), (localAccounts) -> { localAccounts$.removeObservers(requireActivity()); for (Account localAccount : localAccounts) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java index acc5e0a2..fe2e4758 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/BaseNoteFragment.java @@ -93,56 +93,6 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego db = NotesDatabase.getInstance(context); } - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - try { - SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(requireActivity().getApplicationContext()); - this.localAccount = db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name); - - if (savedInstanceState == null) { - long id = requireArguments().getLong(PARAM_NOTE_ID); - if (id > 0) { - long accountId = requireArguments().getLong(PARAM_ACCOUNT_ID); - if (accountId > 0) { - /* Switch account if account id has been provided */ - this.localAccount = db.getAccountDao().getAccount(accountId); - SingleAccountHelper.setCurrentAccount(requireActivity().getApplicationContext(), localAccount.getAccountName()); - } - isNew = false; - note = originalNote = db.getNoteDao().getNoteById(id); - onNoteLoaded(note); - } else { - Note cloudNote = (Note) requireArguments().getSerializable(PARAM_NEWNOTE); - String content = requireArguments().getString(PARAM_CONTENT); - if (cloudNote == null) { - if (content == null) { - throw new IllegalArgumentException(PARAM_NOTE_ID + " is not given, argument " + PARAM_NEWNOTE + " is missing and " + PARAM_CONTENT + " is missing."); - } else { - note = new Note(-1, -1L, Calendar.getInstance(), NoteUtil.generateNoteTitle(content), content, getString(R.string.category_readonly), false, null, DBStatus.VOID, -1, "", 0); - onNoteLoaded(note); - } - } else { - final LiveData<Note> createLiveData = db.addNoteAndSync(localAccount, cloudNote); - createLiveData.observe(requireActivity(), (createdNote) -> { - note = createdNote; - originalNote = null; - onNoteLoaded(note); - createLiveData.removeObservers(requireActivity()); - }); - } - } - } else { - note = (Note) savedInstanceState.getSerializable(SAVEDKEY_NOTE); - originalNote = (Note) savedInstanceState.getSerializable(SAVEDKEY_ORIGINAL_NOTE); - onNoteLoaded(note); - } - setHasOptionsMenu(true); - } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); - } - } - @Nullable protected abstract ScrollView getScrollView(); @@ -167,6 +117,59 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego this.originalScrollY = note.getScrollY(); scrollView.post(() -> scrollView.scrollTo(0, originalScrollY)); } + new Thread(() -> { + try { + SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext().getApplicationContext()); + this.localAccount = db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name); + + if (savedInstanceState == null) { + long id = requireArguments().getLong(PARAM_NOTE_ID); + if (id > 0) { + long accountId = requireArguments().getLong(PARAM_ACCOUNT_ID); + if (accountId > 0) { + /* Switch account if account id has been provided */ + this.localAccount = db.getAccountDao().getAccount(accountId); + SingleAccountHelper.setCurrentAccount(requireContext().getApplicationContext(), localAccount.getAccountName()); + } + isNew = false; + note = originalNote = db.getNoteDao().getNoteById(id); + requireActivity().runOnUiThread(() -> onNoteLoaded(note)); + requireActivity().invalidateOptionsMenu(); + } else { + Note cloudNote = (Note) requireArguments().getSerializable(PARAM_NEWNOTE); + String content = requireArguments().getString(PARAM_CONTENT); + if (cloudNote == null) { + if (content == null) { + throw new IllegalArgumentException(PARAM_NOTE_ID + " is not given, argument " + PARAM_NEWNOTE + " is missing and " + PARAM_CONTENT + " is missing."); + } else { + note = new Note(-1, null, Calendar.getInstance(), NoteUtil.generateNoteTitle(content), content, getString(R.string.category_readonly), false, null, DBStatus.VOID, -1, "", 0); + requireActivity().runOnUiThread(() -> onNoteLoaded(note)); + requireActivity().invalidateOptionsMenu(); + } + } else { + requireActivity().runOnUiThread(() -> { + final LiveData<Note> createLiveData$ = db.addNoteAndSync(localAccount, cloudNote); + createLiveData$.observe(requireActivity(), (createdNote) -> { + note = createdNote; + originalNote = null; + requireActivity().runOnUiThread(() -> onNoteLoaded(note)); + requireActivity().invalidateOptionsMenu(); + createLiveData$.removeObservers(requireActivity()); + }); + }); + } + } + } else { + note = (Note) savedInstanceState.getSerializable(SAVEDKEY_NOTE); + originalNote = (Note) savedInstanceState.getSerializable(SAVEDKEY_ORIGINAL_NOTE); + requireActivity().runOnUiThread(() -> onNoteLoaded(note)); + requireActivity().invalidateOptionsMenu(); + } + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); + } + }).start(); + setHasOptionsMenu(true); } @Override @@ -209,11 +212,12 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego @Override public void onPrepareOptionsMenu(@NonNull Menu menu) { super.onPrepareOptionsMenu(menu); - MenuItem itemFavorite = menu.findItem(R.id.menu_favorite); - prepareFavoriteOption(itemFavorite); + if (note != null) { + prepareFavoriteOption(menu.findItem(R.id.menu_favorite)); - menu.findItem(R.id.menu_title).setVisible(localAccount.getPreferredApiVersion() != null && localAccount.getPreferredApiVersion().compareTo(new ApiVersion("1.0", 1, 0)) >= 0); - menu.findItem(R.id.menu_delete).setVisible(!isNew); + menu.findItem(R.id.menu_title).setVisible(localAccount.getPreferredApiVersion() != null && localAccount.getPreferredApiVersion().compareTo(new ApiVersion("1.0", 1, 0)) >= 0); + menu.findItem(R.id.menu_delete).setVisible(!isNew); + } } private void prepareFavoriteOption(MenuItem item) { @@ -229,11 +233,13 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.menu_cancel) { - if (originalNote == null) { - db.deleteNoteAndSync(localAccount, note.getId()); - } else { - db.updateNoteAndSync(localAccount, originalNote, null, null, null); - } + new Thread(() -> { + if (originalNote == null) { + db.deleteNoteAndSync(localAccount, note.getId()); + } else { + db.updateNoteAndSync(localAccount, originalNote, null, null, null); + } + }).start(); listener.close(); return true; } else if (itemId == R.id.menu_delete) { @@ -252,9 +258,11 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego showEditTitleDialog(); return true; } else if (itemId == R.id.menu_move) { - AccountPickerDialogFragment - .newInstance(new ArrayList<>(db.getAccountDao().getAccounts()), note.getAccountId()) - .show(requireActivity().getSupportFragmentManager(), BaseNoteFragment.class.getSimpleName()); + new Thread(() -> { + AccountPickerDialogFragment + .newInstance(new ArrayList<>(), note.getAccountId()) + .show(requireActivity().getSupportFragmentManager(), BaseNoteFragment.class.getSimpleName()); + }).start(); return true; } else if (itemId == R.id.menu_share) { ShareUtil.openShareDialog(requireContext(), note.getTitle(), note.getContent()); @@ -300,7 +308,7 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego protected void saveNote(@Nullable ISyncCallback callback) { Log.d(TAG, "saveData()"); if (note != null) { - String newContent = getContent(); + final String newContent = getContent(); if (note.getContent().equals(newContent)) { if (note.getScrollY() != originalScrollY) { Log.v(TAG, "... only saving new scroll state, since content did not change"); @@ -309,9 +317,11 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego Log.v(TAG, "... not saving, since nothing has changed"); } } else { - note = db.updateNoteAndSync(localAccount, note, newContent, null, callback); - listener.onNoteUpdated(note); - requireActivity().invalidateOptionsMenu(); + new Thread(() -> { + note = db.updateNoteAndSync(localAccount, note, newContent, null, callback); + requireActivity().runOnUiThread(() -> listener.onNoteUpdated(note)); + requireActivity().invalidateOptionsMenu(); + }).start(); } } else { Log.e(TAG, "note is null"); @@ -362,8 +372,10 @@ public abstract class BaseNoteFragment extends BrandedFragment implements Catego public void onTitleEdited(String newTitle) { titleModified = true; note.setTitle(newTitle); - note = db.updateNoteAndSync(localAccount, note, note.getContent(), newTitle, null); - listener.onNoteUpdated(note); + new Thread(() -> { + note = db.updateNoteAndSync(localAccount, note, note.getContent(), newTitle, null); + requireActivity().runOnUiThread(() -> listener.onNoteUpdated(note)); + }).start(); } public void moveNote(Account account) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java index c3815828..996fd4d0 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java @@ -20,6 +20,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.util.Consumer; import androidx.core.view.ViewCompat; import androidx.preference.PreferenceManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener; @@ -169,25 +170,26 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O }) .build()); - TextProcessorChain chain = defaultTextProcessorChain(note); - try { - binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); - } catch (StringIndexOutOfBoundsException e) { - // Workaround for RxMarkdown: https://github.com/stefan-niedermann/nextcloud-notes/issues/668 - binding.singleNoteContent.setText(chain.apply(note.getContent())); - Toast.makeText(binding.singleNoteContent.getContext(), R.string.could_not_load_preview_two_digit_numbered_list, Toast.LENGTH_LONG).show(); - e.printStackTrace(); - } - changedText = note.getContent(); - binding.singleNoteContent.setMovementMethod(LinkMovementMethod.getInstance()); + defaultTextProcessorChain(note, (chain) -> { + try { + binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); + } catch (StringIndexOutOfBoundsException e) { + // Workaround for RxMarkdown: https://github.com/stefan-niedermann/nextcloud-notes/issues/668 + binding.singleNoteContent.setText(chain.apply(note.getContent())); + Toast.makeText(binding.singleNoteContent.getContext(), R.string.could_not_load_preview_two_digit_numbered_list, Toast.LENGTH_LONG).show(); + e.printStackTrace(); + } + changedText = note.getContent(); + binding.singleNoteContent.setMovementMethod(LinkMovementMethod.getInstance()); - binding.swiperefreshlayout.setOnRefreshListener(this); + binding.swiperefreshlayout.setOnRefreshListener(this); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(requireActivity().getApplicationContext()); - binding.singleNoteContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(requireContext(), sp)); - if (sp.getBoolean(getString(R.string.pref_key_font), false)) { - binding.singleNoteContent.setTypeface(Typeface.MONOSPACE); - } + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(requireActivity().getApplicationContext()); + binding.singleNoteContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(requireContext(), sp)); + if (sp.getBoolean(getString(R.string.pref_key_font), false)) { + binding.singleNoteContent.setTypeface(Typeface.MONOSPACE); + } + }); } @@ -209,19 +211,20 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O public void onRefresh() { if (noteLoaded && db.getNoteServerSyncHelper().isSyncPossible() && SSOUtil.isConfigured(getContext())) { binding.swiperefreshlayout.setRefreshing(true); - try { - TextProcessorChain chain = defaultTextProcessorChain(note); - Account account = db.getAccountDao().getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()).name); - db.getNoteServerSyncHelper().addCallbackPull(account, () -> { - note = db.getNoteDao().getNoteById(note.getId()); - changedText = note.getContent(); - binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); - binding.swiperefreshlayout.setRefreshing(false); - }); - db.getNoteServerSyncHelper().scheduleSync(account, false); - } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); - } + defaultTextProcessorChain(note, (chain) -> { + try { + Account account = db.getAccountDao().getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()).name); + db.getNoteServerSyncHelper().addCallbackPull(account, () -> { + note = db.getNoteDao().getNoteById(note.getId()); + changedText = note.getContent(); + binding.singleNoteContent.setText(parseCompat(markdownProcessor, chain.apply(note.getContent()))); + binding.swiperefreshlayout.setRefreshing(false); + }); + db.getNoteServerSyncHelper().scheduleSync(account, false); + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); + } + }); } else { binding.swiperefreshlayout.setRefreshing(false); Toast.makeText(requireContext(), getString(R.string.error_sync, getString(R.string.error_no_network)), Toast.LENGTH_LONG).show(); @@ -234,10 +237,12 @@ public class NotePreviewFragment extends SearchableBaseNoteFragment implements O binding.singleNoteContent.setHighlightColor(getTextHighlightBackgroundColor(requireContext(), mainColor, colorPrimary, colorAccent)); } - private TextProcessorChain defaultTextProcessorChain(Note note) { - TextProcessorChain chain = new TextProcessorChain(); - chain.add(new NoteLinksProcessor(new HashSet<>(db.getNoteDao().getRemoteIds(note.getAccountId())))); - chain.add(new WwwLinksProcessor()); - return chain; + private void defaultTextProcessorChain(@NonNull Note note, @NonNull Consumer<TextProcessorChain> onChainReady) { + new Thread(() -> { + final TextProcessorChain chain = new TextProcessorChain(); + chain.add(new NoteLinksProcessor(new HashSet<>(db.getNoteDao().getRemoteIds(note.getAccountId())))); + chain.add(new WwwLinksProcessor()); + requireActivity().runOnUiThread(() -> onChainReady.accept(chain)); + }).start(); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java index e16d0f6c..ca8c3c6e 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java @@ -480,7 +480,10 @@ public class MainViewModel extends AndroidViewModel { } public LiveData<Note> moveNoteToAnotherAccount(Account account, Long noteId) { - return db.moveNoteToAnotherAccount(account, db.getNoteDao().getNoteById(noteId)); + return switchMap(db.getNoteDao().getNoteByIdLiveData(noteId), (note) -> { + Log.v(TAG, "[moveNoteToAnotherAccount] - note: " + note); + return db.moveNoteToAnotherAccount(account, note); + }); } public LiveData<Void> toggleFavoriteAndSync(long noteId) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java index 97c18e0c..c2f45722 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java @@ -122,12 +122,14 @@ public class MultiSelectedActionModeCallback implements Callback { }); return true; } else if (itemId == R.id.menu_move) { - final LiveData<Account> accountLiveData = mainViewModel.getCurrentAccount(); - accountLiveData.observe(lifecycleOwner, account -> { - accountLiveData.removeObservers(lifecycleOwner); - AccountPickerDialogFragment - .newInstance(new ArrayList<>(mainViewModel.getAccounts()), account.getId()) - .show(fragmentManager, AccountPickerDialogFragment.class.getSimpleName()); + final LiveData<Account> currentAccount$ = mainViewModel.getCurrentAccount(); + currentAccount$.observe(lifecycleOwner, account -> { + currentAccount$.removeObservers(lifecycleOwner); + new Thread(() -> { + AccountPickerDialogFragment + .newInstance(new ArrayList<>(mainViewModel.getAccounts()), account.getId()) + .show(fragmentManager, AccountPickerDialogFragment.class.getSimpleName()); + }).start(); }); return true; } else if (itemId == R.id.menu_share) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java index 00dd9d0e..3a7228e3 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/manageaccounts/ManageAccountsActivity.java @@ -17,6 +17,8 @@ import it.niedermann.owncloud.notes.databinding.ActivityManageAccountsBinding; import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.persistence.entity.Account; +import static androidx.lifecycle.Transformations.distinctUntilChanged; + public class ManageAccountsActivity extends LockedActivity { private ActivityManageAccountsBinding binding; @@ -34,37 +36,40 @@ public class ManageAccountsActivity extends LockedActivity { db = NotesDatabase.getInstance(this); - List<Account> localAccounts = db.getAccountDao().getAccounts(); - - adapter = new ManageAccountAdapter((localAccount) -> SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccount.getAccountName()), (localAccount) -> { - LiveData<Void> deleteLiveData = db.deleteAccount(localAccount); - deleteLiveData.observe(this, (v) -> { - for (Account temp : localAccounts) { - if (temp.getId() == localAccount.getId()) { - localAccounts.remove(temp); - break; + distinctUntilChanged(db.getAccountDao().getAccountsLiveData()).observe(this, (localAccounts) -> { + adapter = new ManageAccountAdapter((localAccount) -> SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccount.getAccountName()), (localAccount) -> { + LiveData<Void> deleteLiveData = db.deleteAccount(localAccount); + deleteLiveData.observe(this, (v) -> { + for (Account temp : localAccounts) { + if (temp.getId() == localAccount.getId()) { + localAccounts.remove(temp); + break; + } } - } - if (localAccounts.size() > 0) { - SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccounts.get(0).getAccountName()); - adapter.setCurrentLocalAccount(localAccounts.get(0)); - } else { - SingleAccountHelper.setCurrentAccount(getApplicationContext(), null); - finish(); - } - deleteLiveData.removeObservers(this); + if (localAccounts.size() > 0) { + SingleAccountHelper.setCurrentAccount(getApplicationContext(), localAccounts.get(0).getAccountName()); + adapter.setCurrentLocalAccount(localAccounts.get(0)); + } else { + SingleAccountHelper.setCurrentAccount(getApplicationContext(), null); + finish(); + } + deleteLiveData.removeObservers(this); + }); }); - }); - adapter.setLocalAccounts(localAccounts); - try { - SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(this); - if (ssoAccount != null) { - adapter.setCurrentLocalAccount(db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name)); + adapter.setLocalAccounts(localAccounts); + try { + final SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(this); + if (ssoAccount != null) { + new Thread(() -> { + final Account account = db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name); + runOnUiThread(() -> adapter.setCurrentLocalAccount(account)); + }).start(); + } + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); } - } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); - } - binding.accounts.setAdapter(adapter); + binding.accounts.setAdapter(adapter); + }); } @Override 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 c1268e20..3a386f50 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 @@ -44,7 +44,6 @@ import it.niedermann.owncloud.notes.shared.util.SSOUtil; import static androidx.lifecycle.Transformations.distinctUntilChanged; import static it.niedermann.owncloud.notes.shared.model.DBStatus.LOCAL_DELETED; -import static it.niedermann.owncloud.notes.shared.model.DBStatus.LOCAL_EDITED; import static it.niedermann.owncloud.notes.shared.util.NoteUtil.generateNoteExcerpt; import static java.net.HttpURLConnection.HTTP_NOT_FOUND; import static java.net.HttpURLConnection.HTTP_NOT_MODIFIED; @@ -441,7 +440,9 @@ public class NoteServerSyncHelper { Log.d(TAG, "pullRemoteChanges() for account " + localAccount.getAccountName()); try { final Map<Long, Long> idMap = db.getIdMap(localAccount.getId()); - final ServerResponse.NotesResponse response = notesClient.getNotes(ssoAccount, localAccount.getModified().getTimeInMillis()/1000, localAccount.getETag()); + final Calendar modified = localAccount.getModified(); + final long modifiedForServer = modified == null ? 0 : modified.getTimeInMillis() / 1000; + final ServerResponse.NotesResponse response = notesClient.getNotes(ssoAccount, modifiedForServer, localAccount.getETag()); List<Note> remoteNotes = response.getNotes(); Set<Long> remoteIDs = new HashSet<>(); // pull remote changes: update or create each remote note diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java index c7558b4a..8b73ec44 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java @@ -1,4 +1,4 @@ -package it.niedermann.owncloud.notes.persistence; + package it.niedermann.owncloud.notes.persistence; import android.content.Context; import android.content.Intent; @@ -74,6 +74,7 @@ import it.niedermann.owncloud.notes.shared.util.NoteUtil; import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION_CODES.O; import static androidx.lifecycle.Transformations.map; +import static androidx.lifecycle.Transformations.switchMap; import static it.niedermann.owncloud.notes.edit.EditNoteActivity.ACTION_SHORTCUT; import static it.niedermann.owncloud.notes.shared.util.NoteUtil.generateNoteExcerpt; import static it.niedermann.owncloud.notes.widget.notelist.NoteListWidget.updateNoteListWidgets; @@ -116,7 +117,7 @@ public abstract class NotesDatabase extends RoomDatabase { new Migration_19_20() ) .fallbackToDestructiveMigrationOnDowngrade() -// .fallbackToDestructiveMigration() + .fallbackToDestructiveMigration() .addCallback(new RoomDatabase.Callback() { @Override public void onCreate(@NonNull SupportSQLiteDatabase db) { @@ -127,7 +128,7 @@ public abstract class NotesDatabase extends RoomDatabase { Log.v(TAG, NotesDatabase.class.getSimpleName() + " created."); } }) - .allowMainThreadQueries() // FIXME remove +// .allowMainThreadQueries() // FIXME remove .build(); } @@ -202,11 +203,13 @@ public abstract class NotesDatabase extends RoomDatabase { return getNoteDao().getNoteById(getNoteDao().addNote(entity)); } - @AnyThread + @WorkerThread public LiveData<Note> moveNoteToAnotherAccount(Account account, Note note) { - Note Note = new Note(null, note.getModified(), note.getTitle(), getNoteDao().getContent(note.getId()), note.getCategory(), note.getFavorite(), null); - deleteNoteAndSync(account, note.getId()); - return addNoteAndSync(account, Note); + return switchMap(getNoteDao().getContentLiveData(note.getId()), (content) -> { + final Note fullNote = new Note(null, note.getModified(), note.getTitle(), content, note.getCategory(), note.getFavorite(), null); + deleteNoteAndSync(account, note.getId()); + return addNoteAndSync(account, fullNote); + }); } @NonNull @@ -481,7 +484,7 @@ public abstract class NotesDatabase extends RoomDatabase { default: { final String category = selectedCategory.getCategory(); if (category != null) { - if(getCategoryOptionsDao().modifyCategoryOrder(accountId, category, sortingMethod) == 0) { + if (getCategoryOptionsDao().modifyCategoryOrder(accountId, category, sortingMethod) == 0) { // Nothing updated means we didn't have this yet final CategoryOptions categoryOptions = new CategoryOptions(); categoryOptions.setAccountId(accountId); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java index 65c60fd6..1734e1a7 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java @@ -29,6 +29,9 @@ public interface AccountDao { @Query("SELECT * FROM Account WHERE ACCOUNTNAME = :accountName") Account getLocalAccountByAccountName(String accountName); + @Query("SELECT * FROM Account WHERE ACCOUNTNAME = :accountName") + LiveData<Account> getLocalAccountByAccountNameLiveData(String accountName); + @Query("SELECT * FROM Account") List<Account> getAccounts(); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/NoteDao.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/NoteDao.java index 0cfa4de2..e1493ca1 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/NoteDao.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/NoteDao.java @@ -63,6 +63,9 @@ public interface NoteDao { @Query("SELECT * FROM NOTE WHERE id = :id") Note getNoteById(long id); + @Query("SELECT * FROM NOTE WHERE id = :id") + LiveData<Note> getNoteByIdLiveData(long id); + @Query("UPDATE NOTE SET status = :status WHERE id = :id") void updateStatus(long id, DBStatus status); @@ -131,12 +134,15 @@ public interface NoteDao { * used by: {@link NoteServerSyncHelper.SyncTask#pullRemoteChanges()} update only, if not modified locally (i.e. STATUS="") and if modified remotely (i.e. any (!) column has changed) */ @Query("UPDATE NOTE SET title = :title, modified = :modified, favorite = :favorite, etag = :eTag, content = :content, status = '', excerpt = :excerpt " + - "WHERE id = :id AND status = '' AND (title != :title OR modified != (:modified / 1000) OR favorite != :favorite OR category != :category OR (eTag IS NULL OR eTag != :eTag) OR content != :content)") + "WHERE id = :id AND status = '' AND (title != :title OR modified != :modified OR favorite != :favorite OR category != :category OR (eTag IS NULL OR eTag != :eTag) OR content != :content)") int updateIfNotModifiedLocallyAndAnyRemoteColumnHasChanged(long id, Long modified, String title, boolean favorite, String category, String eTag, String content, String excerpt); @Query("SELECT content FROM NOTE WHERE id = :id") String getContent(Long id); + @Query("SELECT content FROM NOTE WHERE id = :id") + LiveData<String> getContentLiveData(Long id); + /** * This method return all of the categories with given {@param accountId} * diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Converters.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Converters.java index 1984df40..099fec4e 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Converters.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Converters.java @@ -44,14 +44,14 @@ public class Converters { if (value == null) { calendar.setTimeInMillis(0); } else { - calendar.setTimeInMillis(value * 1000); + calendar.setTimeInMillis(value); } return calendar; } @TypeConverter public static Long calendarToLong(Calendar calendar) { - return calendar == null ? 0 : calendar.getTimeInMillis() / 1000; + return calendar == null ? 0 : calendar.getTimeInMillis(); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListViewModel.java new file mode 100644 index 00000000..09af033c --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListViewModel.java @@ -0,0 +1,68 @@ +package it.niedermann.owncloud.notes.widget.notelist; + +import android.app.Application; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; + +import java.util.ArrayList; +import java.util.List; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.main.MainActivity; +import it.niedermann.owncloud.notes.main.navigation.NavigationAdapter; +import it.niedermann.owncloud.notes.main.navigation.NavigationItem; +import it.niedermann.owncloud.notes.persistence.NotesDatabase; + +import static androidx.lifecycle.Transformations.distinctUntilChanged; +import static androidx.lifecycle.Transformations.map; +import static androidx.lifecycle.Transformations.switchMap; +import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.FAVORITES; +import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.RECENT; +import static it.niedermann.owncloud.notes.shared.util.DisplayUtils.convertToCategoryNavigationItem; + +public class NoteListViewModel extends AndroidViewModel { + + private static final String TAG = NoteListViewModel.class.getSimpleName(); + + @NonNull + private final NotesDatabase db; + + public NoteListViewModel(@NonNull Application application) { + super(application); + this.db = NotesDatabase.getInstance(application); + } + + public LiveData<List<NavigationItem>> getAdapterCategories(Long accountId) { + return distinctUntilChanged( + switchMap(distinctUntilChanged(db.getNoteDao().countLiveData(accountId)), (count) -> { + Log.v(TAG, "[getAdapterCategories] countLiveData: " + count); + return switchMap(distinctUntilChanged(db.getNoteDao().getFavoritesCountLiveData(accountId)), (favoritesCount) -> { + Log.v(TAG, "[getAdapterCategories] getFavoritesCountLiveData: " + favoritesCount); + return map(distinctUntilChanged(db.getNoteDao().getCategoriesLiveData(accountId)), fromDatabase -> { + final List<NavigationItem.CategoryNavigationItem> categories = convertToCategoryNavigationItem(getApplication(), fromDatabase); + + final List<NavigationItem> items = new ArrayList<>(fromDatabase.size() + 3); + items.add(new NavigationItem(MainActivity.ADAPTER_KEY_RECENT, getApplication().getString(R.string.label_all_notes), count, R.drawable.ic_access_time_grey600_24dp, RECENT)); + items.add(new NavigationItem(MainActivity.ADAPTER_KEY_STARRED, getApplication().getString(R.string.label_favorites), favoritesCount, R.drawable.ic_star_yellow_24dp, FAVORITES)); + + if (categories.size() > 2 && categories.get(2).label.isEmpty()) { + items.add(new NavigationItem(MainActivity.ADAPTER_KEY_UNCATEGORIZED, "", null, NavigationAdapter.ICON_NOFOLDER)); + } + + for (NavigationItem item : categories) { + final int slashIndex = item.label.indexOf('/'); + + item.label = slashIndex < 0 ? item.label : item.label.substring(0, slashIndex); + item.id = "category:" + item.label; + items.add(item); + } + return items; + }); + }); + }) + ); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidget.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidget.java index 6b6d5050..77c11ac6 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidget.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidget.java @@ -38,94 +38,95 @@ public class NoteListWidget extends AppWidgetProvider { static void updateAppWidget(Context context, AppWidgetManager awm, int[] appWidgetIds) { final NotesDatabase db = NotesDatabase.getInstance(context); - RemoteViews views; - DarkModeSetting darkTheme; - for (int appWidgetId : appWidgetIds) { - final NotesListWidgetData data = db.getWidgetNotesListDao().getNoteListWidgetData(appWidgetId); - if (data != null) { - final Account localAccount = db.getAccountDao().getAccount(data.getAccountId()); - - String category = data.getCategory(); - - darkTheme = DarkModeSetting.fromModeID(data.getThemeMode()); - - Intent serviceIntent = new Intent(context, NoteListWidgetService.class); - serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME))); - - // Launch application when user taps the header icon or app title - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setComponent(new ComponentName(context.getPackageName(), - MainActivity.class.getName())); - - // Open the main app if the user taps the widget header - PendingIntent openAppI = PendingIntent.getActivity(context, PENDING_INTENT_OPEN_APP_RQ, - intent, - PendingIntent.FLAG_UPDATE_CURRENT); - - // Launch create note activity if user taps "+" icon on header - PendingIntent newNoteI = PendingIntent.getActivity(context, (PENDING_INTENT_NEW_NOTE_RQ + appWidgetId), - new Intent(context, EditNoteActivity.class).putExtra(PARAM_CATEGORY, - data.getMode() == MODE_DISPLAY_STARRED - ? new NavigationCategory(ENavigationCategoryType.FAVORITES) - : new NavigationCategory(localAccount.getId(), category)), - PendingIntent.FLAG_UPDATE_CURRENT); - - PendingIntent templatePI = PendingIntent.getActivity(context, PENDING_INTENT_EDIT_NOTE_RQ, - new Intent(context, EditNoteActivity.class), - PendingIntent.FLAG_UPDATE_CURRENT); - - Log.v(TAG, "-- data - " + data); - - if (NotesApplication.isDarkThemeActive(context, darkTheme)) { - views = new RemoteViews(context.getPackageName(), R.layout.widget_note_list_dark); - views.setTextViewText(R.id.widget_note_list_title_tv_dark, getWidgetTitle(context, data.getMode(), category)); - views.setOnClickPendingIntent(R.id.widget_note_header_icon_dark, openAppI); - views.setOnClickPendingIntent(R.id.widget_note_list_title_tv_dark, openAppI); - views.setOnClickPendingIntent(R.id.widget_note_list_create_icon_dark, newNoteI); - views.setPendingIntentTemplate(R.id.note_list_widget_lv_dark, templatePI); - views.setRemoteAdapter(appWidgetId, R.id.note_list_widget_lv_dark, serviceIntent); - views.setEmptyView(R.id.note_list_widget_lv_dark, R.id.widget_note_list_placeholder_tv_dark); - awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.note_list_widget_lv_dark); - if (BrandingUtil.isBrandingEnabled(context)) { - views.setInt(R.id.widget_note_header_dark, "setBackgroundColor", localAccount.getColor()); - views.setInt(R.id.widget_note_header_icon_dark, "setColorFilter", localAccount.getTextColor()); - views.setInt(R.id.widget_note_list_create_icon_dark, "setColorFilter", localAccount.getTextColor()); - views.setTextColor(R.id.widget_note_list_title_tv_dark, localAccount.getTextColor()); + new Thread(() -> { + RemoteViews views; + DarkModeSetting darkTheme; + final NotesListWidgetData data = db.getWidgetNotesListDao().getNoteListWidgetData(appWidgetId); + if (data != null) { + final Account localAccount = db.getAccountDao().getAccount(data.getAccountId()); + + String category = data.getCategory(); + + darkTheme = DarkModeSetting.fromModeID(data.getThemeMode()); + + Intent serviceIntent = new Intent(context, NoteListWidgetService.class); + serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME))); + + // Launch application when user taps the header icon or app title + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setComponent(new ComponentName(context.getPackageName(), + MainActivity.class.getName())); + + // Open the main app if the user taps the widget header + PendingIntent openAppI = PendingIntent.getActivity(context, PENDING_INTENT_OPEN_APP_RQ, + intent, + PendingIntent.FLAG_UPDATE_CURRENT); + + // Launch create note activity if user taps "+" icon on header + PendingIntent newNoteI = PendingIntent.getActivity(context, (PENDING_INTENT_NEW_NOTE_RQ + appWidgetId), + new Intent(context, EditNoteActivity.class).putExtra(PARAM_CATEGORY, + data.getMode() == MODE_DISPLAY_STARRED + ? new NavigationCategory(ENavigationCategoryType.FAVORITES) + : new NavigationCategory(localAccount.getId(), category)), + PendingIntent.FLAG_UPDATE_CURRENT); + + PendingIntent templatePI = PendingIntent.getActivity(context, PENDING_INTENT_EDIT_NOTE_RQ, + new Intent(context, EditNoteActivity.class), + PendingIntent.FLAG_UPDATE_CURRENT); + + Log.v(TAG, "-- data - " + data); + + if (NotesApplication.isDarkThemeActive(context, darkTheme)) { + views = new RemoteViews(context.getPackageName(), R.layout.widget_note_list_dark); + views.setTextViewText(R.id.widget_note_list_title_tv_dark, getWidgetTitle(context, data.getMode(), category)); + views.setOnClickPendingIntent(R.id.widget_note_header_icon_dark, openAppI); + views.setOnClickPendingIntent(R.id.widget_note_list_title_tv_dark, openAppI); + views.setOnClickPendingIntent(R.id.widget_note_list_create_icon_dark, newNoteI); + views.setPendingIntentTemplate(R.id.note_list_widget_lv_dark, templatePI); + views.setRemoteAdapter(appWidgetId, R.id.note_list_widget_lv_dark, serviceIntent); + views.setEmptyView(R.id.note_list_widget_lv_dark, R.id.widget_note_list_placeholder_tv_dark); + awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.note_list_widget_lv_dark); + if (BrandingUtil.isBrandingEnabled(context)) { + views.setInt(R.id.widget_note_header_dark, "setBackgroundColor", localAccount.getColor()); + views.setInt(R.id.widget_note_header_icon_dark, "setColorFilter", localAccount.getTextColor()); + views.setInt(R.id.widget_note_list_create_icon_dark, "setColorFilter", localAccount.getTextColor()); + views.setTextColor(R.id.widget_note_list_title_tv_dark, localAccount.getTextColor()); + } else { + views.setInt(R.id.widget_note_header_dark, "setBackgroundColor", context.getResources().getColor(R.color.defaultBrand)); + views.setInt(R.id.widget_note_header_icon_dark, "setColorFilter", Color.WHITE); + views.setInt(R.id.widget_note_list_create_icon_dark, "setColorFilter", Color.WHITE); + views.setTextColor(R.id.widget_note_list_title_tv_dark, Color.WHITE); + } } else { - views.setInt(R.id.widget_note_header_dark, "setBackgroundColor", context.getResources().getColor(R.color.defaultBrand)); - views.setInt(R.id.widget_note_header_icon_dark, "setColorFilter", Color.WHITE); - views.setInt(R.id.widget_note_list_create_icon_dark, "setColorFilter", Color.WHITE); - views.setTextColor(R.id.widget_note_list_title_tv_dark, Color.WHITE); + views = new RemoteViews(context.getPackageName(), R.layout.widget_note_list); + views.setTextViewText(R.id.widget_note_list_title_tv, getWidgetTitle(context, data.getMode(), category)); + views.setOnClickPendingIntent(R.id.widget_note_header_icon, openAppI); + views.setOnClickPendingIntent(R.id.widget_note_list_title_tv, openAppI); + views.setOnClickPendingIntent(R.id.widget_note_list_create_icon, newNoteI); + views.setPendingIntentTemplate(R.id.note_list_widget_lv, templatePI); + views.setRemoteAdapter(appWidgetId, R.id.note_list_widget_lv, serviceIntent); + views.setEmptyView(R.id.note_list_widget_lv, R.id.widget_note_list_placeholder_tv); + awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.note_list_widget_lv); + if (BrandingUtil.isBrandingEnabled(context)) { + views.setInt(R.id.widget_note_header, "setBackgroundColor", localAccount.getColor()); + views.setInt(R.id.widget_note_header_icon, "setColorFilter", localAccount.getTextColor()); + views.setInt(R.id.widget_note_list_create_icon, "setColorFilter", localAccount.getTextColor()); + views.setTextColor(R.id.widget_note_list_title_tv, localAccount.getTextColor()); + } else { + views.setInt(R.id.widget_note_header, "setBackgroundColor", context.getResources().getColor(R.color.defaultBrand)); + views.setInt(R.id.widget_note_header_icon, "setColorFilter", Color.WHITE); + views.setInt(R.id.widget_note_list_create_icon, "setColorFilter", Color.WHITE); + views.setTextColor(R.id.widget_note_list_title_tv, Color.WHITE); + } } + + awm.updateAppWidget(appWidgetId, views); } else { - views = new RemoteViews(context.getPackageName(), R.layout.widget_note_list); - views.setTextViewText(R.id.widget_note_list_title_tv, getWidgetTitle(context, data.getMode(), category)); - views.setOnClickPendingIntent(R.id.widget_note_header_icon, openAppI); - views.setOnClickPendingIntent(R.id.widget_note_list_title_tv, openAppI); - views.setOnClickPendingIntent(R.id.widget_note_list_create_icon, newNoteI); - views.setPendingIntentTemplate(R.id.note_list_widget_lv, templatePI); - views.setRemoteAdapter(appWidgetId, R.id.note_list_widget_lv, serviceIntent); - views.setEmptyView(R.id.note_list_widget_lv, R.id.widget_note_list_placeholder_tv); - awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.note_list_widget_lv); - if (BrandingUtil.isBrandingEnabled(context)) { - views.setInt(R.id.widget_note_header, "setBackgroundColor", localAccount.getColor()); - views.setInt(R.id.widget_note_header_icon, "setColorFilter", localAccount.getTextColor()); - views.setInt(R.id.widget_note_list_create_icon, "setColorFilter", localAccount.getTextColor()); - views.setTextColor(R.id.widget_note_list_title_tv, localAccount.getTextColor()); - } else { - views.setInt(R.id.widget_note_header, "setBackgroundColor", context.getResources().getColor(R.color.defaultBrand)); - views.setInt(R.id.widget_note_header_icon, "setColorFilter", Color.WHITE); - views.setInt(R.id.widget_note_list_create_icon, "setColorFilter", Color.WHITE); - views.setTextColor(R.id.widget_note_list_title_tv, Color.WHITE); - } + Log.i(TAG, "onUpdate has been triggered before the user finished configuring the widget"); } - - awm.updateAppWidget(appWidgetId, views); - } else { - Log.i(TAG, "onUpdate has been triggered before the user finished configuring the widget"); - } + }).start(); } } @@ -163,7 +164,7 @@ public class NoteListWidget extends AppWidgetProvider { final NotesDatabase db = NotesDatabase.getInstance(context); for (int appWidgetId : appWidgetIds) { - db.getWidgetNotesListDao().removeNoteListWidget(appWidgetId); + new Thread(() -> db.getWidgetNotesListDao().removeNoteListWidget(appWidgetId)).start(); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetConfigurationActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetConfigurationActivity.java index eb251a9c..70d5bf4d 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetConfigurationActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetConfigurationActivity.java @@ -8,6 +8,7 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -15,13 +16,10 @@ import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundExce import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; import com.nextcloud.android.sso.helper.SingleAccountHelper; -import java.util.ArrayList; -import java.util.List; - import it.niedermann.owncloud.notes.LockedActivity; import it.niedermann.owncloud.notes.NotesApplication; import it.niedermann.owncloud.notes.R; -import it.niedermann.owncloud.notes.main.MainActivity; +import it.niedermann.owncloud.notes.databinding.ActivityNoteListConfigurationBinding; import it.niedermann.owncloud.notes.main.navigation.NavigationAdapter; import it.niedermann.owncloud.notes.main.navigation.NavigationClickListener; import it.niedermann.owncloud.notes.main.navigation.NavigationItem; @@ -29,14 +27,10 @@ import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.persistence.entity.Account; import it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData; -import static androidx.lifecycle.Transformations.distinctUntilChanged; -import static androidx.lifecycle.Transformations.map; import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData.MODE_DISPLAY_ALL; import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData.MODE_DISPLAY_CATEGORY; import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData.MODE_DISPLAY_STARRED; -import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.FAVORITES; import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.RECENT; -import static it.niedermann.owncloud.notes.shared.util.DisplayUtils.convertToCategoryNavigationItem; public class NoteListWidgetConfigurationActivity extends LockedActivity { @@ -46,6 +40,8 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity { private Account localAccount = null; + private ActivityNoteListConfigurationBinding binding; + private NoteListViewModel viewModel; private NavigationAdapter adapterCategories; private NotesDatabase db = null; @@ -56,16 +52,6 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity { setContentView(R.layout.activity_note_list_configuration); db = NotesDatabase.getInstance(this); - try { - this.localAccount = db.getAccountDao().getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(this).name); - } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); - Toast.makeText(this, R.string.widget_not_logged_in, Toast.LENGTH_LONG).show(); - // TODO Present user with app login screen - Log.w(TAG, "onCreate: user not logged in"); - finish(); - return; - } final Bundle extras = getIntent().getExtras(); if (extras != null) { @@ -78,9 +64,9 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity { finish(); } - RecyclerView recyclerView; - RecyclerView.LayoutManager layoutManager; - + viewModel = new ViewModelProvider(this).get(NoteListViewModel.class); + binding = ActivityNoteListConfigurationBinding.inflate(getLayoutInflater()); + binding.recyclerView.setAdapter(adapterCategories); adapterCategories = new NavigationAdapter(this, new NavigationClickListener() { @Override public void onItemClick(NavigationItem item) { @@ -120,14 +106,15 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity { data.setAccountId(localAccount.getId()); data.setThemeMode(NotesApplication.getAppTheme(getApplicationContext()).getModeId()); - db.getWidgetNotesListDao().createOrUpdateNoteListWidgetData(data); + new Thread(() -> { + db.getWidgetNotesListDao().createOrUpdateNoteListWidgetData(data); - Intent updateIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, - getApplicationContext(), NoteListWidget.class); - updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - setResult(RESULT_OK, updateIntent); - getApplicationContext().sendBroadcast(updateIntent); - finish(); + final Intent updateIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, getApplicationContext(), NoteListWidget.class) + .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + setResult(RESULT_OK, updateIntent); + getApplicationContext().sendBroadcast(updateIntent); + finish(); + }).start(); } public void onIconClick(NavigationItem item) { @@ -135,32 +122,18 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity { } }); - recyclerView = findViewById(R.id.recycler_view); - recyclerView.setHasFixedSize(true); - layoutManager = new LinearLayoutManager(this); - recyclerView.setLayoutManager(layoutManager); - recyclerView.setAdapter(adapterCategories); - distinctUntilChanged( - map(db.getNoteDao().getCategoriesLiveData(localAccount.getId()), fromDatabase -> { - List<NavigationItem.CategoryNavigationItem> categories = convertToCategoryNavigationItem(NoteListWidgetConfigurationActivity.this, fromDatabase); - - ArrayList<NavigationItem> items = new ArrayList<>(fromDatabase.size() + 3); - items.add(new NavigationItem(MainActivity.ADAPTER_KEY_RECENT, getString(R.string.label_all_notes), db.getNoteDao().count(localAccount.getId()), R.drawable.ic_access_time_grey600_24dp, RECENT)); - items.add(new NavigationItem(MainActivity.ADAPTER_KEY_STARRED, getString(R.string.label_favorites), db.getNoteDao().getFavoritesCount(localAccount.getId()), R.drawable.ic_star_yellow_24dp, FAVORITES)); - - if (categories.size() > 2 && categories.get(2).label.isEmpty()) { - items.add(new NavigationItem(MainActivity.ADAPTER_KEY_UNCATEGORIZED, "", null, NavigationAdapter.ICON_NOFOLDER)); - } - - for (NavigationItem item : categories) { - int slashIndex = item.label.indexOf('/'); - - item.label = slashIndex < 0 ? item.label : item.label.substring(0, slashIndex); - item.id = "category:" + item.label; - items.add(item); - } - return items; - })).observe(this, (navigationItems) -> adapterCategories.setItems(navigationItems)); + new Thread(() -> { + try { + this.localAccount = db.getAccountDao().getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(this).name); + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + e.printStackTrace(); + Toast.makeText(this, R.string.widget_not_logged_in, Toast.LENGTH_LONG).show(); + // TODO Present user with app login screen + Log.w(TAG, "onCreate: user not logged in"); + finish(); + } + runOnUiThread(() -> viewModel.getAdapterCategories(localAccount.getId()).observe(this, (navigationItems) -> adapterCategories.setItems(navigationItems))); + }).start(); } @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetFactory.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetFactory.java index 5b200f8f..72a69b18 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetFactory.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetFactory.java @@ -5,6 +5,8 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.widget.RemoteViews; import android.widget.RemoteViewsService; @@ -29,26 +31,30 @@ import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetDat public class NoteListWidgetFactory implements RemoteViewsService.RemoteViewsFactory { private static final String TAG = NoteListWidgetFactory.class.getSimpleName(); + private final NotesDatabase db; + private final int appWidgetId; private final Context context; - private final NotesListWidgetData data; - private final boolean darkTheme; - private NotesDatabase db; + private boolean darkTheme; private List<Note> noteEntities; NoteListWidgetFactory(Context context, Intent intent) { this.context = context; - final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); - db = NotesDatabase.getInstance(context); - data = db.getWidgetNotesListDao().getNoteListWidgetData(appWidgetId); - - darkTheme = NotesApplication.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode())); + this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + this.db = NotesDatabase.getInstance(context); } @Override public void onCreate() { + // NoOp + } + + @Override + public void onDataSetChanged() { final LiveData<List<Note>> noteEntitiesLiveData; try { + NotesListWidgetData data = db.getWidgetNotesListDao().getNoteListWidgetData(appWidgetId); + darkTheme = NotesApplication.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode())); Log.v(TAG, "--- data - " + data); switch (data.getMode()) { case MODE_DISPLAY_ALL: @@ -66,18 +72,14 @@ public class NoteListWidgetFactory implements RemoteViewsService.RemoteViewsFact } break; } - noteEntitiesLiveData.observeForever((notes) -> noteEntities = notes); + + new Handler(Looper.getMainLooper()).post(() -> noteEntitiesLiveData.observeForever((notes) -> noteEntities = notes)); } catch (IllegalArgumentException e) { e.printStackTrace(); } } @Override - public void onDataSetChanged() { - // NoOp - } - - @Override public void onDestroy() { // NoOp } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidget.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidget.java index 9791594a..3637f72d 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidget.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidget.java @@ -27,36 +27,38 @@ public class SingleNoteWidget extends AppWidgetProvider { final NotesDatabase db = NotesDatabase.getInstance(context); for (int appWidgetId : appWidgetIds) { - final SingleNoteWidgetData data = db.getWidgetSingleNoteDao().getSingleNoteWidgetData(appWidgetId); - if (data != null) { - templateIntent.putExtra(BaseNoteFragment.PARAM_ACCOUNT_ID, data.getAccountId()); - - final PendingIntent templatePendingIntent = PendingIntent.getActivity(context, appWidgetId, templateIntent, - PendingIntent.FLAG_UPDATE_CURRENT); - - Intent serviceIntent = new Intent(context, SingleNoteWidgetService.class); - serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME))); - - RemoteViews views; - - if (NotesApplication.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode()))) { - views = new RemoteViews(context.getPackageName(), R.layout.widget_single_note_dark); - views.setPendingIntentTemplate(R.id.single_note_widget_lv_dark, templatePendingIntent); - views.setRemoteAdapter(R.id.single_note_widget_lv_dark, serviceIntent); - views.setEmptyView(R.id.single_note_widget_lv_dark, R.id.widget_single_note_placeholder_tv_dark); - awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.single_note_widget_lv_dark); + new Thread(() -> { + final SingleNoteWidgetData data = db.getWidgetSingleNoteDao().getSingleNoteWidgetData(appWidgetId); + if (data != null) { + templateIntent.putExtra(BaseNoteFragment.PARAM_ACCOUNT_ID, data.getAccountId()); + + final PendingIntent templatePendingIntent = PendingIntent.getActivity(context, appWidgetId, templateIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + Intent serviceIntent = new Intent(context, SingleNoteWidgetService.class); + serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME))); + + RemoteViews views; + + if (NotesApplication.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode()))) { + views = new RemoteViews(context.getPackageName(), R.layout.widget_single_note_dark); + views.setPendingIntentTemplate(R.id.single_note_widget_lv_dark, templatePendingIntent); + views.setRemoteAdapter(R.id.single_note_widget_lv_dark, serviceIntent); + views.setEmptyView(R.id.single_note_widget_lv_dark, R.id.widget_single_note_placeholder_tv_dark); + awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.single_note_widget_lv_dark); + } else { + views = new RemoteViews(context.getPackageName(), R.layout.widget_single_note); + views.setPendingIntentTemplate(R.id.single_note_widget_lv, templatePendingIntent); + views.setRemoteAdapter(R.id.single_note_widget_lv, serviceIntent); + views.setEmptyView(R.id.single_note_widget_lv, R.id.widget_single_note_placeholder_tv); + awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.single_note_widget_lv); + } + awm.updateAppWidget(appWidgetId, views); } else { - views = new RemoteViews(context.getPackageName(), R.layout.widget_single_note); - views.setPendingIntentTemplate(R.id.single_note_widget_lv, templatePendingIntent); - views.setRemoteAdapter(R.id.single_note_widget_lv, serviceIntent); - views.setEmptyView(R.id.single_note_widget_lv, R.id.widget_single_note_placeholder_tv); - awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.single_note_widget_lv); + Log.i(TAG, "onUpdate has been triggered before the user finished configuring the widget"); } - awm.updateAppWidget(appWidgetId, views); - } else { - Log.i(TAG, "onUpdate has been triggered before the user finished configuring the widget"); - } + }).start(); } } @@ -80,7 +82,7 @@ public class SingleNoteWidget extends AppWidgetProvider { final NotesDatabase db = NotesDatabase.getInstance(context); for (int appWidgetId : appWidgetIds) { - db.getWidgetSingleNoteDao().removeSingleNoteWidget(appWidgetId); + new Thread(() -> db.getWidgetSingleNoteDao().removeSingleNoteWidget(appWidgetId)).start(); } super.onDeleted(context, appWidgetIds); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetFactory.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetFactory.java index 49783eae..ea36d705 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetFactory.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetFactory.java @@ -31,15 +31,14 @@ public class SingleNoteWidgetFactory implements RemoteViewsService.RemoteViewsFa SingleNoteWidgetFactory(Context context, Intent intent) { this.context = context; - this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, - AppWidgetManager.INVALID_APPWIDGET_ID); + this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); this.db = NotesDatabase.getInstance(context); - final SingleNoteWidgetData data = db.getWidgetSingleNoteDao().getSingleNoteWidgetData(appWidgetId); - if (data != null) { - darkModeActive = NotesApplication.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode())); - } else { - Log.w(TAG, "Widget with ID " + appWidgetId + " seems to be not configured yet."); - } +// final SingleNoteWidgetData data = db.getWidgetSingleNoteDao().getSingleNoteWidgetData(appWidgetId); +// if (data != null) { +// darkModeActive = NotesApplication.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode())); +// } else { +// Log.w(TAG, "Widget with ID " + appWidgetId + " seems to be not configured yet."); +// } } @Override @@ -51,6 +50,7 @@ public class SingleNoteWidgetFactory implements RemoteViewsService.RemoteViewsFa public void onDataSetChanged() { final SingleNoteWidgetData data = db.getWidgetSingleNoteDao().getSingleNoteWidgetData(appWidgetId); if (data != null) { + darkModeActive = NotesApplication.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode())); final long noteId = data.getNoteId(); Log.v(TAG, "Fetch note with id " + noteId); note = db.getNoteDao().getNoteById(noteId); |