From 6e5c89c36537714ce898802d1f97a4d4b6f5493b Mon Sep 17 00:00:00 2001 From: M66B Date: Wed, 22 Jan 2020 19:53:17 +0100 Subject: [PATCH] Prevent account/identity invalidation --- .../java/eu/faircode/email/ApplicationEx.java | 54 ++++++++++++++- app/src/main/java/eu/faircode/email/DB.java | 6 ++ .../java/eu/faircode/email/DaoAccount.java | 3 + .../java/eu/faircode/email/DaoIdentity.java | 3 + .../java/eu/faircode/email/DaoMessage.java | 30 ++++----- .../eu/faircode/email/TupleAccountView.java | 66 +++++++++++++++++++ .../eu/faircode/email/TupleIdentityView.java | 59 +++++++++++++++++ 7 files changed, 205 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/eu/faircode/email/TupleAccountView.java create mode 100644 app/src/main/java/eu/faircode/email/TupleIdentityView.java diff --git a/app/src/main/java/eu/faircode/email/ApplicationEx.java b/app/src/main/java/eu/faircode/email/ApplicationEx.java index 46a9114f11..20ff5e3568 100644 --- a/app/src/main/java/eu/faircode/email/ApplicationEx.java +++ b/app/src/main/java/eu/faircode/email/ApplicationEx.java @@ -93,8 +93,60 @@ public class ApplicationEx extends Application { DB db = DB.getInstance(this); + db.account().liveAccountView().observeForever(new Observer>() { + private List last = null; + + @Override + public void onChanged(List accounts) { + if (accounts == null) + accounts = new ArrayList<>(); + + boolean changed = false; + if (last == null || last.size() != accounts.size()) + changed = true; + else + for (int i = 0; i < accounts.size(); i++) + if (!accounts.get(i).equals(last.get(i))) { + changed = true; + last = accounts; + } + + if (changed) { + Log.i("Invalidating account view"); + last = accounts; + db.getInvalidationTracker().notifyObserversByTableNames("account_view"); + } + } + }); + + db.identity().liveIdentityView().observeForever(new Observer>() { + private List last = null; + + @Override + public void onChanged(List identities) { + if (identities == null) + identities = new ArrayList<>(); + + boolean changed = false; + if (last == null || last.size() != identities.size()) + changed = true; + else + for (int i = 0; i < identities.size(); i++) + if (!identities.get(i).equals(last.get(i))) { + changed = true; + last = identities; + } + + if (changed) { + Log.i("Invalidating identity view"); + last = identities; + db.getInvalidationTracker().notifyObserversByTableNames("identity_view"); + } + } + }); + db.folder().liveFolderView().observeForever(new Observer>() { - List last = null; + private List last = null; @Override public void onChanged(List folders) { diff --git a/app/src/main/java/eu/faircode/email/DB.java b/app/src/main/java/eu/faircode/email/DB.java index dbb03033e6..7f0a316347 100644 --- a/app/src/main/java/eu/faircode/email/DB.java +++ b/app/src/main/java/eu/faircode/email/DB.java @@ -75,6 +75,8 @@ import io.requery.android.database.sqlite.SQLiteDatabase; EntityLog.class }, views = { + TupleAccountView.class, + TupleIdentityView.class, TupleFolderView.class } ) @@ -142,6 +144,8 @@ public abstract class DB extends RoomDatabase { Field fmViewTables = InvalidationTracker.class.getDeclaredField("mViewTables"); fmViewTables.setAccessible(true); Map> mViewTables = (Map) fmViewTables.get(sInstance.getInvalidationTracker()); + mViewTables.get("account_view").clear(); + mViewTables.get("identity_view").clear(); mViewTables.get("folder_view").clear(); Log.i("Disabled view invalidation"); } catch (ReflectiveOperationException ex) { @@ -1313,6 +1317,8 @@ public abstract class DB extends RoomDatabase { @Override public void migrate(@NonNull SupportSQLiteDatabase db) { Log.i("DB migration from version " + startVersion + " to " + endVersion); + db.execSQL("CREATE VIEW IF NOT EXISTS `account_view` AS " + TupleAccountView.query); + db.execSQL("CREATE VIEW IF NOT EXISTS `identity_view` AS " + TupleIdentityView.query); db.execSQL("CREATE VIEW IF NOT EXISTS `folder_view` AS " + TupleFolderView.query); } }); diff --git a/app/src/main/java/eu/faircode/email/DaoAccount.java b/app/src/main/java/eu/faircode/email/DaoAccount.java index 1c81c08ca2..f558c96ac4 100644 --- a/app/src/main/java/eu/faircode/email/DaoAccount.java +++ b/app/src/main/java/eu/faircode/email/DaoAccount.java @@ -91,6 +91,9 @@ public interface DaoAccount { @Query("SELECT * FROM account WHERE id = :id") LiveData liveAccount(long id); + @Query(TupleAccountView.query) + LiveData> liveAccountView(); + @Query("SELECT account.id" + ", account.swipe_left, l.type AS left_type, l.name AS left_name" + ", account.swipe_right, r.type AS right_type, r.name AS right_name" + diff --git a/app/src/main/java/eu/faircode/email/DaoIdentity.java b/app/src/main/java/eu/faircode/email/DaoIdentity.java index eb04acebc5..113e9de04e 100644 --- a/app/src/main/java/eu/faircode/email/DaoIdentity.java +++ b/app/src/main/java/eu/faircode/email/DaoIdentity.java @@ -34,6 +34,9 @@ public interface DaoIdentity { " WHERE NOT :synchronize OR account.synchronize") LiveData> liveIdentities(boolean synchronize); + @Query(TupleIdentityView.query) + LiveData> liveIdentityView(); + @Query("SELECT identity.*, account.name AS accountName FROM identity" + " JOIN account ON account.id = identity.account" + " JOIN folder ON folder.account = identity.account AND folder.type = '" + EntityFolder.DRAFTS + "'" + diff --git a/app/src/main/java/eu/faircode/email/DaoMessage.java b/app/src/main/java/eu/faircode/email/DaoMessage.java index 791ec89f67..f6759fcf1e 100644 --- a/app/src/main/java/eu/faircode/email/DaoMessage.java +++ b/app/src/main/java/eu/faircode/email/DaoMessage.java @@ -62,8 +62,8 @@ public interface DaoMessage { " OR (NOT :found AND folder.type = :type))" + " THEN message.received ELSE 0 END) AS dummy" + " FROM (SELECT * FROM message ORDER BY received DESC) AS message" + - " JOIN account ON account.id = message.account" + - " LEFT JOIN identity ON identity.id = message.identity" + + " JOIN account_view AS account ON account.id = message.account" + + " LEFT JOIN identity_view AS identity ON identity.id = message.identity" + " JOIN folder_view AS folder ON folder.id = message.folder" + " WHERE account.`synchronize`" + " AND (:threading OR (:type IS NULL AND (folder.unified OR :found)) OR (:type IS NOT NULL AND folder.type = :type))" + @@ -110,8 +110,8 @@ public interface DaoMessage { ", SUM(message.total) AS totalSize" + ", MAX(CASE WHEN folder.id = :folder THEN message.received ELSE 0 END) AS dummy" + " FROM (SELECT * FROM message ORDER BY received DESC) AS message" + - " JOIN account ON account.id = message.account" + - " LEFT JOIN identity ON identity.id = message.identity" + + " JOIN account_view AS account ON account.id = message.account" + + " LEFT JOIN identity_view AS identity ON identity.id = message.identity" + " JOIN folder_view AS folder ON folder.id = message.folder" + " JOIN folder_view AS f ON f.id = :folder" + " WHERE (message.account = f.account OR " + is_outbox + ")" + @@ -155,8 +155,8 @@ public interface DaoMessage { ", 1 AS visible" + ", message.total AS totalSize" + " FROM message" + - " JOIN account ON account.id = message.account" + - " LEFT JOIN identity ON identity.id = message.identity" + + " JOIN account_view AS account ON account.id = message.account" + + " LEFT JOIN identity_view AS identity ON identity.id = message.identity" + " JOIN folder_view AS folder ON folder.id = message.folder" + " WHERE message.account = :account" + " AND message.thread = :thread" + @@ -181,7 +181,7 @@ public interface DaoMessage { ", COUNT(message.id) AS count" + ", SUM(message.ui_seen) AS seen" + " FROM message" + - " JOIN account ON account.id = message.account" + + " JOIN account_view AS account ON account.id = message.account" + " WHERE message.account = :account" + " AND message.thread = :thread" + " AND (:id IS NULL OR message.id = :id)" + @@ -270,7 +270,7 @@ public interface DaoMessage { @Query("SELECT message.*" + " FROM message" + - " LEFT JOIN account ON account.id = message.account" + + " LEFT JOIN account_view AS account ON account.id = message.account" + " WHERE account = :account" + " AND thread = :thread" + " AND (:id IS NULL OR message.id = :id)" + @@ -309,8 +309,8 @@ public interface DaoMessage { ", 1 AS visible" + ", message.total AS totalSize" + " FROM message" + - " JOIN account ON account.id = message.account" + - " LEFT JOIN identity ON identity.id = message.identity" + + " JOIN account_view AS account ON account.id = message.account" + + " LEFT JOIN identity_view AS identity ON identity.id = message.identity" + " JOIN folder_view AS folder ON folder.id = message.folder" + " WHERE message.id = :id") LiveData liveMessage(long id); @@ -318,7 +318,7 @@ public interface DaoMessage { @Transaction @Query("SELECT account.id AS account, COUNT(message.id) AS unseen, SUM(ABS(notifying)) AS notifying" + " FROM message" + - " JOIN account ON account.id = message.account" + + " JOIN account_view AS account ON account.id = message.account" + " JOIN folder_view AS folder ON folder.id = message.folder" + " WHERE (:account IS NULL OR account.id = :account)" + " AND account.`synchronize`" + @@ -330,7 +330,7 @@ public interface DaoMessage { @Query("SELECT :account AS account, COUNT(message.id) AS unseen, SUM(ABS(notifying)) AS notifying" + " FROM message" + - " JOIN account ON account.id = message.account" + + " JOIN account_view AS account ON account.id = message.account" + " JOIN folder_view AS folder ON folder.id = message.folder" + " WHERE (:account IS NULL OR account.id = :account)" + " AND account.`synchronize`" + @@ -354,8 +354,8 @@ public interface DaoMessage { ", 1 AS visible" + ", message.total AS totalSize" + " FROM message" + - " JOIN account ON account.id = message.account" + - " LEFT JOIN identity ON identity.id = message.identity" + + " JOIN account_view AS account ON account.id = message.account" + + " LEFT JOIN identity_view AS identity ON identity.id = message.identity" + " JOIN folder_view AS folder ON folder.id = message.folder" + " WHERE account.`synchronize`" + " AND folder.notify" + @@ -381,7 +381,7 @@ public interface DaoMessage { ", COUNT(message.id) - SUM(message.ui_flagged) AS unflagged" + ", MAX(message.received) AS dummy" + " FROM message" + - " JOIN account ON account.id = message.account" + + " JOIN account_view AS account ON account.id = message.account" + " JOIN folder_view AS folder ON folder.id = message.folder" + " WHERE account.`synchronize`" + " AND ((:folder IS NULL AND folder.unified) OR folder.id = :folder)" + diff --git a/app/src/main/java/eu/faircode/email/TupleAccountView.java b/app/src/main/java/eu/faircode/email/TupleAccountView.java new file mode 100644 index 0000000000..5b9d04a8d6 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/TupleAccountView.java @@ -0,0 +1,66 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail 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. + + FairEmail 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 FairEmail. If not, see . + + Copyright 2018-2020 by Marcel Bokhorst (M66B) +*/ + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.room.ColumnInfo; +import androidx.room.DatabaseView; + +import java.util.Objects; + +@DatabaseView( + viewName = "account_view", + value = TupleAccountView.query +) +public class TupleAccountView { + static final String query = "SELECT id, pop, name, color, synchronize, notify, auto_seen, created FROM account"; + + @NonNull + public Long id; + @NonNull + @ColumnInfo(name = "pop") + public Integer protocol; + public String name; + public Integer color; + @NonNull + public Boolean synchronize; + @NonNull + public Boolean notify = false; + @NonNull + public Boolean auto_seen = true; + public Long created; + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof TupleAccountView) { + TupleAccountView other = (TupleAccountView) obj; + return (this.id.equals(other.id) && + this.protocol.equals(other.protocol) && + Objects.equals(this.name, other.name) && + Objects.equals(this.color, other.color) && + this.synchronize.equals(other.synchronize) && + this.notify.equals(other.notify) && + this.auto_seen.equals(other.auto_seen) && + Objects.equals(this.created, other.created)); + } else + return false; + } +} diff --git a/app/src/main/java/eu/faircode/email/TupleIdentityView.java b/app/src/main/java/eu/faircode/email/TupleIdentityView.java new file mode 100644 index 0000000000..92085fcfb2 --- /dev/null +++ b/app/src/main/java/eu/faircode/email/TupleIdentityView.java @@ -0,0 +1,59 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail 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. + + FairEmail 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 FairEmail. If not, see . + + Copyright 2018-2020 by Marcel Bokhorst (M66B) +*/ + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.room.DatabaseView; + +import java.util.Objects; + +@DatabaseView( + viewName = "identity_view", + value = TupleIdentityView.query +) +public class TupleIdentityView { + static final String query = "SELECT id, name, email, display, color, synchronize FROM identity"; + + @NonNull + public Long id; + @NonNull + public String name; + @NonNull + public String email; + public String display; + public Integer color; + @NonNull + public Boolean synchronize; + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof TupleIdentityView) { + TupleIdentityView other = (TupleIdentityView) obj; + return (this.id.equals(other.id) && + this.name.equals(other.name) && + this.email.equals(other.email) && + Objects.equals(this.display, other.display) && + Objects.equals(this.color, other.color) && + this.synchronize.equals(other.synchronize)); + } else + return false; + } +}