Browse Source

Search improvements

main
M66B 6 years ago
parent
commit
24991d621b
8 changed files with 1081 additions and 51 deletions
  1. +1016
    -0
      app/schemas/eu.faircode.email.DB/22.json
  2. +2
    -1
      app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java
  3. +11
    -1
      app/src/main/java/eu/faircode/email/DB.java
  4. +13
    -10
      app/src/main/java/eu/faircode/email/DaoMessage.java
  5. +2
    -2
      app/src/main/java/eu/faircode/email/EntityMessage.java
  6. +26
    -13
      app/src/main/java/eu/faircode/email/FragmentMessages.java
  7. +3
    -3
      app/src/main/java/eu/faircode/email/ServiceSynchronize.java
  8. +8
    -21
      app/src/main/java/eu/faircode/email/ViewModelBrowse.java

+ 1016
- 0
app/schemas/eu.faircode.email.DB/22.json
File diff suppressed because it is too large
View File


+ 2
- 1
app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java View File

@ -35,7 +35,8 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
private ViewModelBrowse model; private ViewModelBrowse model;
private Handler handler; private Handler handler;
private IBoundaryCallbackMessages intf; private IBoundaryCallbackMessages intf;
private ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory);
private static ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory);
interface IBoundaryCallbackMessages { interface IBoundaryCallbackMessages {
void onLoading(); void onLoading();


+ 11
- 1
app/src/main/java/eu/faircode/email/DB.java View File

@ -45,7 +45,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase;
// https://developer.android.com/topic/libraries/architecture/room.html // https://developer.android.com/topic/libraries/architecture/room.html
@Database( @Database(
version = 21,
version = 22,
entities = { entities = {
EntityIdentity.class, EntityIdentity.class,
EntityAccount.class, EntityAccount.class,
@ -265,6 +265,16 @@ public abstract class DB extends RoomDatabase {
db.execSQL("CREATE INDEX `index_message_ui_ignored` ON `message` (`ui_ignored`)"); db.execSQL("CREATE INDEX `index_message_ui_ignored` ON `message` (`ui_ignored`)");
} }
}) })
.addMigrations(new Migration(21, 22) {
@Override
public void migrate(SupportSQLiteDatabase db) {
Log.i(Helper.TAG, "DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("DROP INDEX `index_message_folder_uid`");
db.execSQL("CREATE UNIQUE INDEX `index_message_folder_uid_ui_found` ON `message` (`folder`, `uid`, `ui_found`)");
db.execSQL("DROP INDEX `index_message_msgid_folder`");
db.execSQL("CREATE UNIQUE INDEX `index_message_msgid_folder_ui_found` ON `message` (`msgid`, `folder`, `ui_found`)");
}
})
.build(); .build();
} }


+ 13
- 10
app/src/main/java/eu/faircode/email/DaoMessage.java View File

@ -54,6 +54,7 @@ public interface DaoMessage {
" JOIN folder ON folder.id = message.folder" + " JOIN folder ON folder.id = message.folder" +
" WHERE account.`synchronize`" + " WHERE account.`synchronize`" +
" AND (NOT message.ui_hide OR :debug)" + " AND (NOT message.ui_hide OR :debug)" +
" AND NOT ui_found" +
" GROUP BY account.id, CASE WHEN message.thread IS NULL THEN message.id ELSE message.thread END" + " GROUP BY account.id, CASE WHEN message.thread IS NULL THEN message.id ELSE message.thread END" +
" HAVING SUM(unified) > 0" + " HAVING SUM(unified) > 0" +
" ORDER BY CASE" + " ORDER BY CASE" +
@ -83,7 +84,7 @@ public interface DaoMessage {
" JOIN folder f ON f.id = :folder" + " JOIN folder f ON f.id = :folder" +
" WHERE (message.account = f.account OR folder.type = '" + EntityFolder.OUTBOX + "')" + " WHERE (message.account = f.account OR folder.type = '" + EntityFolder.OUTBOX + "')" +
" AND (NOT message.ui_hide OR :debug)" + " AND (NOT message.ui_hide OR :debug)" +
" AND (NOT :found OR ui_found = :found)" +
" AND ui_found = :found" +
" GROUP BY CASE WHEN message.thread IS NULL THEN message.id ELSE message.thread END" + " GROUP BY CASE WHEN message.thread IS NULL THEN message.id ELSE message.thread END" +
" HAVING SUM(CASE WHEN folder.id = :folder THEN 1 ELSE 0 END) > 0" + " HAVING SUM(CASE WHEN folder.id = :folder THEN 1 ELSE 0 END) > 0" +
" ORDER BY CASE" + " ORDER BY CASE" +
@ -126,26 +127,30 @@ public interface DaoMessage {
@Query("SELECT *" + @Query("SELECT *" +
" FROM message" + " FROM message" +
" WHERE folder = :folder" + " WHERE folder = :folder" +
" AND uid = :uid")
EntityMessage getMessageByUid(long folder, long uid);
" AND uid = :uid" +
" AND ui_found = :found")
EntityMessage getMessageByUid(long folder, long uid, boolean found);
@Query("SELECT *" + @Query("SELECT *" +
" FROM message" + " FROM message" +
" WHERE folder = :folder")
" WHERE folder = :folder" +
" AND NOT ui_found")
List<EntityMessage> getMessageByFolder(long folder); List<EntityMessage> getMessageByFolder(long folder);
@Query("SELECT *" + @Query("SELECT *" +
" FROM message" + " FROM message" +
" WHERE account = :account" + " WHERE account = :account" +
" AND thread = :thread")
" AND thread = :thread" +
" AND NOT ui_found")
List<EntityMessage> getMessageByThread(long account, String thread); List<EntityMessage> getMessageByThread(long account, String thread);
@Query("SELECT message.* FROM message" + @Query("SELECT message.* FROM message" +
" JOIN folder ON folder.id = message.folder" + " JOIN folder ON folder.id = message.folder" +
" WHERE message.account = :account" + " WHERE message.account = :account" +
" AND (message.msgid = :msgid" + " AND (message.msgid = :msgid" +
" OR message.msgid = :reference)")
List<EntityMessage> getMessageByMsgId(long account, String msgid, String reference);
" OR message.msgid = :reference)" +
" AND ui_found = :found")
List<EntityMessage> getMessageByMsgId(long account, String msgid, String reference, boolean found);
@Query("SELECT message.*" + @Query("SELECT message.*" +
", account.name AS accountName, account.color AS accountColor" + ", account.name AS accountName, account.color AS accountColor" +
@ -167,6 +172,7 @@ public interface DaoMessage {
" AND folder.unified" + " AND folder.unified" +
" AND NOT message.ui_seen" + " AND NOT message.ui_seen" +
" AND NOT message.ui_hide" + " AND NOT message.ui_hide" +
" AND NOT message.ui_found" +
" AND NOT message.ui_ignored" + " AND NOT message.ui_ignored" +
" ORDER BY message.received") " ORDER BY message.received")
LiveData<List<EntityMessage>> liveUnseenUnified(); LiveData<List<EntityMessage>> liveUnseenUnified();
@ -208,9 +214,6 @@ public interface DaoMessage {
@Query("UPDATE message SET error = :error WHERE id = :id") @Query("UPDATE message SET error = :error WHERE id = :id")
int setMessageError(long id, String error); int setMessageError(long id, String error);
@Query("UPDATE message SET ui_found = :found WHERE id = :id")
int setMessageFound(long id, boolean found);
@Query("UPDATE message SET content = :content WHERE id = :id") @Query("UPDATE message SET content = :content WHERE id = :id")
int setMessageContent(long id, boolean content); int setMessageContent(long id, boolean content);


+ 2
- 2
app/src/main/java/eu/faircode/email/EntityMessage.java View File

@ -58,8 +58,8 @@ import static androidx.room.ForeignKey.CASCADE;
@Index(value = {"folder"}), @Index(value = {"folder"}),
@Index(value = {"identity"}), @Index(value = {"identity"}),
@Index(value = {"replying"}), @Index(value = {"replying"}),
@Index(value = {"folder", "uid"}, unique = true),
@Index(value = {"msgid", "folder"}, unique = true),
@Index(value = {"folder", "uid", "ui_found"}, unique = true),
@Index(value = {"msgid", "folder", "ui_found"}, unique = true),
@Index(value = {"thread"}), @Index(value = {"thread"}),
@Index(value = {"received"}), @Index(value = {"received"}),
@Index(value = {"ui_seen"}), @Index(value = {"ui_seen"}),


+ 26
- 13
app/src/main/java/eu/faircode/email/FragmentMessages.java View File

@ -644,15 +644,30 @@ public class FragmentMessages extends FragmentEx {
menuSearch.collapseActionView(); menuSearch.collapseActionView();
if (Helper.isPro(getContext())) { if (Helper.isPro(getContext())) {
Intent intent = new Intent();
intent.putExtra("folder", folder);
intent.putExtra("search", query);
Bundle args = new Bundle();
args.putLong("folder", folder);
args.putString("search", query);
new SimpleTask<Void>() {
@Override
protected Void onLoad(Context context, Bundle args) throws Throwable {
DB.getInstance(context).message().deleteFoundMessages();
return null;
}
FragmentMessages fragment = new FragmentMessages();
fragment.setArguments(intent.getExtras());
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("search");
fragmentTransaction.commit();
@Override
protected void onLoaded(Bundle args, Void data) {
Intent intent = new Intent();
intent.putExtra("folder", args.getLong("folder", folder));
intent.putExtra("search", args.getString("search"));
FragmentMessages fragment = new FragmentMessages();
fragment.setArguments(intent.getExtras());
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content_frame, fragment).addToBackStack("search");
fragmentTransaction.commit();
}
}.load(FragmentMessages.this, args);
} else { } else {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content_frame, new FragmentPro()).addToBackStack("pro"); fragmentTransaction.replace(R.id.content_frame, new FragmentPro()).addToBackStack("pro");
@ -741,7 +756,7 @@ public class FragmentMessages extends FragmentEx {
private void loadMessages() { private void loadMessages() {
final DB db = DB.getInstance(getContext()); final DB db = DB.getInstance(getContext());
ViewModelBrowse model = ViewModelProviders.of(this).get(ViewModelBrowse.class);
ViewModelBrowse model = ViewModelProviders.of(getActivity()).get(ViewModelBrowse.class);
model.set(getContext(), folder, search, REMOTE_PAGE_SIZE); model.set(getContext(), folder, search, REMOTE_PAGE_SIZE);
// Observe folder/messages/search // Observe folder/messages/search
@ -760,8 +775,7 @@ public class FragmentMessages extends FragmentEx {
break; break;
case FOLDER: case FOLDER:
if (searchCallback == null) if (searchCallback == null)
searchCallback = new BoundaryCallbackMessages(
this, model,
searchCallback = new BoundaryCallbackMessages(this, model,
new BoundaryCallbackMessages.IBoundaryCallbackMessages() { new BoundaryCallbackMessages.IBoundaryCallbackMessages() {
@Override @Override
public void onLoading() { public void onLoading() {
@ -802,8 +816,7 @@ public class FragmentMessages extends FragmentEx {
} }
} else { } else {
if (searchCallback == null) if (searchCallback == null)
searchCallback = new BoundaryCallbackMessages(
this, model,
searchCallback = new BoundaryCallbackMessages(this, model,
new BoundaryCallbackMessages.IBoundaryCallbackMessages() { new BoundaryCallbackMessages.IBoundaryCallbackMessages() {
@Override @Override
public void onLoading() { public void onLoading() {


+ 3
- 3
app/src/main/java/eu/faircode/email/ServiceSynchronize.java View File

@ -1596,7 +1596,7 @@ public class ServiceSynchronize extends LifecycleService {
List<Message> full = new ArrayList<>(); List<Message> full = new ArrayList<>();
for (Message imessage : isub) { for (Message imessage : isub) {
long uid = ifolder.getUID(imessage); long uid = ifolder.getUID(imessage);
EntityMessage message = db.message().getMessageByUid(folder.id, uid);
EntityMessage message = db.message().getMessageByUid(folder.id, uid, false);
if (message == null) if (message == null)
full.add(imessage); full.add(imessage);
} }
@ -1695,7 +1695,7 @@ public class ServiceSynchronize extends LifecycleService {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
// Find message by uid (fast, no headers required) // Find message by uid (fast, no headers required)
EntityMessage message = db.message().getMessageByUid(folder.id, uid);
EntityMessage message = db.message().getMessageByUid(folder.id, uid, found);
// Find message by Message-ID (slow, headers required) // Find message by Message-ID (slow, headers required)
// - messages in inbox have same id as message sent to self // - messages in inbox have same id as message sent to self
@ -1706,7 +1706,7 @@ public class ServiceSynchronize extends LifecycleService {
String[] refs = helper.getReferences(); String[] refs = helper.getReferences();
String reference = (refs.length == 1 && refs[0].indexOf(BuildConfig.APPLICATION_ID) > 0 ? refs[0] : msgid); String reference = (refs.length == 1 && refs[0].indexOf(BuildConfig.APPLICATION_ID) > 0 ? refs[0] : msgid);
Log.i(Helper.TAG, "Searching for " + msgid + " / " + reference); Log.i(Helper.TAG, "Searching for " + msgid + " / " + reference);
for (EntityMessage dup : db.message().getMessageByMsgId(folder.account, msgid, reference)) {
for (EntityMessage dup : db.message().getMessageByMsgId(folder.account, msgid, reference, found)) {
EntityFolder dfolder = db.folder().getFolder(dup.folder); EntityFolder dfolder = db.folder().getFolder(dup.folder);
boolean outbox = EntityFolder.OUTBOX.equals(dfolder.type); boolean outbox = EntityFolder.OUTBOX.equals(dfolder.type);
Log.i(Helper.TAG, folder.name + " found as id=" + dup.id + "/" + dup.uid + Log.i(Helper.TAG, folder.name + " found as id=" + dup.id + "/" + dup.uid +


+ 8
- 21
app/src/main/java/eu/faircode/email/ViewModelBrowse.java View File

@ -8,9 +8,7 @@ import com.sun.mail.imap.IMAPMessage;
import com.sun.mail.imap.IMAPStore; import com.sun.mail.imap.IMAPStore;
import com.sun.mail.util.FolderClosedIOException; import com.sun.mail.util.FolderClosedIOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import javax.mail.FetchProfile; import javax.mail.FetchProfile;
@ -38,7 +36,6 @@ public class ViewModelBrowse extends ViewModel {
private IMAPStore istore = null; private IMAPStore istore = null;
private IMAPFolder ifolder = null; private IMAPFolder ifolder = null;
private Message[] imessages = null; private Message[] imessages = null;
private List<Long> existing = new ArrayList<>();
private int index; private int index;
private boolean searching = false; private boolean searching = false;
private int loaded = 0; private int loaded = 0;
@ -48,6 +45,10 @@ public class ViewModelBrowse extends ViewModel {
this.fid = folder; this.fid = folder;
this.search = search; this.search = search;
this.pageSize = pageSize; this.pageSize = pageSize;
this.index = -1;
this.searching = false;
this.loaded = 0;
} }
Context getContext() { Context getContext() {
@ -130,15 +131,9 @@ public class ViewModelBrowse extends ViewModel {
try { try {
long uid = ifolder.getUID(isub[j]); long uid = ifolder.getUID(isub[j]);
Log.i(Helper.TAG, "Boundary sync uid=" + uid); Log.i(Helper.TAG, "Boundary sync uid=" + uid);
EntityMessage message = db.message().getMessageByUid(fid, uid);
if (message == null) {
ServiceSynchronize.synchronizeMessage(context, folder, ifolder, (IMAPMessage) isub[j], search != null);
count++;
loaded++;
} else if (search != null) {
existing.add(message.id);
db.message().setMessageFound(message.id, true);
}
ServiceSynchronize.synchronizeMessage(context, folder, ifolder, (IMAPMessage) isub[j], search != null);
count++;
loaded++;
} catch (MessageRemovedException ex) { } catch (MessageRemovedException ex) {
Log.w(Helper.TAG, "Boundary " + ex + "\n" + Log.getStackTraceString(ex)); Log.w(Helper.TAG, "Boundary " + ex + "\n" + Log.getStackTraceString(ex));
} catch (FolderClosedException ex) { } catch (FolderClosedException ex) {
@ -164,24 +159,16 @@ public class ViewModelBrowse extends ViewModel {
} }
void clear() { void clear() {
Log.i(Helper.TAG, "Boundary close");
DB db = DB.getInstance(context);
for (long id : existing)
db.message().setMessageFound(id, false);
db.message().deleteFoundMessages();
Log.i(Helper.TAG, "Boundary clear");
try { try {
if (istore != null) if (istore != null)
istore.close(); istore.close();
} catch (Throwable ex) { } catch (Throwable ex) {
Log.e(Helper.TAG, "Boundary " + ex + "\n" + Log.getStackTraceString(ex)); Log.e(Helper.TAG, "Boundary " + ex + "\n" + Log.getStackTraceString(ex));
} finally { } finally {
context = null;
istore = null; istore = null;
ifolder = null; ifolder = null;
imessages = null; imessages = null;
existing.clear();
} }
} }
} }

Loading…
Cancel
Save