Cache message list view models

pull/156/head
M66B 6 years ago
parent b2e445c04b
commit 4a3d18104d

@ -25,10 +25,6 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.paging.PagedList; import androidx.paging.PagedList;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
@ -72,6 +68,7 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
private boolean server; private boolean server;
private String query; private String query;
private int pageSize; private int pageSize;
private IBoundaryCallbackMessages intf; private IBoundaryCallbackMessages intf;
private Handler handler; private Handler handler;
@ -80,7 +77,6 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
private boolean destroyed = false; private boolean destroyed = false;
private boolean error = false; private boolean error = false;
private int index = 0; private int index = 0;
private boolean loading = false;
private List<Long> messages = null; private List<Long> messages = null;
@ -96,39 +92,17 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
void onError(Throwable ex); void onError(Throwable ex);
} }
BoundaryCallbackMessages( BoundaryCallbackMessages(Context context, long folder, boolean server, String query, int pageSize) {
Context context, LifecycleOwner owner,
long folder, boolean server, String query, int pageSize,
IBoundaryCallbackMessages intf) {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.folder = (folder < 0 ? null : folder); this.folder = (folder < 0 ? null : folder);
this.server = server; this.server = server;
this.query = query; this.query = query;
this.pageSize = pageSize; this.pageSize = pageSize;
this.intf = intf; }
void setCallback(IBoundaryCallbackMessages intf) {
this.handler = new Handler(); this.handler = new Handler();
this.intf = intf;
owner.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroyed() {
destroyed = true;
executor.submit(new Runnable() {
@Override
public void run() {
Log.i("Boundary destroy");
try {
if (istore != null)
istore.close();
} catch (Throwable ex) {
Log.e("Boundary", ex);
}
}
});
}
});
} }
@Override @Override
@ -153,43 +127,40 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
if (destroyed) if (destroyed)
return; return;
loading = true;
fetched = 0; fetched = 0;
handler.post(new Runnable() { if (intf != null)
@Override handler.post(new Runnable() {
public void run() { @Override
intf.onLoading(); public void run() {
} intf.onLoading();
}); }
});
if (server) if (server)
fetched = load_server(); fetched = load_server();
else else
fetched = load_device(); fetched = load_device();
} catch (final Throwable ex) { } catch (final Throwable ex) {
Log.e("Boundary", ex); Log.e("Boundary", ex);
handler.post(new Runnable() { if (intf != null)
@Override handler.post(new Runnable() {
public void run() { @Override
intf.onError(ex); public void run() {
} intf.onError(ex);
}); }
});
} finally { } finally {
loading = false; if (intf != null)
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
intf.onLoaded(fetched); intf.onLoaded(fetched);
} }
}); });
} }
} }
}); });
} }
boolean isLoading() {
return loading;
}
private int load_device() { private int load_device() {
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
@ -468,4 +439,21 @@ public class BoundaryCallbackMessages extends PagedList.BoundaryCallback<TupleMe
Log.i("Boundary server done"); Log.i("Boundary server done");
return found; return found;
} }
void clear() {
destroyed = true;
executor.submit(new Runnable() {
@Override
public void run() {
Log.i("Boundary destroy");
try {
if (istore != null)
istore.close();
} catch (Throwable ex) {
Log.e("Boundary", ex);
}
}
});
}
} }

@ -71,10 +71,10 @@ import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProviders;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList; import androidx.paging.PagedList;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.selection.Selection; import androidx.recyclerview.selection.Selection;
@ -104,8 +104,6 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.DAY_IN_MILLIS;
@ -182,10 +180,6 @@ public class FragmentMessages extends FragmentBase {
private LongSparseArray<List<EntityAttachment>> attachments = new LongSparseArray<>(); private LongSparseArray<List<EntityAttachment>> attachments = new LongSparseArray<>();
private LongSparseArray<TupleAccountSwipes> accountSwipes = new LongSparseArray<>(); private LongSparseArray<TupleAccountSwipes> accountSwipes = new LongSparseArray<>();
private BoundaryCallbackMessages boundaryCallback = null;
private ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory);
private final int action_seen = 1; private final int action_seen = 1;
private final int action_unseen = 2; private final int action_unseen = 2;
private final int action_snooze = 3; private final int action_snooze = 3;
@ -199,8 +193,6 @@ public class FragmentMessages extends FragmentBase {
private NumberFormat nf = NumberFormat.getNumberInstance(); private NumberFormat nf = NumberFormat.getNumberInstance();
private static final int LOCAL_PAGE_SIZE = 100;
private static final int REMOTE_PAGE_SIZE = 10;
private static final int UNDO_TIMEOUT = 5000; // milliseconds private static final int UNDO_TIMEOUT = 5000; // milliseconds
private static final int SWIPE_DISABLE_SELECT_DURATION = 1500; // milliseconds private static final int SWIPE_DISABLE_SELECT_DURATION = 1500; // milliseconds
@ -679,7 +671,7 @@ public class FragmentMessages extends FragmentBase {
if (viewType == AdapterMessage.ViewType.THREAD) { if (viewType == AdapterMessage.ViewType.THREAD) {
ViewModelMessages model = ViewModelProviders.of(getActivity()).get(ViewModelMessages.class); ViewModelMessages model = ViewModelProviders.of(getActivity()).get(ViewModelMessages.class);
model.observePrevNext(getViewLifecycleOwner(), id, found, new ViewModelMessages.IPrevNext() { model.observePrevNext(getViewLifecycleOwner(), id, new ViewModelMessages.IPrevNext() {
@Override @Override
public void onPrevious(boolean exists, Long id) { public void onPrevious(boolean exists, Long id) {
previous = id; previous = id;
@ -2540,7 +2532,7 @@ public class FragmentMessages extends FragmentBase {
private void loadMessages(final boolean top) { private void loadMessages(final boolean top) {
if (viewType == AdapterMessage.ViewType.THREAD && autonext) { if (viewType == AdapterMessage.ViewType.THREAD && autonext) {
ViewModelMessages model = ViewModelProviders.of(getActivity()).get(ViewModelMessages.class); ViewModelMessages model = ViewModelProviders.of(getActivity()).get(ViewModelMessages.class);
model.observePrevNext(getViewLifecycleOwner(), id, found, new ViewModelMessages.IPrevNext() { model.observePrevNext(getViewLifecycleOwner(), id, new ViewModelMessages.IPrevNext() {
boolean once = false; boolean once = false;
@Override @Override
@ -2568,126 +2560,48 @@ public class FragmentMessages extends FragmentBase {
loadMessagesNext(top); loadMessagesNext(top);
} }
private void loadMessagesNext(final boolean top) { private boolean loading = false;
if (viewType == AdapterMessage.ViewType.FOLDER || viewType == AdapterMessage.ViewType.SEARCH)
if (boundaryCallback == null)
boundaryCallback = new BoundaryCallbackMessages(
getContext(), getViewLifecycleOwner(),
folder, server || viewType == AdapterMessage.ViewType.FOLDER,
query, REMOTE_PAGE_SIZE,
new BoundaryCallbackMessages.IBoundaryCallbackMessages() {
@Override
public void onLoading() {
pbWait.setVisibility(View.VISIBLE);
}
@Override
public void onLoaded(int fetched) {
pbWait.setVisibility(View.GONE);
Integer submitted = (Integer) rvMessage.getTag();
if (submitted == null)
submitted = 0;
if (submitted + fetched == 0)
tvNoEmail.setVisibility(View.VISIBLE);
}
@Override private void loadMessagesNext(final boolean top) {
public void onError(Throwable ex) { ViewModelMessages model = ViewModelProviders.of(getActivity()).get(ViewModelMessages.class);
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED))
if (ex instanceof IllegalArgumentException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else
new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner())
.setMessage(Helper.formatThrowable(ex))
.setPositiveButton(android.R.string.cancel, null)
.create()
.show();
}
});
// Observe folder/messages/search
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
String sort = prefs.getString("sort", "time");
boolean filter_seen = prefs.getBoolean("filter_seen", false);
boolean filter_unflagged = prefs.getBoolean("filter_unflagged", false);
boolean filter_snoozed = prefs.getBoolean("filter_snoozed", true);
boolean debug = prefs.getBoolean("debug", false);
Log.i("Load messages type=" + viewType +
" sort=" + sort +
" filter seen=" + filter_seen + " unflagged=" + filter_unflagged + " snoozed=" + filter_snoozed +
" debug=" + debug);
// Sort changed
final ViewModelMessages modelMessages = ViewModelProviders.of(getActivity()).get(ViewModelMessages.class);
modelMessages.removeObservers(viewType, getViewLifecycleOwner());
DB db = DB.getInstance(getContext());
LivePagedListBuilder<Integer, TupleMessageEx> builder = null;
switch (viewType) {
case UNIFIED:
builder = new LivePagedListBuilder<>(
db.message().pagedUnifiedInbox(
threading,
sort,
filter_seen, filter_unflagged, filter_snoozed,
false,
debug),
LOCAL_PAGE_SIZE);
break;
case FOLDER:
PagedList.Config configFolder = new PagedList.Config.Builder()
.setPageSize(LOCAL_PAGE_SIZE)
.setPrefetchDistance(REMOTE_PAGE_SIZE)
.build();
builder = new LivePagedListBuilder<>(
db.message().pagedFolder(
folder, threading,
sort,
filter_seen, filter_unflagged, filter_snoozed,
false,
debug),
configFolder);
builder.setBoundaryCallback(boundaryCallback);
break;
case THREAD: LiveData<PagedList<TupleMessageEx>> liveMessages = model.getPagedList(
builder = new LivePagedListBuilder<>( getContext(), getViewLifecycleOwner(),
db.message().pagedThread(account, thread, threading ? null : id, debug), LOCAL_PAGE_SIZE); viewType, account, folder, thread, id, query, server,
break; new BoundaryCallbackMessages.IBoundaryCallbackMessages() {
@Override
public void onLoading() {
loading = true;
pbWait.setVisibility(View.VISIBLE);
}
case SEARCH: @Override
PagedList.Config configSearch = new PagedList.Config.Builder() public void onLoaded(int fetched) {
.setPageSize(LOCAL_PAGE_SIZE) loading = false;
.setPrefetchDistance(REMOTE_PAGE_SIZE) pbWait.setVisibility(View.GONE);
.build();
if (folder < 0) Integer submitted = (Integer) rvMessage.getTag();
builder = new LivePagedListBuilder<>( if (submitted == null)
db.message().pagedUnifiedInbox( submitted = 0;
threading, if (submitted + fetched == 0)
"time", tvNoEmail.setVisibility(View.VISIBLE);
false, false, false, }
true,
debug),
configSearch);
else
builder = new LivePagedListBuilder<>(
db.message().pagedFolder(
folder, threading,
"time",
false, false, false,
true,
debug),
configSearch);
builder.setBoundaryCallback(boundaryCallback);
break;
}
builder.setFetchExecutor(executor); @Override
public void onError(Throwable ex) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED))
if (ex instanceof IllegalArgumentException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else
new DialogBuilderLifecycle(getContext(), getViewLifecycleOwner())
.setMessage(Helper.formatThrowable(ex))
.setPositiveButton(android.R.string.cancel, null)
.create()
.show();
}
});
modelMessages.setMessages(viewType, getViewLifecycleOwner(), builder.build()); liveMessages.observe(getViewLifecycleOwner(), new Observer<PagedList<TupleMessageEx>>() {
modelMessages.observe(viewType, getViewLifecycleOwner(), new Observer<PagedList<TupleMessageEx>>() {
private boolean topped = false; private boolean topped = false;
@Override @Override
@ -2714,9 +2628,11 @@ public class FragmentMessages extends FragmentBase {
rvMessage.setTag(messages.size()); rvMessage.setTag(messages.size());
if (boundaryCallback == null || !boundaryCallback.isLoading()) boolean hasBoundary = (viewType == AdapterMessage.ViewType.FOLDER || viewType == AdapterMessage.ViewType.SEARCH);
if (!hasBoundary || !loading)
pbWait.setVisibility(View.GONE); pbWait.setVisibility(View.GONE);
if (boundaryCallback == null && messages.size() == 0) if (!hasBoundary && messages.size() == 0)
tvNoEmail.setVisibility(View.VISIBLE); tvNoEmail.setVisibility(View.VISIBLE);
if (messages.size() > 0) { if (messages.size() > 0) {
tvNoEmail.setVisibility(View.GONE); tvNoEmail.setVisibility(View.GONE);

@ -19,6 +19,12 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B) Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/ */
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
@ -26,77 +32,184 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.OnLifecycleEvent; import androidx.lifecycle.OnLifecycleEvent;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList; import androidx.paging.PagedList;
import androidx.preference.PreferenceManager;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ViewModelMessages extends ViewModel { public class ViewModelMessages extends ViewModel {
private Map<AdapterMessage.ViewType, LiveData<PagedList<TupleMessageEx>>> messages = new HashMap<>(); private AdapterMessage.ViewType last = AdapterMessage.ViewType.UNIFIED;
private Map<AdapterMessage.ViewType, Model> models = new HashMap<>();
private ExecutorService executor = Executors.newCachedThreadPool(Helper.backgroundThreadFactory);
private static final int LOCAL_PAGE_SIZE = 100;
private static final int REMOTE_PAGE_SIZE = 10;
LiveData<PagedList<TupleMessageEx>> getPagedList(
Context context, LifecycleOwner owner,
final AdapterMessage.ViewType viewType,
long account, long folder, String thread, long id,
String query, boolean server,
BoundaryCallbackMessages.IBoundaryCallbackMessages callback) {
Args args = new Args(context, account, folder, thread, id, query, server);
Log.i("Get model " + viewType + " " + args);
dump();
Model model = models.get(viewType);
if (model == null || !model.args.equals(args)) {
Log.i("Creating model");
void setMessages(AdapterMessage.ViewType viewType, LifecycleOwner owner, final LiveData<PagedList<TupleMessageEx>> messages) { if (model != null)
if (viewType == AdapterMessage.ViewType.UNIFIED) model.clear();
viewType = AdapterMessage.ViewType.FOLDER;
this.messages.put(viewType, messages); DB db = DB.getInstance(context);
BoundaryCallbackMessages boundary = null;
if (viewType == AdapterMessage.ViewType.FOLDER || viewType == AdapterMessage.ViewType.SEARCH)
boundary = new BoundaryCallbackMessages(context,
args.folder, args.server || viewType == AdapterMessage.ViewType.FOLDER,
args.query, REMOTE_PAGE_SIZE);
LivePagedListBuilder<Integer, TupleMessageEx> builder = null;
switch (viewType) {
case UNIFIED:
builder = new LivePagedListBuilder<>(
db.message().pagedUnifiedInbox(
args.threading,
args.sort,
args.filter_seen, args.filter_unflagged, args.filter_snoozed,
false,
args.debug),
LOCAL_PAGE_SIZE);
break;
case FOLDER:
PagedList.Config configFolder = new PagedList.Config.Builder()
.setPageSize(LOCAL_PAGE_SIZE)
.setPrefetchDistance(REMOTE_PAGE_SIZE)
.build();
builder = new LivePagedListBuilder<>(
db.message().pagedFolder(
args.folder, args.threading,
args.sort,
args.filter_seen, args.filter_unflagged, args.filter_snoozed,
false,
args.debug),
configFolder);
builder.setBoundaryCallback(boundary);
break;
case THREAD:
builder = new LivePagedListBuilder<>(
db.message().pagedThread(
args.account, args.thread,
args.threading ? null : args.id,
args.debug), LOCAL_PAGE_SIZE);
break;
case SEARCH:
PagedList.Config configSearch = new PagedList.Config.Builder()
.setPageSize(LOCAL_PAGE_SIZE)
.setPrefetchDistance(REMOTE_PAGE_SIZE)
.build();
if (args.folder < 0)
builder = new LivePagedListBuilder<>(
db.message().pagedUnifiedInbox(
args.threading,
"time",
false, false, false,
true,
args.debug),
configSearch);
else
builder = new LivePagedListBuilder<>(
db.message().pagedFolder(
args.folder, args.threading,
"time",
false, false, false,
true,
args.debug),
configSearch);
builder.setBoundaryCallback(boundary);
break;
}
if (viewType == AdapterMessage.ViewType.FOLDER) builder.setFetchExecutor(executor);
this.messages.remove(AdapterMessage.ViewType.SEARCH);
model = new Model(args, builder.build(), boundary);
models.put(viewType, model);
if (viewType == AdapterMessage.ViewType.THREAD)
owner.getLifecycle().addObserver(new LifecycleObserver() { owner.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroyed() { public void onDestroyed() {
Log.i("Removed model thread"); Log.i("Destroy model " + viewType);
ViewModelMessages.this.messages.remove(AdapterMessage.ViewType.THREAD);
} if (viewType == AdapterMessage.ViewType.THREAD)
}); remove(viewType);
else {
// Keep list up-to-date for previous/next navigation dump();
messages.observeForever(new Observer<PagedList<TupleMessageEx>>() {
@Override
public void onChanged(PagedList<TupleMessageEx> messages) {
} }
}); });
if (viewType != AdapterMessage.ViewType.THREAD)
// Keep list up-to-date for previous/next navigation
model.list.observeForever(new Observer<PagedList<TupleMessageEx>>() {
@Override
public void onChanged(PagedList<TupleMessageEx> messages) {
}
});
} }
}
void observe(AdapterMessage.ViewType viewType, LifecycleOwner owner, Observer<PagedList<TupleMessageEx>> observer) { if (model.boundary != null)
if (viewType == AdapterMessage.ViewType.UNIFIED) model.boundary.setCallback(callback);
viewType = AdapterMessage.ViewType.FOLDER;
if (owner.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.INITIALIZED)) if (viewType == AdapterMessage.ViewType.UNIFIED) {
messages.get(viewType).observe(owner, observer); remove(AdapterMessage.ViewType.FOLDER);
} remove(AdapterMessage.ViewType.SEARCH);
} else if (viewType == AdapterMessage.ViewType.FOLDER)
remove(AdapterMessage.ViewType.SEARCH);
void removeObservers(AdapterMessage.ViewType viewType, LifecycleOwner owner) { last = viewType;
if (viewType == AdapterMessage.ViewType.UNIFIED)
viewType = AdapterMessage.ViewType.FOLDER;
if (owner.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.INITIALIZED)) { Log.i("Returning model=" + viewType);
LiveData<PagedList<TupleMessageEx>> list = messages.get(viewType); dump();
if (list != null)
list.removeObservers(owner); return model.list;
}
} }
@Override @Override
protected void onCleared() { protected void onCleared() {
messages.clear(); for (AdapterMessage.ViewType viewType : new ArrayList<>(models.keySet()))
remove(viewType);
} }
void observePrevNext(LifecycleOwner owner, final long id, boolean found, final IPrevNext intf) { private void remove(AdapterMessage.ViewType viewType) {
AdapterMessage.ViewType viewType = Model model = models.get(viewType);
(found ? AdapterMessage.ViewType.SEARCH : AdapterMessage.ViewType.FOLDER); if (model != null) {
model.clear();
models.remove(viewType);
}
}
void observePrevNext(LifecycleOwner owner, final long id, final IPrevNext intf) {
Log.i("Observe prev/next model=" + last);
LiveData<PagedList<TupleMessageEx>> list = messages.get(viewType); Model model = models.get(last);
if (list == null) { if (model == null) {
Log.w("Observe previous/next without list"); Log.w("Observe previous/next without list");
return; return;
} }
Log.i("Observe previous/next id=" + id); Log.i("Observe previous/next id=" + id);
list.observe(owner, new Observer<PagedList<TupleMessageEx>>() { model.list.observe(owner, new Observer<PagedList<TupleMessageEx>>() {
@Override @Override
public void onChanged(PagedList<TupleMessageEx> messages) { public void onChanged(PagedList<TupleMessageEx> messages) {
Log.i("Observe previous/next id=" + id + " messages=" + messages.size()); Log.i("Observe previous/next id=" + id + " messages=" + messages.size());
@ -136,6 +249,95 @@ public class ViewModelMessages extends ViewModel {
}); });
} }
private class Args {
private long account;
private long folder;
private String thread;
private long id;
private String query;
private boolean server;
private boolean threading;
private String sort;
private boolean filter_seen;
private boolean filter_unflagged;
private boolean filter_snoozed;
private boolean debug;
Args(Context context,
long account, long folder, String thread, long id,
String query, boolean server) {
this.account = account;
this.folder = folder;
this.thread = thread;
this.id = id;
this.query = query;
this.server = server;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
this.threading = prefs.getBoolean("threading", true);
this.sort = prefs.getString("sort", "time");
this.filter_seen = prefs.getBoolean("filter_seen", false);
this.filter_unflagged = prefs.getBoolean("filter_unflagged", false);
this.filter_snoozed = prefs.getBoolean("filter_snoozed", true);
this.debug = prefs.getBoolean("debug", false);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof Args) {
Args other = (Args) obj;
return (this.account == other.account &&
this.folder == other.folder &&
Objects.equals(this.thread, other.thread) &&
this.id == other.id &&
Objects.equals(this.query, other.query) &&
this.server == other.server &&
this.threading == other.threading &&
Objects.equals(this.sort, other.sort) &&
this.filter_seen == other.filter_seen &&
this.filter_unflagged == other.filter_unflagged &&
this.filter_snoozed == other.filter_snoozed &&
this.debug == other.debug);
} else
return false;
}
@NonNull
@Override
public String toString() {
return "folder=" + account + ":" + folder + " thread=" + thread + ":" + id +
" query=" + query + ":" + server + "" +
" threading=" + threading +
" sort=" + sort +
" filter seen=" + filter_seen + " unflagged=" + filter_unflagged + " snoozed=" + filter_snoozed +
" debug=" + debug;
}
}
private void dump() {
Log.i("Current models=" + TextUtils.join(", ", models.keySet()));
}
private class Model {
private Args args;
private LiveData<PagedList<TupleMessageEx>> list;
private BoundaryCallbackMessages boundary;
Model(Args args, LiveData<PagedList<TupleMessageEx>> list, BoundaryCallbackMessages boundary) {
this.args = args;
this.list = list;
this.boundary = boundary;
}
private void clear() {
if (this.boundary != null)
this.boundary.clear();
}
}
interface IPrevNext { interface IPrevNext {
void onPrevious(boolean exists, Long id); void onPrevious(boolean exists, Long id);

Loading…
Cancel
Save