#831 Migrate from SQLiteOpenHelper to Room

List widget
This commit is contained in:
Stefan Niedermann 2020-10-10 17:40:31 +02:00
parent 287300a94f
commit cd90f4dc84
8 changed files with 77 additions and 91 deletions

View file

@ -113,6 +113,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, V
public static final String CREATED_NOTE = "it.niedermann.owncloud.notes.created_notes"; public static final String CREATED_NOTE = "it.niedermann.owncloud.notes.created_notes";
public static final String ADAPTER_KEY_RECENT = "recent"; public static final String ADAPTER_KEY_RECENT = "recent";
public static final String ADAPTER_KEY_STARRED = "starred"; public static final String ADAPTER_KEY_STARRED = "starred";
public static final String ADAPTER_KEY_UNCATEGORIZED = "uncategorized";
protected ItemAdapter adapter; protected ItemAdapter adapter;

View file

@ -157,8 +157,7 @@ public class MainViewModel extends AndroidViewModel {
@NonNull @NonNull
public LiveData<List<NavigationItem>> getNavigationCategories(String navigationOpen) { public LiveData<List<NavigationItem>> getNavigationCategories(String navigationOpen) {
Account currentAccount = getCurrentAccount().getValue(); Account currentAccount = getCurrentAccount().getValue();
NavigationCategory selectedCategory = getSelectedCategory().getValue(); if (currentAccount != null) {
if (currentAccount != null && selectedCategory != null) {
return distinctUntilChanged( return distinctUntilChanged(
map(db.getCategoryDao().getCategoriesLiveData(currentAccount.getId()), fromDatabase -> { map(db.getCategoryDao().getCategoriesLiveData(currentAccount.getId()), fromDatabase -> {
List<NavigationItem.CategoryNavigationItem> categories = convertToCategoryNavigationItem(getApplication(), db.getCategoryDao().getCategories(currentAccount.getId())); List<NavigationItem.CategoryNavigationItem> categories = convertToCategoryNavigationItem(getApplication(), db.getCategoryDao().getCategories(currentAccount.getId()));

View file

@ -17,6 +17,7 @@ import java.util.List;
import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.branding.BrandingUtil;
import it.niedermann.owncloud.notes.main.MainActivity;
import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.UNCATEGORIZED; import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.UNCATEGORIZED;
@ -75,6 +76,7 @@ public class NavigationAdapter extends RecyclerView.Adapter<NavigationViewHolder
public void setItems(@NonNull List<NavigationItem> items) { public void setItems(@NonNull List<NavigationItem> items) {
for (NavigationItem item : items) { for (NavigationItem item : items) {
if (TextUtils.isEmpty(item.label)) { if (TextUtils.isEmpty(item.label)) {
item.id = MainActivity.ADAPTER_KEY_UNCATEGORIZED;
item.label = context.getString(R.string.action_uncategorized); item.label = context.getString(R.string.action_uncategorized);
item.icon = NavigationAdapter.ICON_NOFOLDER; item.icon = NavigationAdapter.ICON_NOFOLDER;
item.type = UNCATEGORIZED; item.type = UNCATEGORIZED;

View file

@ -468,7 +468,6 @@ public abstract class NotesDatabase extends RoomDatabase {
} }
} }
/** /**
* Modifies the sorting method for one category, the category can be normal category or * Modifies the sorting method for one category, the category can be normal category or
* one of "All notes", "Favorite", and "Uncategorized". * one of "All notes", "Favorite", and "Uncategorized".

View file

@ -28,7 +28,6 @@ public interface NoteDao {
@Query("DELETE FROM NOTE WHERE accountId = :accountId") @Query("DELETE FROM NOTE WHERE accountId = :accountId")
int deleteByAccountId(Long accountId); int deleteByAccountId(Long accountId);
@Query("SELECT NOTE.id, NOTE.accountId, NOTE.title, NOTE.excerpt, NOTE.favorite, NOTE.modified, CATEGORY.title as 'category' FROM NOTE INNER JOIN CATEGORY ON categoryId = CATEGORY.id WHERE NOTE.accountId = :accountId AND status != 'LOCAL_DELETED' AND ( " + @Query("SELECT NOTE.id, NOTE.accountId, NOTE.title, NOTE.excerpt, NOTE.favorite, NOTE.modified, 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 + '/%' " + "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") ") ORDER BY categoryId, favorite DESC, :sortingMethod")
@ -46,15 +45,6 @@ public interface NoteDao {
"NOTE.title LIKE :query OR content LIKE :query OR CATEGORY.title LIKE :query) ORDER BY favorite DESC, categoryId DESC, :sortingMethod") "NOTE.title LIKE :query OR content LIKE :query OR CATEGORY.title LIKE :query) ORDER BY favorite DESC, categoryId DESC, :sortingMethod")
LiveData<List<NoteWithCategory>> searchRecent(long accountId, String query, String sortingMethod); LiveData<List<NoteWithCategory>> searchRecent(long accountId, String query, String sortingMethod);
/**
* Returns a list of all Notes in the Database
*
* @return List&lt;Note&gt;
*/
@Query("SELECT * FROM NOTE WHERE accountId = :accountId AND status != 'LOCAL_DELETED' ORDER BY favorite DESC, modified DESC")
List<Note> getNotes(long accountId);
@Query("DELETE FROM NOTE WHERE id = :id and status = :forceDBStatus") @Query("DELETE FROM NOTE WHERE id = :id and status = :forceDBStatus")
void deleteByCardId(long id, DBStatus forceDBStatus); void deleteByCardId(long id, DBStatus forceDBStatus);
@ -111,9 +101,6 @@ public interface NoteDao {
@Query("UPDATE NOTE SET status = 'LOCAL_EDITED', favorite = ((favorite | 1) - (favorite & 1)) WHERE id = :id") @Query("UPDATE NOTE SET status = 'LOCAL_EDITED', favorite = ((favorite | 1) - (favorite & 1)) WHERE id = :id")
void toggleFavorite(long id); void toggleFavorite(long id);
@Query("SELECT * FROM NOTE WHERE accountId = :accountId AND status != 'LOCAL_DELETED' AND (title LIKE :query OR content LIKE :query OR categoryId LIKE :query) AND (categoryId = :category OR title LIKE :category + '/%') AND favorite = :favorite ORDER BY favorite DESC, :sortingMethod")
List<Note> searchNotes(long accountId, String query, String category, Boolean favorite, CategorySortingMethod sortingMethod);
@Query("UPDATE NOTE SET remoteId = :remoteId WHERE id = :id") @Query("UPDATE NOTE SET remoteId = :remoteId WHERE id = :id")
void updateRemoteId(long id, long remoteId); void updateRemoteId(long id, long remoteId);
@ -125,7 +112,6 @@ public interface NoteDao {
"WHERE id = :id AND content = :content AND favorite = :favorite AND categoryId = :categoryTitle") "WHERE id = :id AND content = :content AND favorite = :favorite AND categoryId = :categoryTitle")
void updateIfModifiedLocallyDuringSync(long id, long modified, String title, Boolean favorite, String categoryTitle, String eTag, String content); void updateIfModifiedLocallyDuringSync(long id, long modified, String title, Boolean favorite, String categoryTitle, String eTag, String content);
/** /**
* 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) * 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)
*/ */

View file

@ -5,6 +5,8 @@ import androidx.room.ForeignKey;
import androidx.room.Index; import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import java.io.Serializable;
import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod; import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
@Entity( @Entity(
@ -23,7 +25,7 @@ import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
@Index(name = "IDX_CATEGORIES_TITLE", value = "title") @Index(name = "IDX_CATEGORIES_TITLE", value = "title")
} }
) )
public class Category { public class Category implements Serializable {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
private long id; private long id;
private long accountId; private long accountId;

View file

@ -3,7 +3,6 @@ package it.niedermann.owncloud.notes.widget.notelist;
import android.app.Activity; import android.app.Activity;
import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetManager;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
@ -30,9 +29,13 @@ import it.niedermann.owncloud.notes.persistence.NotesDatabase;
import it.niedermann.owncloud.notes.persistence.entity.Account; import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData; 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_ALL;
import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData.MODE_DISPLAY_CATEGORY; 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.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; import static it.niedermann.owncloud.notes.shared.util.DisplayUtils.convertToCategoryNavigationItem;
@ -44,8 +47,6 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity {
private Account localAccount = null; private Account localAccount = null;
private NavigationAdapter adapterCategories; private NavigationAdapter adapterCategories;
private NavigationItem itemRecent;
private NavigationItem itemFavorites;
private NotesDatabase db = null; private NotesDatabase db = null;
@Override @Override
@ -77,14 +78,6 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity {
finish(); finish();
} }
itemRecent = new NavigationItem(MainActivity.ADAPTER_KEY_RECENT,
getString(R.string.label_all_notes),
null,
R.drawable.ic_access_time_grey600_24dp);
itemFavorites = new NavigationItem(MainActivity.ADAPTER_KEY_STARRED,
getString(R.string.label_favorites),
null,
R.drawable.ic_star_yellow_24dp);
RecyclerView recyclerView; RecyclerView recyclerView;
RecyclerView.LayoutManager layoutManager; RecyclerView.LayoutManager layoutManager;
@ -94,17 +87,30 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity {
NotesListWidgetData data = new NotesListWidgetData(); NotesListWidgetData data = new NotesListWidgetData();
data.setId(appWidgetId); data.setId(appWidgetId);
if (itemRecent.equals(item)) { if (item.type != null) {
data.setMode(MODE_DISPLAY_ALL); switch (item.type) {
} else if (itemFavorites.equals(item)) { case RECENT: {
data.setMode(MODE_DISPLAY_STARRED); data.setMode(MODE_DISPLAY_ALL);
} else { break;
data.setMode(MODE_DISPLAY_CATEGORY); }
if (item instanceof NavigationItem.CategoryNavigationItem) { case FAVORITES: {
data.setCategoryId(((NavigationItem.CategoryNavigationItem) item).categoryId); data.setMode(MODE_DISPLAY_STARRED);
} else { break;
throw new IllegalStateException("Tried to choose a category, but "); }
case UNCATEGORIZED:
default: {
if (item.getClass() == NavigationItem.CategoryNavigationItem.class) {
data.setMode(MODE_DISPLAY_CATEGORY);
data.setCategoryId(((NavigationItem.CategoryNavigationItem) item).categoryId);
} else {
data.setMode(MODE_DISPLAY_ALL);
Log.e(TAG, "Unknown item navigation type. Fallback to show " + RECENT);
}
}
} }
} else {
data.setMode(MODE_DISPLAY_ALL);
Log.e(TAG, "Unknown item navigation type. Fallback to show " + RECENT);
} }
data.setAccountId(localAccount.getId()); data.setAccountId(localAccount.getId());
@ -130,53 +136,30 @@ public class NoteListWidgetConfigurationActivity extends LockedActivity {
layoutManager = new LinearLayoutManager(this); layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapterCategories); recyclerView.setAdapter(adapterCategories);
} distinctUntilChanged(
map(db.getCategoryDao().getCategoriesLiveData(localAccount.getId()), fromDatabase -> {
List<NavigationItem.CategoryNavigationItem> categories = convertToCategoryNavigationItem(NoteListWidgetConfigurationActivity.this, fromDatabase);
@Override ArrayList<NavigationItem> items = new ArrayList<>(fromDatabase.size() + 3);
protected void onResume() { 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));
super.onResume(); 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));
new LoadCategoryListTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if (categories.size() > 2 && categories.get(2).label.isEmpty()) {
items.add(new NavigationItem(MainActivity.ADAPTER_KEY_UNCATEGORIZED, getString(R.string.action_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));
} }
@Override @Override
public void applyBrand(int mainColor, int textColor) { public void applyBrand(int mainColor, int textColor) {
} }
private class LoadCategoryListTask extends AsyncTask<Void, Void, List<NavigationItem>> {
@Override
protected List<NavigationItem> doInBackground(Void... voids) {
if (localAccount == null) {
return new ArrayList<>();
}
NavigationItem itemUncategorized;
List<NavigationItem.CategoryNavigationItem> categories = convertToCategoryNavigationItem(NoteListWidgetConfigurationActivity.this, db.getCategoryDao().getCategories(localAccount.getId()));
if (!categories.isEmpty() && categories.get(0).label.isEmpty()) {
itemUncategorized = categories.get(0);
itemUncategorized.label = getString(R.string.action_uncategorized);
itemUncategorized.icon = NavigationAdapter.ICON_NOFOLDER;
}
itemFavorites.count = db.getNoteDao().getFavoritesCount(localAccount.getId());
itemRecent.count = db.getNoteDao().count(localAccount.getId());
ArrayList<NavigationItem> items = new ArrayList<>();
items.add(itemRecent);
items.add(itemFavorites);
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;
}
@Override
protected void onPostExecute(List<NavigationItem> items) {
adapterCategories.setItems(items);
}
}
} }

View file

@ -9,6 +9,9 @@ import android.util.Log;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import android.widget.RemoteViewsService; import android.widget.RemoteViewsService;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import java.util.List; import java.util.List;
import it.niedermann.owncloud.notes.NotesApplication; import it.niedermann.owncloud.notes.NotesApplication;
@ -16,8 +19,10 @@ import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.edit.EditNoteActivity; import it.niedermann.owncloud.notes.edit.EditNoteActivity;
import it.niedermann.owncloud.notes.persistence.NotesDatabase; import it.niedermann.owncloud.notes.persistence.NotesDatabase;
import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.persistence.entity.Note;
import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory;
import it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData; import it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData;
import it.niedermann.owncloud.notes.preferences.DarkModeSetting; import it.niedermann.owncloud.notes.preferences.DarkModeSetting;
import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData.MODE_DISPLAY_ALL; 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_CATEGORY;
@ -31,7 +36,9 @@ public class NoteListWidgetFactory implements RemoteViewsService.RemoteViewsFact
private final NotesListWidgetData data; private final NotesListWidgetData data;
private final boolean darkTheme; private final boolean darkTheme;
private NotesDatabase db; private NotesDatabase db;
private List<Note> noteEntities; private LiveData<List<NoteWithCategory>> noteEntitiesLiveData;
private Observer<List<NoteWithCategory>> noteEntitiesObserver = (notes) -> this.noteEntities = notes;
private List<NoteWithCategory> noteEntities;
NoteListWidgetFactory(Context context, Intent intent) { NoteListWidgetFactory(Context context, Intent intent) {
this.context = context; this.context = context;
@ -46,28 +53,35 @@ public class NoteListWidgetFactory implements RemoteViewsService.RemoteViewsFact
@Override @Override
public void onCreate() { public void onCreate() {
} if (noteEntitiesLiveData != null) {
noteEntitiesLiveData.removeObserver(noteEntitiesObserver);
@Override }
public void onDataSetChanged() {
try { try {
Log.v(TAG, "--- data - " + data); Log.v(TAG, "--- data - " + data);
switch (data.getMode()) { switch (data.getMode()) {
case MODE_DISPLAY_ALL: case MODE_DISPLAY_ALL:
noteEntities = db.getNoteDao().getNotes(data.getAccountId()); noteEntitiesLiveData = db.getNoteDao().searchRecent(data.getAccountId(), "%", CategorySortingMethod.SORT_MODIFIED_DESC.getSorder());
break; break;
case MODE_DISPLAY_STARRED: case MODE_DISPLAY_STARRED:
noteEntities = db.getNoteDao().searchNotes(data.getAccountId(), null, null, true, null); noteEntitiesLiveData = db.getNoteDao().searchFavorites(data.getAccountId(), "%", CategorySortingMethod.SORT_MODIFIED_DESC.getSorder());
break; break;
case MODE_DISPLAY_CATEGORY: case MODE_DISPLAY_CATEGORY:
if (data.getCategoryId() != null) { if (data.getCategoryId() != null) {
noteEntities = db.getNoteDao().searchNotes(data.getAccountId(), null, db.getCategoryDao().getCategoryTitleById(data.getCategoryId()), null, null); // FIXME
noteEntitiesLiveData = db.getNoteDao().searchByCategory(data.getAccountId(), "%", db.getCategoryDao().getCategoryTitleById(data.getCategoryId()), CategorySortingMethod.SORT_MODIFIED_DESC.getSorder());
} else {
noteEntitiesLiveData = db.getNoteDao().searchUncategorized(data.getAccountId(), "%", CategorySortingMethod.SORT_MODIFIED_DESC.getSorder());
} }
break; break;
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
e.printStackTrace(); e.printStackTrace();
} }
noteEntitiesLiveData.observeForever(noteEntitiesObserver);
}
@Override
public void onDataSetChanged() {
} }
@Override @Override
@ -98,7 +112,7 @@ public class NoteListWidgetFactory implements RemoteViewsService.RemoteViewsFact
return null; return null;
} }
Note note = noteEntities.get(position); Note note = noteEntities.get(position).getNote();
final Intent fillInIntent = new Intent(); final Intent fillInIntent = new Intent();
final Bundle extras = new Bundle(); final Bundle extras = new Bundle();