Loading app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java +2 −0 Original line number Diff line number Diff line Loading @@ -175,6 +175,8 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A executor.submit(() -> { try { final var account = mainViewModel.getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()).name); mainViewModel.migrateOldNotes(account); runOnUiThread(() -> mainViewModel.postCurrentAccount(account)); } catch (NextcloudFilesAppAccountNotFoundException e) { // Verbose log output for https://github.com/stefan-niedermann/nextcloud-notes/issues/1256 Loading app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java +5 −0 Original line number Diff line number Diff line Loading @@ -663,4 +663,9 @@ public class MainViewModel extends AndroidViewModel { final var lower = input.toLowerCase(Locale.ROOT); return lower.contains("failed to connect") && lower.contains("network is unreachable"); } public void migrateOldNotes(@NonNull Account account) { repo.migrateOldNotesIfPossible(account.getId()); } } No newline at end of file app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java +19 −4 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.persistence; import android.content.Context; import trikita.log.Log; import androidx.annotation.NonNull; import androidx.room.Database; Loading @@ -10,6 +9,8 @@ import androidx.room.RoomDatabase; import androidx.room.TypeConverters; import androidx.sqlite.db.SupportSQLiteDatabase; import java.util.List; import it.niedermann.owncloud.notes.persistence.dao.AccountDao; import it.niedermann.owncloud.notes.persistence.dao.CategoryOptionsDao; import it.niedermann.owncloud.notes.persistence.dao.NoteDao; Loading @@ -35,6 +36,7 @@ import it.niedermann.owncloud.notes.persistence.migration.Migration_20_21; import it.niedermann.owncloud.notes.persistence.migration.Migration_21_22; import it.niedermann.owncloud.notes.persistence.migration.Migration_22_23; import it.niedermann.owncloud.notes.persistence.migration.Migration_9_10; import trikita.log.Log; @Database( entities = { Loading Loading @@ -105,4 +107,17 @@ public abstract class NotesDatabase extends RoomDatabase { public abstract WidgetSingleNoteDao getWidgetSingleNoteDao(); public abstract WidgetNotesListDao getWidgetNotesListDao(); /** * retrieve notes from old `NOTES` table for migration. * If we try to follow the *ROOM way* (entity, dao) for `NOTES` table, the existing data will be removed. * So we have to retrieve old entries via raw sql commands. * * <a href="https://gitlab.e.foundation/e/os/backlog/-/issues/1147">Check for more details</a> * @return list of old notes entries */ @NonNull public List<OldNote> getNotesFromOldTable() { return OldNoteRetriever.getNotesFromOldTable(this); } } app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesRepository.java +67 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.pm.ShortcutManager; import android.graphics.drawable.Icon; import android.net.ConnectivityManager; import android.text.TextUtils; import trikita.log.Log; import androidx.annotation.AnyThread; Loading Loading @@ -79,6 +80,8 @@ import retrofit2.Call; @SuppressWarnings("UnusedReturnValue") public class NotesRepository { private static final String PREF_KEY_MIGRATION_DONE = "old_note_migration_done"; private static final String TAG = NotesRepository.class.getSimpleName(); private static NotesRepository instance; Loading Loading @@ -959,4 +962,68 @@ public class NotesRepository { public void updateDisplayName(long id, @Nullable String displayName) { db.getAccountDao().updateDisplayName(id, displayName); } /** * migrate old notes to latest note table. * if the migration is already done, skip. * link the provided account to the migrated notes, as old notes don't have account entry to them. * * * <a href="https://gitlab.e.foundation/e/os/backlog/-/issues/1147">Check for more details</a>. * @param accountId which will be used to link migrated notes to account */ @AnyThread public void migrateOldNotesIfPossible(long accountId) { if (isMigrationDone()) { return; } migrateOldNotes(accountId); } private void migrateOldNotes(long accountId) { try { executor.submit(() -> { List<OldNote> oldNotes = db.getNotesFromOldTable(); if (oldNotes.isEmpty()) { updatePrefOnMigrationDone(); return; } for (OldNote oldNote : oldNotes) { if (oldNote == null || isNoteAlreadyExistsInNewTable(oldNote)) { continue; } addNewNote(accountId, oldNote); } updatePrefOnMigrationDone(); }); } catch (Exception e) { Log.e(TAG, "An exception has been caught", e); } } private boolean isNoteAlreadyExistsInNewTable(@NonNull OldNote oldNote) { return db.getNoteDao().countByTitleAndContent(oldNote.getTitle(), oldNote.getContent()) > 0; } private void addNewNote(long accountId, @NonNull OldNote oldNote) { Note newNote = new Note(oldNote.getRemoteId(), oldNote.getModified(), oldNote.getTitle(), oldNote.getContent(), oldNote.getCategory(), oldNote.getFavorite(), oldNote.getETag()); newNote.setStatus(oldNote.getStatus()); newNote.setAccountId(accountId); db.getNoteDao().addNote(newNote); } private boolean isMigrationDone() { final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); return sharedPreferences.getBoolean(PREF_KEY_MIGRATION_DONE, false); } private void updatePrefOnMigrationDone() { final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); sharedPreferences.edit() .putBoolean(PREF_KEY_MIGRATION_DONE, true) .apply(); } } app/src/main/java/it/niedermann/owncloud/notes/persistence/OldNote.java 0 → 100644 +149 −0 Original line number Diff line number Diff line /* * Copyright MURENA SAS 2023 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package it.niedermann.owncloud.notes.persistence; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.io.Serializable; import java.util.Calendar; import it.niedermann.owncloud.notes.shared.model.DBStatus; /** * Entity representation for `NOTES` table. * This is added to migrate old `NOTES` items to new `Note` table. * * <a href="https://gitlab.e.foundation/e/os/backlog/-/issues/1147">Check for more details</a> */ public class OldNote implements Serializable { private long id; private long remoteId; @NonNull private DBStatus status; @NonNull private String title; @Nullable private Calendar modified; @NonNull private String content; private boolean favorite = false; @NonNull private String category = ""; @Nullable private String eTag; public OldNote(long id, long remoteId, @Nullable Calendar modified, @NonNull String title, @NonNull String content, boolean favorite, @NonNull String category, @Nullable String eTag, @NonNull DBStatus status) { this.id = id; this.remoteId = remoteId; this.status = status; this.title = title; this.modified = modified; this.content = content; this.favorite = favorite; this.category = category; this.eTag = eTag; } public long getId() { return id; } public void setId(long id) { this.id = id; } public long getRemoteId() { return remoteId; } public void setRemoteId(long remoteId) { this.remoteId = remoteId; } @NonNull public DBStatus getStatus() { return status; } public void setStatus(@NonNull DBStatus status) { this.status = status; } @NonNull public String getTitle() { return title; } public void setTitle(@NonNull String title) { this.title = title; } @Nullable public Calendar getModified() { return modified; } public void setModified(@Nullable Calendar modified) { this.modified = modified; } @NonNull public String getContent() { return content; } public void setContent(@NonNull String content) { this.content = content; } public boolean getFavorite() { return favorite; } public void setFavorite(boolean favorite) { this.favorite = favorite; } @NonNull public String getCategory() { return category; } public void setCategory(@NonNull String category) { this.category = category; } @Nullable public String getETag() { return eTag; } public void setETag(@Nullable String eTag) { this.eTag = eTag; } } Loading
app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java +2 −0 Original line number Diff line number Diff line Loading @@ -175,6 +175,8 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A executor.submit(() -> { try { final var account = mainViewModel.getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()).name); mainViewModel.migrateOldNotes(account); runOnUiThread(() -> mainViewModel.postCurrentAccount(account)); } catch (NextcloudFilesAppAccountNotFoundException e) { // Verbose log output for https://github.com/stefan-niedermann/nextcloud-notes/issues/1256 Loading
app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java +5 −0 Original line number Diff line number Diff line Loading @@ -663,4 +663,9 @@ public class MainViewModel extends AndroidViewModel { final var lower = input.toLowerCase(Locale.ROOT); return lower.contains("failed to connect") && lower.contains("network is unreachable"); } public void migrateOldNotes(@NonNull Account account) { repo.migrateOldNotesIfPossible(account.getId()); } } No newline at end of file
app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java +19 −4 Original line number Diff line number Diff line package it.niedermann.owncloud.notes.persistence; import android.content.Context; import trikita.log.Log; import androidx.annotation.NonNull; import androidx.room.Database; Loading @@ -10,6 +9,8 @@ import androidx.room.RoomDatabase; import androidx.room.TypeConverters; import androidx.sqlite.db.SupportSQLiteDatabase; import java.util.List; import it.niedermann.owncloud.notes.persistence.dao.AccountDao; import it.niedermann.owncloud.notes.persistence.dao.CategoryOptionsDao; import it.niedermann.owncloud.notes.persistence.dao.NoteDao; Loading @@ -35,6 +36,7 @@ import it.niedermann.owncloud.notes.persistence.migration.Migration_20_21; import it.niedermann.owncloud.notes.persistence.migration.Migration_21_22; import it.niedermann.owncloud.notes.persistence.migration.Migration_22_23; import it.niedermann.owncloud.notes.persistence.migration.Migration_9_10; import trikita.log.Log; @Database( entities = { Loading Loading @@ -105,4 +107,17 @@ public abstract class NotesDatabase extends RoomDatabase { public abstract WidgetSingleNoteDao getWidgetSingleNoteDao(); public abstract WidgetNotesListDao getWidgetNotesListDao(); /** * retrieve notes from old `NOTES` table for migration. * If we try to follow the *ROOM way* (entity, dao) for `NOTES` table, the existing data will be removed. * So we have to retrieve old entries via raw sql commands. * * <a href="https://gitlab.e.foundation/e/os/backlog/-/issues/1147">Check for more details</a> * @return list of old notes entries */ @NonNull public List<OldNote> getNotesFromOldTable() { return OldNoteRetriever.getNotesFromOldTable(this); } }
app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesRepository.java +67 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.pm.ShortcutManager; import android.graphics.drawable.Icon; import android.net.ConnectivityManager; import android.text.TextUtils; import trikita.log.Log; import androidx.annotation.AnyThread; Loading Loading @@ -79,6 +80,8 @@ import retrofit2.Call; @SuppressWarnings("UnusedReturnValue") public class NotesRepository { private static final String PREF_KEY_MIGRATION_DONE = "old_note_migration_done"; private static final String TAG = NotesRepository.class.getSimpleName(); private static NotesRepository instance; Loading Loading @@ -959,4 +962,68 @@ public class NotesRepository { public void updateDisplayName(long id, @Nullable String displayName) { db.getAccountDao().updateDisplayName(id, displayName); } /** * migrate old notes to latest note table. * if the migration is already done, skip. * link the provided account to the migrated notes, as old notes don't have account entry to them. * * * <a href="https://gitlab.e.foundation/e/os/backlog/-/issues/1147">Check for more details</a>. * @param accountId which will be used to link migrated notes to account */ @AnyThread public void migrateOldNotesIfPossible(long accountId) { if (isMigrationDone()) { return; } migrateOldNotes(accountId); } private void migrateOldNotes(long accountId) { try { executor.submit(() -> { List<OldNote> oldNotes = db.getNotesFromOldTable(); if (oldNotes.isEmpty()) { updatePrefOnMigrationDone(); return; } for (OldNote oldNote : oldNotes) { if (oldNote == null || isNoteAlreadyExistsInNewTable(oldNote)) { continue; } addNewNote(accountId, oldNote); } updatePrefOnMigrationDone(); }); } catch (Exception e) { Log.e(TAG, "An exception has been caught", e); } } private boolean isNoteAlreadyExistsInNewTable(@NonNull OldNote oldNote) { return db.getNoteDao().countByTitleAndContent(oldNote.getTitle(), oldNote.getContent()) > 0; } private void addNewNote(long accountId, @NonNull OldNote oldNote) { Note newNote = new Note(oldNote.getRemoteId(), oldNote.getModified(), oldNote.getTitle(), oldNote.getContent(), oldNote.getCategory(), oldNote.getFavorite(), oldNote.getETag()); newNote.setStatus(oldNote.getStatus()); newNote.setAccountId(accountId); db.getNoteDao().addNote(newNote); } private boolean isMigrationDone() { final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); return sharedPreferences.getBoolean(PREF_KEY_MIGRATION_DONE, false); } private void updatePrefOnMigrationDone() { final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); sharedPreferences.edit() .putBoolean(PREF_KEY_MIGRATION_DONE, true) .apply(); } }
app/src/main/java/it/niedermann/owncloud/notes/persistence/OldNote.java 0 → 100644 +149 −0 Original line number Diff line number Diff line /* * Copyright MURENA SAS 2023 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package it.niedermann.owncloud.notes.persistence; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.io.Serializable; import java.util.Calendar; import it.niedermann.owncloud.notes.shared.model.DBStatus; /** * Entity representation for `NOTES` table. * This is added to migrate old `NOTES` items to new `Note` table. * * <a href="https://gitlab.e.foundation/e/os/backlog/-/issues/1147">Check for more details</a> */ public class OldNote implements Serializable { private long id; private long remoteId; @NonNull private DBStatus status; @NonNull private String title; @Nullable private Calendar modified; @NonNull private String content; private boolean favorite = false; @NonNull private String category = ""; @Nullable private String eTag; public OldNote(long id, long remoteId, @Nullable Calendar modified, @NonNull String title, @NonNull String content, boolean favorite, @NonNull String category, @Nullable String eTag, @NonNull DBStatus status) { this.id = id; this.remoteId = remoteId; this.status = status; this.title = title; this.modified = modified; this.content = content; this.favorite = favorite; this.category = category; this.eTag = eTag; } public long getId() { return id; } public void setId(long id) { this.id = id; } public long getRemoteId() { return remoteId; } public void setRemoteId(long remoteId) { this.remoteId = remoteId; } @NonNull public DBStatus getStatus() { return status; } public void setStatus(@NonNull DBStatus status) { this.status = status; } @NonNull public String getTitle() { return title; } public void setTitle(@NonNull String title) { this.title = title; } @Nullable public Calendar getModified() { return modified; } public void setModified(@Nullable Calendar modified) { this.modified = modified; } @NonNull public String getContent() { return content; } public void setContent(@NonNull String content) { this.content = content; } public boolean getFavorite() { return favorite; } public void setFavorite(boolean favorite) { this.favorite = favorite; } @NonNull public String getCategory() { return category; } public void setCategory(@NonNull String category) { this.category = category; } @Nullable public String getETag() { return eTag; } public void setETag(@Nullable String eTag) { this.eTag = eTag; } }