diff options
Diffstat (limited to 'app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java')
-rw-r--r-- | app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java | 197 |
1 files changed, 129 insertions, 68 deletions
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 5b0226da..eb608ebe 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 @@ -22,6 +22,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import androidx.preference.PreferenceManager; +import androidx.core.content.ContextCompat; import com.nextcloud.android.sso.AccountImporter; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; @@ -31,7 +32,6 @@ import org.json.JSONArray; import org.json.JSONException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.HashMap; @@ -43,8 +43,6 @@ import java.util.Set; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.activity.EditNoteActivity; -import it.niedermann.owncloud.notes.android.appwidget.NoteListWidget; -import it.niedermann.owncloud.notes.android.appwidget.SingleNoteWidget; import it.niedermann.owncloud.notes.model.ApiVersion; import it.niedermann.owncloud.notes.model.Capabilities; import it.niedermann.owncloud.notes.model.Category; @@ -55,10 +53,15 @@ import it.niedermann.owncloud.notes.model.ISyncCallback; import it.niedermann.owncloud.notes.model.LocalAccount; import it.niedermann.owncloud.notes.model.NavigationAdapter; import it.niedermann.owncloud.notes.util.CategorySortingMethod; +import it.niedermann.owncloud.notes.model.NoteListsWidgetData; import it.niedermann.owncloud.notes.model.SingleNoteWidgetData; +import it.niedermann.owncloud.notes.util.ColorUtil; import it.niedermann.owncloud.notes.util.NoteUtil; import static it.niedermann.owncloud.notes.android.activity.EditNoteActivity.ACTION_SHORTCUT; +import static it.niedermann.owncloud.notes.android.appwidget.NoteListWidget.updateNoteListWidgets; +import static it.niedermann.owncloud.notes.android.appwidget.SingleNoteWidget.updateSingleNoteWidgets; +import static it.niedermann.owncloud.notes.model.NoteListsWidgetData.MODE_DISPLAY_CATEGORY; /** * Helps to add, get, update and delete Notes with the option to trigger a Resync with the Server. @@ -95,9 +98,9 @@ public class NotesDatabase extends AbstractNotesDatabase { * @param note Note */ public long addNoteAndSync(SingleSignOnAccount ssoAccount, long accountId, CloudNote note) { - DBNote dbNote = new DBNote(0, 0, note.getModified(), note.getTitle(), note.getContent(), note.isFavorite(), note.getCategory(), note.getEtag(), DBStatus.LOCAL_EDITED, accountId, NoteUtil.generateNoteExcerpt(note.getContent())); + DBNote dbNote = new DBNote(0, 0, note.getModified(), note.getTitle(), note.getContent(), note.isFavorite(), note.getCategory(), note.getEtag(), DBStatus.LOCAL_EDITED, accountId, NoteUtil.generateNoteExcerpt(note.getContent()), 0); long id = addNote(accountId, dbNote); - notifyNotesChanged(); + notifyWidgets(); getNoteServerSyncHelper().scheduleSync(ssoAccount, true); return id; } @@ -110,7 +113,7 @@ public class NotesDatabase extends AbstractNotesDatabase { */ long addNote(long accountId, CloudNote note) { SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(11); if (note instanceof DBNote) { DBNote dbNote = (DBNote) note; if (dbNote.getId() > 0) { @@ -141,7 +144,7 @@ public class NotesDatabase extends AbstractNotesDatabase { addNoteAndSync(ssoAccount, newAccountId, new CloudNote(0, note.getModified(), note.getTitle(), note.getContent(), note.isFavorite(), note.getCategory(), null)); deleteNoteAndSync(ssoAccount, note.getId()); - notifyNotesChanged(); + notifyWidgets(); getNoteServerSyncHelper().scheduleSync(ssoAccount, true); } @@ -215,8 +218,8 @@ public class NotesDatabase extends AbstractNotesDatabase { if (selectionArgs.length > 2) { Log.v(TAG, selection + " ---- " + selectionArgs[0] + " " + selectionArgs[1] + " " + selectionArgs[2]); } - String cols = String.format("%s, %s, %s, %s, %s, %s, %s, %s, %s", - key_id, key_remote_id, key_status, key_title, key_modified, key_favorite, key_category_title, key_etag, key_excerpt); + String cols = String.format("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s", + key_id, key_remote_id, key_status, key_title, key_modified, key_favorite, key_category_title, key_etag, key_excerpt, key_scroll_y); if (!pruneContent) { cols = String.format("%s, %s", cols, key_content); } @@ -249,13 +252,14 @@ public class NotesDatabase extends AbstractNotesDatabase { cursor.getLong(1), modified, cursor.getString(3), - pruneContent ? "" : cursor.getString(9), + pruneContent ? "" : cursor.getString(10), cursor.getInt(5) > 0, cursor.getString(6), cursor.getString(7), DBStatus.parse(cursor.getString(2)), accountId, - cursor.getString(8) + cursor.getString(8), + cursor.getInt(9) ); } @@ -399,7 +403,7 @@ public class NotesDatabase extends AbstractNotesDatabase { */ @NonNull @WorkerThread - public List<NavigationAdapter.NavigationItem> getCategories(long accountId) { + public List<NavigationAdapter.CategoryNavigationItem> getCategories(long accountId) { return searchCategories(accountId, null); } @@ -415,9 +419,9 @@ public class NotesDatabase extends AbstractNotesDatabase { */ @NonNull @WorkerThread - public List<NavigationAdapter.NavigationItem> searchCategories(long accountId, String search) { + public List<NavigationAdapter.CategoryNavigationItem> searchCategories(long accountId, String search) { validateAccountId(accountId); - String columns = key_category_title + ", COUNT(*)"; + String columns = key_category_id + ", " + key_category_title + ", COUNT(*)"; String selection = key_status + " != ? AND " + key_category_account_id + " = ? AND " + key_category_title + " LIKE ? " + @@ -432,10 +436,11 @@ public class NotesDatabase extends AbstractNotesDatabase { Cursor cursor = getReadableDatabase().rawQuery(rawQuery, new String[]{DBStatus.LOCAL_DELETED.getTitle(), String.valueOf(accountId), search == null ? "%" : "%" + search.trim() + "%"}); - List<NavigationAdapter.NavigationItem> categories = new ArrayList<>(cursor.getCount()); + + List<NavigationAdapter.CategoryNavigationItem> categories = new ArrayList<>(cursor.getCount()); while (cursor.moveToNext()) { Resources res = getContext().getResources(); - String category = cursor.getString(0).toLowerCase(); + String category = cursor.getString(1).toLowerCase(); int icon = NavigationAdapter.ICON_FOLDER; if (category.equals(res.getString(R.string.category_music).toLowerCase())) { icon = R.drawable.ic_library_music_grey600_24dp; @@ -444,18 +449,31 @@ public class NotesDatabase extends AbstractNotesDatabase { } else if (category.equals(res.getString(R.string.category_work).toLowerCase())) { icon = R.drawable.ic_work_grey600_24dp; } - categories.add(new NavigationAdapter.NavigationItem("category:" + cursor.getString(0), cursor.getString(0), cursor.getInt(1), icon)); + categories.add(new NavigationAdapter.CategoryNavigationItem("category:" + cursor.getString(1), cursor.getString(1), cursor.getInt(2), icon, cursor.getLong(0))); } cursor.close(); return categories; } + public String getCategoryTitleById(long accountId, long categoryId) { + validateAccountId(accountId); + final String categoryTitle; + final Cursor cursor = getReadableDatabase().query(table_category, new String[]{key_category_title}, key_category_id + " = ?", new String[]{String.valueOf(categoryId)}, null, null, null); + if (cursor.moveToFirst()) { + categoryTitle = cursor.getString(0); + } else { + categoryTitle = null; + } + cursor.close(); + return categoryTitle; + } + public void toggleFavorite(SingleSignOnAccount ssoAccount, @NonNull DBNote note, @Nullable ISyncCallback callback) { note.setFavorite(!note.isFavorite()); note.setStatus(DBStatus.LOCAL_EDITED); SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(2); values.put(key_status, note.getStatus().getTitle()); values.put(key_favorite, note.isFavorite() ? "1" : "0"); db.update(table_notes, values, key_id + " = ?", new String[]{String.valueOf(note.getId())}); @@ -479,7 +497,7 @@ public class NotesDatabase extends AbstractNotesDatabase { note.setCategory(category); note.setStatus(DBStatus.LOCAL_EDITED); SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(2); values.put(key_status, note.getStatus().getTitle()); int id = getCategoryIdByTitle(note.getAccountId(), note.getCategory()); values.put(key_category, id); @@ -494,7 +512,7 @@ public class NotesDatabase extends AbstractNotesDatabase { private long addCategory(long accountId, @NonNull String title) { validateAccountId(accountId); SQLiteDatabase db = getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(2); values.put(key_category_account_id, accountId); values.put(key_category_title, title); return db.insert(table_category, null, values); @@ -513,23 +531,24 @@ public class NotesDatabase extends AbstractNotesDatabase { //debugPrintFullDB(); DBNote newNote; if (newContent == null) { - newNote = new DBNote(oldNote.getId(), oldNote.getRemoteId(), oldNote.getModified(), oldNote.getTitle(), oldNote.getContent(), oldNote.isFavorite(), oldNote.getCategory(), oldNote.getEtag(), DBStatus.LOCAL_EDITED, accountId, oldNote.getExcerpt()); + newNote = new DBNote(oldNote.getId(), oldNote.getRemoteId(), oldNote.getModified(), oldNote.getTitle(), oldNote.getContent(), oldNote.isFavorite(), oldNote.getCategory(), oldNote.getEtag(), DBStatus.LOCAL_EDITED, accountId, oldNote.getExcerpt(), oldNote.getScrollY()); } else { - newNote = new DBNote(oldNote.getId(), oldNote.getRemoteId(), Calendar.getInstance(), NoteUtil.generateNonEmptyNoteTitle(newContent, getContext()), newContent, oldNote.isFavorite(), oldNote.getCategory(), oldNote.getEtag(), DBStatus.LOCAL_EDITED, accountId, NoteUtil.generateNoteExcerpt(newContent)); + newNote = new DBNote(oldNote.getId(), oldNote.getRemoteId(), Calendar.getInstance(), NoteUtil.generateNonEmptyNoteTitle(newContent, getContext()), newContent, oldNote.isFavorite(), oldNote.getCategory(), oldNote.getEtag(), DBStatus.LOCAL_EDITED, accountId, NoteUtil.generateNoteExcerpt(newContent), oldNote.getScrollY()); } SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(7); values.put(key_status, newNote.getStatus().getTitle()); values.put(key_title, newNote.getTitle()); values.put(key_category, getCategoryIdByTitle(newNote.getAccountId(), newNote.getCategory())); values.put(key_modified, newNote.getModified().getTimeInMillis() / 1000); values.put(key_content, newNote.getContent()); values.put(key_excerpt, newNote.getExcerpt()); + values.put(key_scroll_y, newNote.getScrollY()); int rows = db.update(table_notes, values, key_id + " = ? AND (" + key_content + " != ? OR " + key_category + " != ?)", new String[]{String.valueOf(newNote.getId()), newNote.getContent(), newNote.getCategory()}); removeEmptyCategory(accountId); // if data was changed, set new status and schedule sync (with callback); otherwise invoke callback directly. if (rows > 0) { - notifyNotesChanged(); + notifyWidgets(); if (callback != null) { serverSyncHelper.addCallbackPush(ssoAccount, callback); } @@ -543,6 +562,14 @@ public class NotesDatabase extends AbstractNotesDatabase { } } + public void updateScrollY(long noteId, int scrollY) { + Log.e(TAG, "Updated scrollY: " + scrollY); + SQLiteDatabase db = this.getWritableDatabase(); + ContentValues values = new ContentValues(1); + values.put(key_scroll_y, scrollY); + db.update(table_notes, values, key_id + " = ? ", new String[]{String.valueOf(noteId)}); + } + /** * Updates a single Note with data from the server, (if it was not modified locally). * Thereby, an optimistic concurrency control is realized in order to prevent conflicts arising due to parallel changes from the UI and synchronization. @@ -556,7 +583,7 @@ public class NotesDatabase extends AbstractNotesDatabase { SQLiteDatabase db = this.getWritableDatabase(); // First, update the remote ID, since this field cannot be changed in parallel, but have to be updated always. - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(8); values.put(key_remote_id, remoteNote.getRemoteId()); db.update(table_notes, values, key_id + " = ?", new String[]{String.valueOf(id)}); @@ -599,24 +626,28 @@ public class NotesDatabase extends AbstractNotesDatabase { */ public void deleteNoteAndSync(SingleSignOnAccount ssoAccount, long id) { SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(1); values.put(key_status, DBStatus.LOCAL_DELETED.getTitle()); db.update(table_notes, values, key_id + " = ?", new String[]{String.valueOf(id)}); - notifyNotesChanged(); + notifyWidgets(); getNoteServerSyncHelper().scheduleSync(ssoAccount, true); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ShortcutManager shortcutManager = getContext().getSystemService(ShortcutManager.class); - shortcutManager.getPinnedShortcuts().forEach((shortcut) -> { - String shortcutId = id + ""; - if (shortcut.getId().equals(shortcutId)) { - Log.v(TAG, "Removing shortcut for " + shortcutId); - shortcutManager.disableShortcuts(Collections.singletonList(shortcutId), getContext().getResources().getString(R.string.note_has_been_deleted)); - } - }); + if (shortcutManager != null) { + shortcutManager.getPinnedShortcuts().forEach((shortcut) -> { + String shortcutId = id + ""; + if (shortcut.getId().equals(shortcutId)) { + Log.v(TAG, "Removing shortcut for " + shortcutId); + shortcutManager.disableShortcuts(Collections.singletonList(shortcutId), getContext().getResources().getString(R.string.note_has_been_deleted)); + } + }); + } else { + Log.e(TAG, ShortcutManager.class.getSimpleName() + "is null."); + } } } @@ -638,7 +669,7 @@ public class NotesDatabase extends AbstractNotesDatabase { /** * Notify about changed notes. */ - protected void notifyNotesChanged() { + protected void notifyWidgets() { updateSingleNoteWidgets(getContext()); updateNoteListWidgets(getContext()); } @@ -674,24 +705,6 @@ public class NotesDatabase extends AbstractNotesDatabase { }).start(); } - /** - * Update single note widget, if the note data was changed. - */ - private static void updateSingleNoteWidgets(Context context) { - Intent intent = new Intent(context, SingleNoteWidget.class); - intent.setAction("android.appwidget.action.APPWIDGET_UPDATE"); - context.sendBroadcast(intent); - } - - /** - * Update note list widgets, if the note data was changed. - */ - private static void updateNoteListWidgets(Context context) { - Intent intent = new Intent(context, NoteListWidget.class); - intent.setAction("android.appwidget.action.APPWIDGET_UPDATE"); - context.sendBroadcast(intent); - } - public boolean hasAccounts() { return DatabaseUtils.queryNumEntries(getReadableDatabase(), table_accounts) > 0; } @@ -705,14 +718,13 @@ public class NotesDatabase extends AbstractNotesDatabase { */ public void addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities) throws SQLiteConstraintException { SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(4); values.put(key_url, url); values.put(key_username, username); values.put(key_account_name, accountName); - values.put(key_color, capabilities.getColor().substring(1)); - values.put(key_text_color, capabilities.getTextColor().substring(1)); values.put(key_capabilities_etag, capabilities.getETag()); - db.insertOrThrow(table_accounts, null, values); + long accountId = db.insertOrThrow(table_accounts, null, values); + updateBrand(accountId, capabilities); } /** @@ -800,15 +812,26 @@ public class NotesDatabase extends AbstractNotesDatabase { public void updateBrand(long accountId, @NonNull Capabilities capabilities) throws IllegalArgumentException { validateAccountId(accountId); - // Validate color format - Color.parseColor(capabilities.getColor()); - Color.parseColor(capabilities.getTextColor()); + + String color; + try { + color = ColorUtil.formatColorToParsableHexString(capabilities.getColor()).substring(1); + } catch (Exception e) { + color = String.format("%06X", (0xFFFFFF & ContextCompat.getColor(context, R.color.defaultBrand))); + } + + String textColor; + try { + textColor = ColorUtil.formatColorToParsableHexString(capabilities.getTextColor()).substring(1); + } catch (Exception e) { + textColor = String.format("%06X", (0xFFFFFF & ContextCompat.getColor(context, android.R.color.white))); + } final SQLiteDatabase db = this.getWritableDatabase(); - final ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(2); - values.put(key_color, capabilities.getColor().substring(1)); - values.put(key_text_color, capabilities.getTextColor().substring(1)); + values.put(key_color, color); + values.put(key_text_color, textColor); final int updatedRows = db.update(table_accounts, values, key_id + " = ?", new String[]{String.valueOf(accountId)}); if (updatedRows == 1) { @@ -833,7 +856,7 @@ public class NotesDatabase extends AbstractNotesDatabase { } if (apiVersions.length() > 0) { final SQLiteDatabase db = this.getWritableDatabase(); - final ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(1); values.put(key_api_version, apiVersion); final int updatedRows = db.update(table_accounts, values, key_id + " = ?", new String[]{String.valueOf(accountId)}); if (updatedRows == 1) { @@ -885,7 +908,7 @@ public class NotesDatabase extends AbstractNotesDatabase { void updateETag(long accountId, String etag) { validateAccountId(accountId); SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(1); values.put(key_etag, etag); final int updatedRows = db.update(table_accounts, values, key_id + " = ?", new String[]{String.valueOf(accountId)}); if (updatedRows == 1) { @@ -898,7 +921,7 @@ public class NotesDatabase extends AbstractNotesDatabase { public void updateCapabilitiesETag(long accountId, String capabilitiesETag) { validateAccountId(accountId); SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(1); values.put(key_capabilities_etag, capabilitiesETag); final int updatedRows = db.update(table_accounts, values, key_id + " = ?", new String[]{String.valueOf(accountId)}); if (updatedRows == 1) { @@ -914,7 +937,7 @@ public class NotesDatabase extends AbstractNotesDatabase { throw new IllegalArgumentException("modified must be greater or equal 0"); } SQLiteDatabase db = this.getWritableDatabase(); - ContentValues values = new ContentValues(); + ContentValues values = new ContentValues(1); values.put(key_modified, modified); final int updatedRows = db.update(table_accounts, values, key_id + " = ?", new String[]{String.valueOf(accountId)}); if (updatedRows == 1) { @@ -954,7 +977,7 @@ public class NotesDatabase extends AbstractNotesDatabase { public void createOrUpdateSingleNoteWidgetData(@NonNull SingleNoteWidgetData data) throws SQLException { validateAccountId(data.getAccountId()); final SQLiteDatabase db = getWritableDatabase(); - final ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(4); values.put(key_id, data.getAppWidgetId()); values.put(key_account_id, data.getAccountId()); values.put(key_note_id, data.getNoteId()); @@ -962,6 +985,44 @@ public class NotesDatabase extends AbstractNotesDatabase { db.replaceOrThrow(table_widget_single_notes, null, values); } + @NonNull + public NoteListsWidgetData getNoteListWidgetData(int appWidgetId) throws NoSuchElementException { + NoteListsWidgetData data = new NoteListsWidgetData(); + final SQLiteDatabase db = getReadableDatabase(); + final Cursor cursor = db.query(table_widget_note_list, new String[]{key_account_id, key_category_id, key_theme_mode, key_mode}, key_id + " = ?", new String[]{String.valueOf(appWidgetId)}, null, null, null, null); + if (cursor.moveToNext()) { + data.setAppWidgetId(appWidgetId); + data.setAccountId(cursor.getLong(0)); + data.setCategoryId(cursor.getLong(1)); + data.setThemeMode(cursor.getInt(2)); + data.setMode(cursor.getInt(3)); + } else { + throw new NoSuchElementException(); + } + cursor.close(); + return data; + } + + public void removeNoteListWidget(int appWidgetId) { + final SQLiteDatabase db = getWritableDatabase(); + db.delete(table_widget_note_list, key_id + " = ?", new String[]{String.valueOf(appWidgetId)}); + } + + public void createOrUpdateNoteListWidgetData(@NonNull NoteListsWidgetData data) throws SQLException { + validateAccountId(data.getAccountId()); + final SQLiteDatabase db = getWritableDatabase(); + final ContentValues values = new ContentValues(5); + if (data.getMode() != MODE_DISPLAY_CATEGORY && data.getCategoryId() != null) { + throw new UnsupportedOperationException("Cannot create a widget with a categoryId when mode is not " + MODE_DISPLAY_CATEGORY); + } + values.put(key_id, data.getAppWidgetId()); + values.put(key_account_id, data.getAccountId()); + values.put(key_category_id, data.getCategoryId()); + values.put(key_theme_mode, data.getThemeMode()); + values.put(key_mode, data.getMode()); + db.replaceOrThrow(table_widget_note_list, null, values); + } + private static void validateAccountId(long accountId) { if (accountId < 1) { throw new IllegalArgumentException("accountId must be greater than 0"); |