Prevent folder invalidation

pull/172/head
M66B 5 years ago
parent ddd041aa40
commit 4624ae2166

File diff suppressed because it is too large Load Diff

@ -29,9 +29,12 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.webkit.CookieManager;
import androidx.lifecycle.Observer;
import androidx.preference.PreferenceManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -87,6 +90,34 @@ public class ApplicationEx extends Application {
WorkerWatchdog.init(this);
WorkerCleanup.queue(this);
DB db = DB.getInstance(this);
db.folder().liveFolderView().observeForever(new Observer<List<TupleFolderView>>() {
List<TupleFolderView> last = null;
@Override
public void onChanged(List<TupleFolderView> folders) {
if (folders == null)
folders = new ArrayList<>();
boolean changed = false;
if (last == null || last.size() != folders.size())
changed = true;
else
for (int i = 0; i < folders.size(); i++)
if (!folders.get(i).equals(last.get(i))) {
changed = true;
last = folders;
}
if (changed) {
Log.i("Invalidating folder view");
last = folders;
db.getInvalidationTracker().notifyObserversByTableNames("folder_view");
}
}
});
}
@Override

@ -24,9 +24,11 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@ -58,7 +60,7 @@ import io.requery.android.database.sqlite.SQLiteDatabase;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 134,
version = 135,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -73,6 +75,7 @@ import io.requery.android.database.sqlite.SQLiteDatabase;
EntityLog.class
},
views = {
TupleFolderView.class
}
)
@ -107,6 +110,9 @@ public abstract class DB extends RoomDatabase {
private static final String DB_NAME = "fairemail";
private static final int DB_CHECKPOINT = 100;
static final String[] DB_TABLES = new String[]{
"identity", "account", "folder", "message", "attachment", "operation", "contact", "certificate", "answer", "rule", "log"};
@Override
public void init(@NonNull DatabaseConfiguration configuration) {
// https://www.sqlite.org/pragma.html#pragma_wal_autocheckpoint
@ -131,20 +137,21 @@ public abstract class DB extends RoomDatabase {
sInstance = migrate(acontext, getBuilder(acontext)).build();
sInstance.getInvalidationTracker().addObserver(new InvalidationTracker.Observer(
EntityAccount.TABLE_NAME,
EntityIdentity.TABLE_NAME,
EntityFolder.TABLE_NAME,
EntityMessage.TABLE_NAME,
EntityAttachment.TABLE_NAME,
EntityOperation.TABLE_NAME,
EntityContact.TABLE_NAME,
EntityAnswer.TABLE_NAME,
EntityRule.TABLE_NAME,
EntityLog.TABLE_NAME) {
try {
Log.i("Disabling view invalidation");
Field fmViewTables = InvalidationTracker.class.getDeclaredField("mViewTables");
fmViewTables.setAccessible(true);
Map<String, Set<String>> mViewTables = (Map) fmViewTables.get(sInstance.getInvalidationTracker());
mViewTables.get("folder_view").clear();
Log.i("Disabled view invalidation");
} catch (ReflectiveOperationException ex) {
Log.w(ex);
}
sInstance.getInvalidationTracker().addObserver(new InvalidationTracker.Observer(DB.DB_TABLES) {
@Override
public void onInvalidated(@NonNull Set<String> tables) {
Log.d("ROOM invalidated=" + TextUtils.join(",", tables));
Log.i("ROOM invalidated=" + TextUtils.join(",", tables));
}
});
}
@ -1297,10 +1304,17 @@ public abstract class DB extends RoomDatabase {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("DROP TRIGGER attachment_insert");
db.execSQL("DROP TRIGGER attachment_delete");
db.execSQL("DROP TRIGGER IF EXISTS `attachment_insert`");
db.execSQL("DROP TRIGGER IF EXISTS `attachment_delete`");
createTriggers(db);
}
})
.addMigrations(new Migration(134, 135) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("CREATE VIEW IF NOT EXISTS `folder_view` AS " + TupleFolderView.query);
}
});
}

@ -146,6 +146,9 @@ public interface DaoFolder {
" GROUP BY folder.id")
LiveData<TupleFolderEx> liveFolderEx(long id);
@Query(TupleFolderView.query)
LiveData<List<TupleFolderView>> liveFolderView();
@Query("SELECT * FROM folder ORDER BY account, name COLLATE NOCASE")
List<EntityFolder> getFolders();

@ -64,7 +64,7 @@ public interface DaoMessage {
" 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 folder ON folder.id = message.folder" +
" 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))" +
" AND (NOT message.ui_hide OR :debug)" +
@ -112,8 +112,8 @@ public interface DaoMessage {
" 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 folder ON folder.id = message.folder" +
" JOIN folder AS f ON f.id = :folder" +
" 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 + ")" +
" AND (:threading OR folder.id = :folder)" +
" AND (NOT message.ui_hide OR :debug)" +
@ -157,7 +157,7 @@ public interface DaoMessage {
" FROM message" +
" JOIN account ON account.id = message.account" +
" LEFT JOIN identity ON identity.id = message.identity" +
" JOIN folder ON folder.id = message.folder" +
" JOIN folder_view AS folder ON folder.id = message.folder" +
" WHERE message.account = :account" +
" AND message.thread = :thread" +
" AND (:id IS NULL OR message.id = :id)" +
@ -203,7 +203,7 @@ public interface DaoMessage {
LiveData<List<Long>> liveHiddenThread(long account, String thread);
@Query("SELECT SUM(fts) AS fts, COUNT(*) AS total FROM message" +
" JOIN folder ON folder.id = message.folder" +
" JOIN folder_view AS folder ON folder.id = message.folder" +
" WHERE content" +
" AND folder.type <> '" + EntityFolder.OUTBOX + "'")
LiveData<TupleFtsStats> liveFts();
@ -234,7 +234,7 @@ public interface DaoMessage {
List<Long> getMessageIdsByFolder(Long folder);
@Query("SELECT message.id FROM message" +
" JOIN folder ON folder.id = message.folder" +
" JOIN folder_view AS folder ON folder.id = message.folder" +
" WHERE content" +
" AND NOT fts" +
" AND folder.type <> '" + EntityFolder.OUTBOX + "'" +
@ -311,7 +311,7 @@ public interface DaoMessage {
" FROM message" +
" JOIN account ON account.id = message.account" +
" LEFT JOIN identity ON identity.id = message.identity" +
" JOIN folder ON folder.id = message.folder" +
" JOIN folder_view AS folder ON folder.id = message.folder" +
" WHERE message.id = :id")
LiveData<TupleMessageEx> liveMessage(long id);
@ -319,7 +319,7 @@ public interface DaoMessage {
@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 folder ON folder.id = message.folder" +
" JOIN folder_view AS folder ON folder.id = message.folder" +
" WHERE (:account IS NULL OR account.id = :account)" +
" AND account.`synchronize`" +
" AND folder.notify" +
@ -331,7 +331,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 folder ON folder.id = message.folder" +
" JOIN folder_view AS folder ON folder.id = message.folder" +
" WHERE (:account IS NULL OR account.id = :account)" +
" AND account.`synchronize`" +
" AND folder.notify" +
@ -356,7 +356,7 @@ public interface DaoMessage {
" FROM message" +
" JOIN account ON account.id = message.account" +
" LEFT JOIN identity ON identity.id = message.identity" +
" JOIN folder ON folder.id = message.folder" +
" JOIN folder_view AS folder ON folder.id = message.folder" +
" WHERE account.`synchronize`" +
" AND folder.notify" +
" AND (account.created IS NULL OR message.received > account.created)" +
@ -382,7 +382,7 @@ public interface DaoMessage {
", MAX(message.received) AS dummy" +
" FROM message" +
" JOIN account ON account.id = message.account" +
" JOIN folder ON folder.id = message.folder" +
" JOIN folder_view AS folder ON folder.id = message.folder" +
" WHERE account.`synchronize`" +
" AND ((:folder IS NULL AND folder.unified) OR folder.id = :folder)" +
" AND NOT message.ui_hide" +

@ -0,0 +1,67 @@
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 <http://www.gnu.org/licenses/>.
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 = "folder_view",
value = TupleFolderView.query
)
public class TupleFolderView {
static final String query = "SELECT id, account, name, type, display, color, unified, notify, read_only FROM folder";
@NonNull
public Long id;
public Long account;
@NonNull
public String name;
@NonNull
public String type;
public String display;
public Integer color;
@NonNull
public Boolean unified = false;
@NonNull
public Boolean notify = false;
@NonNull
public Boolean read_only = false;
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof TupleFolderView) {
TupleFolderView other = (TupleFolderView) obj;
return (this.id.equals(other.id) &&
Objects.equals(this.account, other.account) &&
this.name.equals(other.name) &&
this.type.equals(other.type) &&
Objects.equals(this.display, other.display) &&
Objects.equals(this.color, other.color) &&
this.unified == other.unified &&
this.notify == other.notify &&
this.read_only == other.read_only);
} else
return false;
}
}
Loading…
Cancel
Save