diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java index 38e32ade..9f96fbde 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java @@ -49,7 +49,6 @@ import com.nextcloud.android.sso.model.SingleSignOnAccount; import java.net.HttpURLConnection; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import it.niedermann.owncloud.notes.FormattingHelpActivity; @@ -74,12 +73,9 @@ import it.niedermann.owncloud.notes.main.items.list.NotesListViewItemTouchHelper import it.niedermann.owncloud.notes.main.items.section.SectionItemDecoration; import it.niedermann.owncloud.notes.persistence.CapabilitiesClient; import it.niedermann.owncloud.notes.persistence.CapabilitiesWorker; -import it.niedermann.owncloud.notes.persistence.LoadNotesListTask; -import it.niedermann.owncloud.notes.persistence.LoadNotesListTask.NotesLoadedListener; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper.ViewProvider; import it.niedermann.owncloud.notes.persistence.NotesDatabase; -import it.niedermann.owncloud.notes.persistence.dao.AccountDao; import it.niedermann.owncloud.notes.persistence.entity.Account; import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory; @@ -164,14 +160,11 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V swipeRefreshLayout.setRefreshing(false); }; - private LiveData> noteWithCategoryLiveData; - private Observer> noteWithCategoryObserver = notes -> { + private LiveData> noteWithCategoryLiveData; + private Observer> noteWithCategoryObserver = notes -> { adapter.setShowCategory(mainViewModel.getSelectedCategory().getValue() == null); adapter.setHighlightSearchQuery(mainViewModel.getSearchTerm().getValue()); - List items = new ArrayList<>(); - items.addAll(notes); - // yes, NoteWithCatecory are Items, check. BUT: setItemList expects an List, the Elements in notes ARE Items, but the List-type isn't correct, since you pass the List, not the items! - adapter.setItemList(items); + adapter.setItemList(notes); binding.activityNotesListView.progressCircular.setVisibility(GONE); binding.activityNotesListView.emptyContentView.getRoot().setVisibility(notes.size() > 0 ? GONE : VISIBLE); // if (scrollToTop) { @@ -196,18 +189,46 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V this.fabCreate = binding.activityNotesListView.fabCreate; this.listView = binding.activityNotesListView.recyclerView; + mainViewModel.getSelectedCategory().observe(this, (category) -> { + View emptyContentView = binding.activityNotesListView.emptyContentView.getRoot(); + emptyContentView.setVisibility(GONE); + binding.activityNotesListView.progressCircular.setVisibility(VISIBLE); + this.navigationSelection = category; + fabCreate.show(); + String subtitle; + if (navigationSelection.category != null) { + if (navigationSelection.category.isEmpty()) { + subtitle = getString(R.string.search_in_category, getString(R.string.action_uncategorized)); + } else { + subtitle = getString(R.string.search_in_category, NoteUtil.extendCategory(navigationSelection.category)); + } + } else if (navigationSelection.favorite != null && navigationSelection.favorite) { + subtitle = getString(R.string.search_in_category, getString(R.string.label_favorites)); + } else { + subtitle = getString(R.string.search_in_all); + } + activityBinding.searchText.setText(subtitle); + }); + mainViewModel.filterChanged().observe(this, (v) -> { + if (noteWithCategoryLiveData != null) { + noteWithCategoryLiveData.removeObserver(noteWithCategoryObserver); + } + noteWithCategoryLiveData = mainViewModel.getNotesListLiveData(); + noteWithCategoryLiveData.observe(this, noteWithCategoryObserver); + }); + String categoryAdapterSelectedItem = ADAPTER_KEY_RECENT; if (savedInstanceState == null) { if (ACTION_RECENT.equals(getIntent().getAction())) { categoryAdapterSelectedItem = ADAPTER_KEY_RECENT; } else if (ACTION_FAVORITES.equals(getIntent().getAction())) { categoryAdapterSelectedItem = ADAPTER_KEY_STARRED; - navigationSelection = new OldCategory(null, true); + mainViewModel.postSelectedCategory(new OldCategory(null, true)); } } else { Object savedCategory = savedInstanceState.getSerializable(SAVED_STATE_NAVIGATION_SELECTION); if (savedCategory != null) { - navigationSelection = (OldCategory) savedCategory; + mainViewModel.postSelectedCategory((OldCategory) savedCategory); } navigationOpen = savedInstanceState.getString(SAVED_STATE_NAVIGATION_OPEN); categoryAdapterSelectedItem = savedInstanceState.getString(SAVED_STATE_NAVIGATION_ADAPTER_SLECTION); @@ -220,11 +241,6 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V activityBinding.activityNotesListView.setBackgroundColor(ContextCompat.getColor(this, R.color.primary)); } - AccountDao dao = db.getAccountDao(); - new Thread(() -> { - List localAccountEntities = dao.getAccounts(); - Log.v("TEST", localAccountEntities.size() + " acs"); - }).start(); setupToolbars(); setupNavigationList(categoryAdapterSelectedItem); setupNavigationMenu(); @@ -245,6 +261,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V List localAccounts = db.getAccountDao().getAccounts(); if (localAccounts.size() > 0) { localAccount = localAccounts.get(0); + mainViewModel.postCurrentAccount(localAccount); } } if (!notAuthorizedAccountHandled) { @@ -275,6 +292,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V fabCreate.hide(); SingleAccountHelper.setCurrentAccount(getApplicationContext(), accountName); localAccount = db.getAccountDao().getLocalAccountByAccountName(accountName); + mainViewModel.postCurrentAccount(localAccount); if (localAccount != null) { try { BrandingUtil.saveBrandColors(this, localAccount.getColor(), localAccount.getTextColor()); @@ -361,7 +379,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V @Override public boolean onQueryTextChange(String newText) { - refreshLists(); + mainViewModel.postSearchTerm(newText); return true; } }); @@ -457,13 +475,13 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V adapterCategories.setSelectedItem(item.id); // update current selection if (itemRecent.equals(item)) { - navigationSelection = new OldCategory(null, null); + mainViewModel.postSelectedCategory(new OldCategory(null, null)); } else if (itemFavorites.equals(item)) { - navigationSelection = new OldCategory(null, true); + mainViewModel.postSelectedCategory(new OldCategory(null, true)); } else if (itemUncategorized != null && itemUncategorized.equals(item)) { - navigationSelection = new OldCategory("", null); + mainViewModel.postSelectedCategory(new OldCategory("", null)); } else { - navigationSelection = new OldCategory(item.label, null); + mainViewModel.postSelectedCategory(new OldCategory(item.label, null)); } // auto-close sub-folder in Navigation if selection is outside of that folder @@ -686,43 +704,43 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V adapter.removeAll(); return; } - View emptyContentView = binding.activityNotesListView.emptyContentView.getRoot(); - emptyContentView.setVisibility(GONE); - binding.activityNotesListView.progressCircular.setVisibility(VISIBLE); - fabCreate.show(); - String subtitle; - if (navigationSelection.category != null) { - if (navigationSelection.category.isEmpty()) { - subtitle = getString(R.string.search_in_category, getString(R.string.action_uncategorized)); - } else { - subtitle = getString(R.string.search_in_category, NoteUtil.extendCategory(navigationSelection.category)); - } - } else if (navigationSelection.favorite != null && navigationSelection.favorite) { - subtitle = getString(R.string.search_in_category, getString(R.string.label_favorites)); - } else { - subtitle = getString(R.string.search_in_all); - } - activityBinding.searchText.setText(subtitle); - CharSequence query = null; - if (activityBinding.searchView.getQuery().length() != 0) { - query = activityBinding.searchView.getQuery(); - } +// View emptyContentView = binding.activityNotesListView.emptyContentView.getRoot(); +// emptyContentView.setVisibility(GONE); +// binding.activityNotesListView.progressCircular.setVisibility(VISIBLE); +// fabCreate.show(); +// String subtitle; +// if (navigationSelection.category != null) { +// if (navigationSelection.category.isEmpty()) { +// subtitle = getString(R.string.search_in_category, getString(R.string.action_uncategorized)); +// } else { +// subtitle = getString(R.string.search_in_category, NoteUtil.extendCategory(navigationSelection.category)); +// } +// } else if (navigationSelection.favorite != null && navigationSelection.favorite) { +// subtitle = getString(R.string.search_in_category, getString(R.string.label_favorites)); +// } else { +// subtitle = getString(R.string.search_in_all); +// } +// activityBinding.searchText.setText(subtitle); +// CharSequence query = null; +// if (activityBinding.searchView.getQuery().length() != 0) { +// query = activityBinding.searchView.getQuery(); +// } - NotesLoadedListener callback = (List notes, boolean showCategory, CharSequence searchQuery) -> { - adapter.setShowCategory(showCategory); - adapter.setHighlightSearchQuery(searchQuery); - adapter.setItemList(notes); - binding.activityNotesListView.progressCircular.setVisibility(GONE); - if (notes.size() > 0) { - emptyContentView.setVisibility(GONE); - } else { - emptyContentView.setVisibility(VISIBLE); - } - if (scrollToTop) { - listView.scrollToPosition(0); - } - }; - new LoadNotesListTask(localAccount.getId(), getApplicationContext(), callback, navigationSelection, query).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); +// NotesLoadedListener callback = (List notes, boolean showCategory, CharSequence searchQuery) -> { +// adapter.setShowCategory(showCategory); +// adapter.setHighlightSearchQuery(searchQuery); +// adapter.setItemList(notes); +// binding.activityNotesListView.progressCircular.setVisibility(GONE); +// if (notes.size() > 0) { +// emptyContentView.setVisibility(GONE); +// } else { +// emptyContentView.setVisibility(VISIBLE); +// } +// if (scrollToTop) { +// listView.scrollToPosition(0); +// } +// }; +// new LoadNotesListTask(localAccount.getId(), getApplicationContext(), callback, navigationSelection, query).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new LoadCategoryListTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); updateSortMethodIcon(localAccount.getId()); @@ -984,6 +1002,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V List remainingAccounts = db.getAccountDao().getAccounts(); if (remainingAccounts.size() > 0) { this.localAccount = remainingAccounts.get(0); + mainViewModel.postCurrentAccount(localAccount); selectAccount(this.localAccount.getAccountName()); } else { selectAccount(null); 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 d627547c..4f80ed85 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 @@ -3,20 +3,25 @@ package it.niedermann.owncloud.notes.main; import android.app.Application; import androidx.annotation.NonNull; +import androidx.arch.core.util.Function; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MediatorLiveData; import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Transformations; import java.util.List; import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.persistence.entity.Account; import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory; +import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod; import it.niedermann.owncloud.notes.shared.model.Item; import it.niedermann.owncloud.notes.shared.model.OldCategory; -import static androidx.lifecycle.Transformations.distinctUntilChanged; +import static it.niedermann.owncloud.notes.main.slots.SlotterUtil.fillListByCategory; +import static it.niedermann.owncloud.notes.main.slots.SlotterUtil.fillListByInitials; +import static it.niedermann.owncloud.notes.main.slots.SlotterUtil.fillListByTime; public class MainViewModel extends AndroidViewModel { @@ -28,9 +33,7 @@ public class MainViewModel extends AndroidViewModel { @NonNull private MutableLiveData searchTerm = new MutableLiveData<>(); @NonNull - private MutableLiveData selectedCategory = new MutableLiveData<>(); - - private MediatorLiveData> notesListLiveData = new MediatorLiveData<>(); + private MutableLiveData selectedCategory = new MutableLiveData<>(new OldCategory(null, null)); public MainViewModel(@NonNull Application application) { super(application); @@ -49,6 +52,10 @@ public class MainViewModel extends AndroidViewModel { this.selectedCategory.postValue(selectedCategory); } + public LiveData getCurrentAccount() { + return currentAccount; + } + public LiveData getSearchTerm() { return searchTerm; } @@ -59,13 +66,44 @@ public class MainViewModel extends AndroidViewModel { public LiveData filterChanged() { MediatorLiveData mediatorLiveData = new MediatorLiveData<>(); - mediatorLiveData.addSource(distinctUntilChanged(currentAccount), (o) -> mediatorLiveData.postValue(null)); - mediatorLiveData.addSource(distinctUntilChanged(searchTerm), (o) -> mediatorLiveData.postValue(null)); - mediatorLiveData.addSource(distinctUntilChanged(selectedCategory), (o) -> mediatorLiveData.postValue(null)); + mediatorLiveData.addSource(currentAccount, (o) -> mediatorLiveData.postValue(null)); + mediatorLiveData.addSource(searchTerm, (o) -> mediatorLiveData.postValue(null)); + mediatorLiveData.addSource(selectedCategory, (o) -> mediatorLiveData.postValue(null)); return mediatorLiveData; } public LiveData> getNotesListLiveData() { - return notesListLiveData; + Account currentAccount = getCurrentAccount().getValue(); + OldCategory selectedCategory = getSelectedCategory().getValue(); + LiveData> fromDatabase; + if (currentAccount != null && selectedCategory != null) { + Long accountId = currentAccount.getId(); + CategorySortingMethod sortingMethod = db.getCategoryOrder(accountId, selectedCategory); + String searchQuery = getSearchTerm().getValue(); + searchQuery = searchQuery == null ? "%" : "%" + searchQuery.trim() + "%"; + if (Boolean.TRUE.equals(selectedCategory.favorite)) { // Favorites + fromDatabase = db.getNoteDao().searchNotesFavorites(accountId, searchQuery, sortingMethod.getSorder()); + } else if (selectedCategory.category != null && selectedCategory.category.length() == 0) { // Uncategorized + fromDatabase = db.getNoteDao().searchNotesUncategorized(accountId, searchQuery, sortingMethod.getSorder()); + } else if( selectedCategory.category == null && selectedCategory.favorite == null) { // Recent + fromDatabase = db.getNoteDao().searchNotesAll(accountId, searchQuery, sortingMethod.getSorder()); + } else { // A special category + fromDatabase = db.getNoteDao().searchNotesByCategory(accountId, searchQuery, selectedCategory.category, sortingMethod.getSorder()); + } + + return Transformations.map(fromDatabase, (Function, List>) noteList -> { + if (selectedCategory.category == null) { + if (sortingMethod == CategorySortingMethod.SORT_MODIFIED_DESC) { + return fillListByTime(getApplication(), noteList); + } else { + return fillListByInitials(getApplication(), noteList); + } + } else { + return fillListByCategory(noteList, selectedCategory.category); + } + }); + } else { + return new MutableLiveData<>(); + } } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/slots/SlotterUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/main/slots/SlotterUtil.java new file mode 100644 index 00000000..3fd27a9a --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/slots/SlotterUtil.java @@ -0,0 +1,74 @@ +package it.niedermann.owncloud.notes.main.slots; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.main.items.section.SectionItem; +import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory; +import it.niedermann.owncloud.notes.shared.model.Item; +import it.niedermann.owncloud.notes.shared.util.NoteUtil; + +public class SlotterUtil { + + private SlotterUtil() { + // Util class + } + + @NonNull + public static List fillListByCategory(@NonNull List noteList, @Nullable String currentCategory) { + List itemList = new ArrayList<>(); + for (NoteWithCategory note : noteList) { + if (currentCategory != null && !currentCategory.equals(note.getCategory())) { + itemList.add(new SectionItem(NoteUtil.extendCategory(note.getCategory()))); + } + + itemList.add(note); + currentCategory = note.getCategory(); + } + return itemList; + } + + @NonNull + public static List fillListByTime(@NonNull Context context, @NonNull List noteList) { + List itemList = new ArrayList<>(); + Timeslotter timeslotter = new Timeslotter(context); + String lastTimeslot = null; + for (int i = 0; i < noteList.size(); i++) { + NoteWithCategory currentNote = noteList.get(i); + String timeslot = timeslotter.getTimeslot(currentNote.getNote()); + if (i > 0 && !timeslot.equals(lastTimeslot)) { + itemList.add(new SectionItem(timeslot)); + } + itemList.add(currentNote); + lastTimeslot = timeslot; + } + + return itemList; + } + + @NonNull + public static List fillListByInitials(@NonNull Context context, @NonNull List noteList) { + List itemList = new ArrayList<>(); + String lastInitials = null; + for (int i = 0; i < noteList.size(); i++) { + NoteWithCategory currentNote = noteList.get(i); + String initials = currentNote.getNote().getTitle().substring(0, 1).toUpperCase(); + if (!initials.matches("[A-Z\\u00C0-\\u00DF]")) { + initials = initials.matches("[\\u0250-\\uFFFF]") ? context.getString(R.string.simple_other) : "#"; + } + if (i > 0 && !initials.equals(lastInitials)) { + itemList.add(new SectionItem(initials)); + } + itemList.add(currentNote); + lastInitials = initials; + } + + return itemList; + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/slots/Timeslot.java b/app/src/main/java/it/niedermann/owncloud/notes/main/slots/Timeslot.java new file mode 100644 index 00000000..562a2985 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/slots/Timeslot.java @@ -0,0 +1,22 @@ +package it.niedermann.owncloud.notes.main.slots; + +import java.util.Calendar; + +public class Timeslot { + private final String label; + private final Calendar time; + + Timeslot(String label, int month, int day) { + this.label = label; + this.time = Calendar.getInstance(); + this.time.set(this.time.get(Calendar.YEAR), month, day, 0, 0, 0); + } + + public String getLabel() { + return label; + } + + public Calendar getTime() { + return time; + } +} \ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/slots/Timeslotter.java b/app/src/main/java/it/niedermann/owncloud/notes/main/slots/Timeslotter.java new file mode 100644 index 00000000..988311bc --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/main/slots/Timeslotter.java @@ -0,0 +1,53 @@ +package it.niedermann.owncloud.notes.main.slots; + +import android.content.Context; +import android.text.format.DateUtils; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.persistence.entity.Note; + +public class Timeslotter { + private final List timeslots = new ArrayList<>(); + private final Calendar lastYear; + private final Context context; + + public Timeslotter(@NonNull Context context) { + this.context = context; + Calendar now = Calendar.getInstance(); + int month = now.get(Calendar.MONTH); + int day = now.get(Calendar.DAY_OF_MONTH); + int offsetWeekStart = (now.get(Calendar.DAY_OF_WEEK) - now.getFirstDayOfWeek() + 7) % 7; + timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_today), month, day)); + timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_yesterday), month, day - 1)); + timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_this_week), month, day - offsetWeekStart)); + timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_last_week), month, day - offsetWeekStart - 7)); + timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_this_month), month, 1)); + timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_last_month), month - 1, 1)); + lastYear = Calendar.getInstance(); + lastYear.set(now.get(Calendar.YEAR) - 1, 0, 1, 0, 0, 0); + } + + public String getTimeslot(Note note) { + if (note.getFavorite()) { + return ""; + } + Calendar modified = note.getModified(); + for (Timeslot timeslot : timeslots) { + if (!modified.before(timeslot.getTime())) { + return timeslot.getLabel(); + } + } + if (!modified.before(this.lastYear)) { + // use YEAR and MONTH in a format based on current locale + return DateUtils.formatDateTime(context, modified.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_NO_MONTH_DAY); + } else { + return Integer.toString(modified.get(Calendar.YEAR)); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java index 93a392cd..094d09d7 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java @@ -1,179 +1,75 @@ -package it.niedermann.owncloud.notes.persistence; - -import android.content.Context; -import android.os.AsyncTask; -import android.text.TextUtils; -import android.text.format.DateUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -import it.niedermann.owncloud.notes.R; -import it.niedermann.owncloud.notes.main.items.section.SectionItem; -import it.niedermann.owncloud.notes.persistence.entity.Note; -import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory; -import it.niedermann.owncloud.notes.shared.model.OldCategory; -import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod; -import it.niedermann.owncloud.notes.shared.model.Item; -import it.niedermann.owncloud.notes.shared.util.NoteUtil; - -public class LoadNotesListTask extends AsyncTask> { - - private final Context context; - private final NotesLoadedListener callback; - private final OldCategory category; - private final String searchQuery; - private final long accountId; - - public LoadNotesListTask(long accountId, @NonNull Context context, @NonNull NotesLoadedListener callback, @NonNull OldCategory category, @Nullable CharSequence searchQuery) { - this.context = context; - this.callback = callback; - this.category = category; - this.searchQuery = searchQuery == null ? "%" : "%" + searchQuery + "%"; - this.accountId = accountId; - } - - @Override - protected List doInBackground(Void... voids) { - List noteList; - NotesDatabase db = NotesDatabase.getInstance(context); - CategorySortingMethod sortingMethod = db.getCategoryOrder(accountId, category); - - if(Boolean.TRUE.equals(category.favorite)) { - noteList = db.getNoteDao().searchNotesByCategoryFavorites(accountId, searchQuery, sortingMethod); - } else if(TextUtils.isEmpty(category.category)) { - noteList = db.getNoteDao().searchNotesByUncategorized(accountId, searchQuery, sortingMethod); - } else { - noteList = db.getNoteDao().searchNotesByCategory(accountId, searchQuery, category.category, sortingMethod); - } - - if (category.category == null) { - if (sortingMethod == CategorySortingMethod.SORT_MODIFIED_DESC) { - return fillListByTime(noteList); - } else { - return fillListByInitials(noteList); - } - } else { - return fillListByCategory(noteList); - } - } - - @NonNull - @WorkerThread - private List fillListByCategory(@NonNull List noteList) { - List itemList = new ArrayList<>(); - String currentCategory = category.category; - for (NoteWithCategory note : noteList) { - if (currentCategory != null && !currentCategory.equals(note.getCategory())) { - itemList.add(new SectionItem(NoteUtil.extendCategory(note.getCategory()))); - } - - itemList.add(note); - currentCategory = note.getCategory(); - } - return itemList; - } - - @NonNull - @WorkerThread - private List fillListByTime(@NonNull List noteList) { - List itemList = new ArrayList<>(); - Timeslotter timeslotter = new Timeslotter(); - String lastTimeslot = null; - for (int i = 0; i < noteList.size(); i++) { - NoteWithCategory currentNote = noteList.get(i); - String timeslot = timeslotter.getTimeslot(currentNote.getNote()); - if (i > 0 && !timeslot.equals(lastTimeslot)) { - itemList.add(new SectionItem(timeslot)); - } - itemList.add(currentNote); - lastTimeslot = timeslot; - } - - return itemList; - } - - @NonNull - @WorkerThread - private List fillListByInitials(@NonNull List noteList) { - List itemList = new ArrayList<>(); - String lastInitials = null; - for (int i = 0; i < noteList.size(); i++) { - NoteWithCategory currentNote = noteList.get(i); - String initials = currentNote.getNote().getTitle().substring(0, 1).toUpperCase(); - if (!initials.matches("[A-Z\\u00C0-\\u00DF]")) { - initials = initials.matches("[\\u0250-\\uFFFF]") ? context.getString(R.string.simple_other) : "#"; - } - if (i > 0 && !initials.equals(lastInitials)) { - itemList.add(new SectionItem(initials)); - } - itemList.add(currentNote); - lastInitials = initials; - } - - return itemList; - } - - @Override - protected void onPostExecute(List items) { - callback.onNotesLoaded(items, category.category == null, searchQuery); - } - - public interface NotesLoadedListener { - void onNotesLoaded(List notes, boolean showCategory, CharSequence searchQuery); - } - - private class Timeslotter { - private final List timeslots = new ArrayList<>(); - private final Calendar lastYear; - - Timeslotter() { - Calendar now = Calendar.getInstance(); - int month = now.get(Calendar.MONTH); - int day = now.get(Calendar.DAY_OF_MONTH); - int offsetWeekStart = (now.get(Calendar.DAY_OF_WEEK) - now.getFirstDayOfWeek() + 7) % 7; - timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_today), month, day)); - timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_yesterday), month, day - 1)); - timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_this_week), month, day - offsetWeekStart)); - timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_last_week), month, day - offsetWeekStart - 7)); - timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_this_month), month, 1)); - timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_last_month), month - 1, 1)); - lastYear = Calendar.getInstance(); - lastYear.set(now.get(Calendar.YEAR) - 1, 0, 1, 0, 0, 0); - } - - private String getTimeslot(Note note) { - if (note.getFavorite()) { - return ""; - } - Calendar modified = note.getModified(); - for (Timeslot timeslot : timeslots) { - if (!modified.before(timeslot.time)) { - return timeslot.label; - } - } - if (!modified.before(this.lastYear)) { - // use YEAR and MONTH in a format based on current locale - return DateUtils.formatDateTime(context, modified.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_NO_MONTH_DAY); - } else { - return Integer.toString(modified.get(Calendar.YEAR)); - } - } - - private class Timeslot { - private final String label; - private final Calendar time; - - Timeslot(String label, int month, int day) { - this.label = label; - this.time = Calendar.getInstance(); - this.time.set(this.time.get(Calendar.YEAR), month, day, 0, 0, 0); - } - } - } -} +//package it.niedermann.owncloud.notes.persistence; +// +//import android.content.Context; +//import android.os.AsyncTask; +//import android.text.TextUtils; +//import android.text.format.DateUtils; +// +//import androidx.annotation.NonNull; +//import androidx.annotation.Nullable; +//import androidx.annotation.WorkerThread; +// +//import java.util.ArrayList; +//import java.util.Calendar; +//import java.util.List; +// +//import it.niedermann.owncloud.notes.R; +//import it.niedermann.owncloud.notes.main.items.section.SectionItem; +//import it.niedermann.owncloud.notes.persistence.entity.Note; +//import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory; +//import it.niedermann.owncloud.notes.shared.model.OldCategory; +//import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod; +//import it.niedermann.owncloud.notes.shared.model.Item; +//import it.niedermann.owncloud.notes.shared.util.NoteUtil; +// +//public class LoadNotesListTask extends AsyncTask> { +// +// private final Context context; +// private final NotesLoadedListener callback; +// private final OldCategory category; +// private final String searchQuery; +// private final long accountId; +// +// public LoadNotesListTask(long accountId, @NonNull Context context, @NonNull NotesLoadedListener callback, @NonNull OldCategory category, @Nullable CharSequence searchQuery) { +// this.context = context; +// this.callback = callback; +// this.category = category; +// this.searchQuery = searchQuery == null ? "%" : "%" + searchQuery + "%"; +// this.accountId = accountId; +// } +// +// @Override +// protected List doInBackground(Void... voids) { +// List noteList; +// NotesDatabase db = NotesDatabase.getInstance(context); +// CategorySortingMethod sortingMethod = db.getCategoryOrder(accountId, category); +// +// if(Boolean.TRUE.equals(category.favorite)) { +// noteList = db.getNoteDao().searchNotesByCategoryFavoritesDirectly(accountId, searchQuery, sortingMethod); +// } else if(TextUtils.isEmpty(category.category)) { +// noteList = db.getNoteDao().searchNotesByUncategorizedDirectly(accountId, searchQuery, sortingMethod); +// } else { +// noteList = db.getNoteDao().searchNotesByCategoryDirectly(accountId, searchQuery, category.category, sortingMethod); +// } +// +//// if (category.category == null) { +//// if (sortingMethod == CategorySortingMethod.SORT_MODIFIED_DESC) { +//// return fillListByTime(noteList); +//// } else { +//// return fillListByInitials(noteList); +//// } +//// } else { +//// return fillListByCategory(noteList); +//// } +// } +// +// @Override +// protected void onPostExecute(List items) { +// callback.onNotesLoaded(items, category.category == null, searchQuery); +// } +// +// public interface NotesLoadedListener { +// void onNotesLoaded(List notes, boolean showCategory, CharSequence searchQuery); +// } +// } +//} 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 49278a56..6a60ea89 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 @@ -366,7 +366,7 @@ public class NoteServerSyncHelper { switch (note.getStatus()) { case LOCAL_EDITED: Log.v(TAG, " ...create/edit"); - if (note.getRemoteId() > 0) { + if (note.getRemoteId() != null && note.getRemoteId() > 0) { Log.v(TAG, " ...Note has remoteId → try to edit"); try { remoteNote = notesClient.editNote(ssoAccount, note).getNote(); 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 72cd3ffa..0ca91a9f 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 @@ -1,5 +1,6 @@ package it.niedermann.owncloud.notes.persistence.dao; +import androidx.lifecycle.LiveData; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.OnConflictStrategy; @@ -31,15 +32,19 @@ public interface NoteDao { @Query("SELECT *, NOTE.title, CATEGORY.title as 'category' FROM NOTE INNER JOIN CATEGORY ON categoryId = CATEGORY.id WHERE NOTE.accountId = :accountId AND status != 'LOCAL_DELETED' AND ( " + "NOTE.title LIKE :query OR content LIKE :query OR CATEGORY.title LIKE :query) AND (CATEGORY.title = :category OR CATEGORY.title LIKE :category + '/%' " + ") ORDER BY categoryId, favorite DESC, :sortingMethod") - List searchNotesByCategory(long accountId, String query, String category, CategorySortingMethod sortingMethod); + LiveData> searchNotesByCategory(long accountId, String query, String category, String sortingMethod); @Query("SELECT *, NOTE.title, CATEGORY.title as 'category' FROM NOTE INNER JOIN CATEGORY ON categoryId = CATEGORY.id WHERE NOTE.accountId = :accountId AND status != 'LOCAL_DELETED' AND ( " + "NOTE.title LIKE :query OR content LIKE :query OR CATEGORY.title LIKE :query) AND favorite = 1 ORDER BY categoryId DESC, :sortingMethod") - List searchNotesByCategoryFavorites(long accountId, String query, CategorySortingMethod sortingMethod); + LiveData> searchNotesFavorites(long accountId, String query, String sortingMethod); @Query("SELECT *, NOTE.title, CATEGORY.title as 'category' FROM NOTE INNER JOIN CATEGORY ON categoryId = CATEGORY.id WHERE NOTE.accountId = :accountId AND status != 'LOCAL_DELETED' AND ( " + - "NOTE.title LIKE :query OR content LIKE :query) OR CATEGORY.title = '' ORDER BY categoryId DESC, :sortingMethod") - List searchNotesByUncategorized(long accountId, String query, CategorySortingMethod sortingMethod); + "NOTE.title LIKE :query OR content LIKE :query) AND CATEGORY.title = '' ORDER BY categoryId DESC, :sortingMethod") + LiveData> searchNotesUncategorized(long accountId, String query, String sortingMethod); + + @Query("SELECT *, NOTE.title, CATEGORY.title as 'category' FROM NOTE INNER JOIN CATEGORY ON categoryId = CATEGORY.id WHERE NOTE.accountId = :accountId AND status != 'LOCAL_DELETED' AND ( " + + "NOTE.title LIKE :query OR content LIKE :query OR CATEGORY.title LIKE :query) ORDER BY categoryId DESC, :sortingMethod") + LiveData> searchNotesAll(long accountId, String query, String sortingMethod); /**