Merge branch 'master' of github.com:M66B/FairEmail

pull/187/head
M66B 5 years ago
commit 261d32c515

@ -82,7 +82,6 @@ Related questions:
* A preview of a message text doesn't (always) appear on Samsung watches because [setLocalOnly](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder.html#setLocalOnly(boolean)) seem to be ignored. Message preview texts are known to be displayed correctly on Pebble 2, Fitbit Charge 3, and Mi band 3 wearables. See also [this FAQ](#user-content-faq126). * A preview of a message text doesn't (always) appear on Samsung watches because [setLocalOnly](https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder.html#setLocalOnly(boolean)) seem to be ignored. Message preview texts are known to be displayed correctly on Pebble 2, Fitbit Charge 3, and Mi band 3 wearables. See also [this FAQ](#user-content-faq126).
* A [bug in Android 6.0](https://issuetracker.google.com/issues/37068143) causes a crash with *... Invalid offset: ... Valid range is ...* when text is selected and tapping outside of the selected text. This bug has been fixed in Android 6.0.1. * A [bug in Android 6.0](https://issuetracker.google.com/issues/37068143) causes a crash with *... Invalid offset: ... Valid range is ...* when text is selected and tapping outside of the selected text. This bug has been fixed in Android 6.0.1.
* Internal (anchor) links will not work because original messages are shown in an embedded WebView in a scrolling view (the conversation list). This is an Android limitation which cannot be fixed or worked around. * Internal (anchor) links will not work because original messages are shown in an embedded WebView in a scrolling view (the conversation list). This is an Android limitation which cannot be fixed or worked around.
* The expanded/collapsed state and the visibility of the message body are sometimes not in sync. This seems to be caused by a bug in the AndroidX ConstraintLayout.
## Planned features ## Planned features

@ -901,24 +901,10 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
// Text size // Text size
if (textSize != 0) { if (textSize != 0) {
float fz_sender = (font_size_sender == null ? textSize : font_size_sender) * (message.unseen > 0 ? 1.1f : 1f);
float fz_subject = (font_size_subject == null ? textSize : font_size_subject) * 0.9f;
tvFrom.setTextSize(TypedValue.COMPLEX_UNIT_PX, fz_sender);
tvSubject.setTextSize(TypedValue.COMPLEX_UNIT_PX, fz_subject);
tvKeywords.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f); tvKeywords.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
tvFolder.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f); tvFolder.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
tvLabels.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f); tvLabels.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
tvPreview.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f); tvPreview.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
if (avatars) {
int px = Math.round(fz_sender + fz_subject + (compact ? 0 : textSize * 0.9f));
ViewGroup.LayoutParams lparams = ibAvatar.getLayoutParams();
if (lparams.width != px || lparams.height != px) {
lparams.width = px;
lparams.height = px;
ibAvatar.requestLayout();
}
}
} }
// Selected / disabled // Selected / disabled
@ -959,27 +945,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
tvError.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f); tvError.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
} }
// Unseen bindSeen(message);
Typeface typeface = (message.unseen > 0 ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tvFrom.setTypeface(typeface);
tvSize.setTypeface(typeface);
tvTime.setTypeface(typeface);
if (subject_italic)
if (message.unseen > 0)
tvSubject.setTypeface(null, Typeface.BOLD_ITALIC);
else
tvSubject.setTypeface(null, Typeface.ITALIC);
else
tvSubject.setTypeface(typeface);
tvCount.setTypeface(typeface);
int colorUnseen = (message.unseen > 0 ? colorUnread : colorRead);
if (!Objects.equals(tvFrom.getTag(), colorUnseen)) {
tvFrom.setTag(colorUnseen);
tvFrom.setTextColor(colorUnseen);
tvSize.setTextColor(colorUnseen);
tvTime.setTextColor(colorUnseen);
}
// Account color // Account color
int colorBackground = int colorBackground =
@ -1382,6 +1348,46 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
pbCalendarWait.setVisibility(View.GONE); pbCalendarWait.setVisibility(View.GONE);
} }
private void bindSeen(TupleMessageEx message) {
if (textSize != 0) {
float fz_sender = (font_size_sender == null ? textSize : font_size_sender) * (message.unseen > 0 ? 1.1f : 1f);
float fz_subject = (font_size_subject == null ? textSize : font_size_subject) * 0.9f;
tvFrom.setTextSize(TypedValue.COMPLEX_UNIT_PX, fz_sender);
tvSubject.setTextSize(TypedValue.COMPLEX_UNIT_PX, fz_subject);
if (avatars) {
int px = Math.round(fz_sender + fz_subject + (compact ? 0 : textSize * 0.9f));
ViewGroup.LayoutParams lparams = ibAvatar.getLayoutParams();
if (lparams.width != px || lparams.height != px) {
lparams.width = px;
lparams.height = px;
ibAvatar.requestLayout();
}
}
}
Typeface typeface = (message.unseen > 0 ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
tvFrom.setTypeface(typeface);
tvSize.setTypeface(typeface);
tvTime.setTypeface(typeface);
if (subject_italic)
if (message.unseen > 0)
tvSubject.setTypeface(null, Typeface.BOLD_ITALIC);
else
tvSubject.setTypeface(null, Typeface.ITALIC);
else
tvSubject.setTypeface(typeface);
tvCount.setTypeface(typeface);
int colorUnseen = (message.unseen > 0 ? colorUnread : colorRead);
if (!Objects.equals(tvFrom.getTag(), colorUnseen)) {
tvFrom.setTag(colorUnseen);
tvFrom.setTextColor(colorUnseen);
tvSize.setTextColor(colorUnseen);
tvTime.setTextColor(colorUnseen);
}
}
private void bindFlagged(TupleMessageEx message, boolean expanded) { private void bindFlagged(TupleMessageEx message, boolean expanded) {
boolean pro = ActivityBilling.isPro(context); boolean pro = ActivityBilling.isPro(context);
boolean flagged = (message.count - message.unflagged) > 0; boolean flagged = (message.count - message.unflagged) > 0;
@ -3048,7 +3054,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
} else { } else {
message.ui_seen = !message.ui_seen; message.ui_seen = !message.ui_seen;
message.unseen = (message.ui_seen ? 0 : message.count); message.unseen = (message.ui_seen ? 0 : message.count);
bindTo(message, false); bindSeen(message);
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putLong("id", message.id); args.putLong("id", message.id);
@ -3529,16 +3535,6 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
else { else {
boolean expanded = !properties.getValue("expanded", message.id); boolean expanded = !properties.getValue("expanded", message.id);
// Prevent flicker
if (expanded && message.accountAutoSeen && !message.folderReadOnly) {
message.unseen = 0;
message.ui_seen = true;
message.visible_unseen = 0;
message.ui_unsnoozed = false;
}
properties.setValue("expanded", message.id, expanded);
bindTo(message, expanded);
properties.setExpanded(message, expanded); properties.setExpanded(message, expanded);
// Needed to scroll to item after collapsing other items // Needed to scroll to item after collapsing other items
@ -4288,10 +4284,9 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if (amessage == null || !amessage.id.equals(id)) if (amessage == null || !amessage.id.equals(id))
return; return;
properties.setExpanded(message, false);
message.ui_seen = args.getBoolean("seen"); message.ui_seen = args.getBoolean("seen");
message.unseen = (message.ui_seen ? 0 : message.count); message.unseen = (message.ui_seen ? 0 : message.count);
bindTo(message, false); properties.setExpanded(message, false);
} }
@Override @Override

@ -101,6 +101,7 @@ public class ContactInfo {
private static final int FAVICON_READ_BYTES = 2048; private static final int FAVICON_READ_BYTES = 2048;
private static final long CACHE_CONTACT_DURATION = 2 * 60 * 1000L; // milliseconds private static final long CACHE_CONTACT_DURATION = 2 * 60 * 1000L; // milliseconds
private static final long CACHE_GRAVATAR_DURATION = 2 * 60 * 60 * 1000L; // milliseconds private static final long CACHE_GRAVATAR_DURATION = 2 * 60 * 60 * 1000L; // milliseconds
private static final long CACHE_FAVICON_DURATION = 2 * 7 * 24 * 60 * 60 * 1000L; // milliseconds
private ContactInfo() { private ContactInfo() {
} }
@ -137,6 +138,21 @@ public class ContactInfo {
return (new Date().getTime() - time > CACHE_CONTACT_DURATION); return (new Date().getTime() - time > CACHE_CONTACT_DURATION);
} }
static void cleanup(Context context) {
long now = new Date().getTime();
// Favicons
Log.i("Cleanup favicons");
File[] favicons = new File(context.getCacheDir(), "favicons").listFiles();
if (favicons != null)
for (File file : favicons)
if (file.lastModified() + CACHE_FAVICON_DURATION < now) {
Log.i("Deleting " + file);
if (!file.delete())
Log.w("Error deleting " + file);
}
}
static void clearCache(Context context) { static void clearCache(Context context) {
clearCache(context, true); clearCache(context, true);
} }

@ -1653,6 +1653,19 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
setValue("expanded", message.id, value); setValue("expanded", message.id, value);
final int p = adapter.getPositionForKey(message.id);
if (p != NO_POSITION)
rvMessage.post(new Runnable() {
@Override
public void run() {
try {
adapter.notifyItemChanged(p);
} catch (Throwable ex) {
Log.e(ex);
}
}
});
// Collapse other messages // Collapse other messages
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
boolean expand_all = prefs.getBoolean("expand_all", false); boolean expand_all = prefs.getBoolean("expand_all", false);

@ -54,8 +54,9 @@ public abstract class SimpleTask<T> implements LifecycleObserver {
private String name; private String name;
private Future<?> future; private Future<?> future;
private ExecutorService localExecutor;
private static ExecutorService executor = null; private static ExecutorService globalExecutor = null;
private static final List<SimpleTask> tasks = new ArrayList<>(); private static final List<SimpleTask> tasks = new ArrayList<>();
static final String ACTION_TASK_COUNT = BuildConfig.APPLICATION_ID + ".ACTION_TASK_COUNT"; static final String ACTION_TASK_COUNT = BuildConfig.APPLICATION_ID + ".ACTION_TASK_COUNT";
@ -70,6 +71,25 @@ public abstract class SimpleTask<T> implements LifecycleObserver {
return this; return this;
} }
public SimpleTask<T> setExecutor(ExecutorService executor) {
this.localExecutor = executor;
return this;
}
private ExecutorService getExecutor(Context context) {
if (localExecutor != null)
return localExecutor;
if (globalExecutor == null) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
int threads = prefs.getInt("query_threads", Runtime.getRuntime().availableProcessors());
Log.i("Task threads=" + threads);
globalExecutor = Helper.getBackgroundExecutor(threads, "task");
}
return globalExecutor;
}
public void execute(Context context, LifecycleOwner owner, @NonNull Bundle args, @NonNull String name) { public void execute(Context context, LifecycleOwner owner, @NonNull Bundle args, @NonNull String name) {
run(context, owner, args, name); run(context, owner, args, name);
} }
@ -114,14 +134,7 @@ public abstract class SimpleTask<T> implements LifecycleObserver {
onException(args, ex); onException(args, ex);
} }
if (executor == null) { future = getExecutor(context).submit(new Runnable() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
int threads = prefs.getInt("query_threads", Runtime.getRuntime().availableProcessors());
Log.i("Task threads=" + threads);
executor = Helper.getBackgroundExecutor(threads, "task");
}
future = executor.submit(new Runnable() {
private Object data; private Object data;
private long elapsed; private long elapsed;
private Throwable ex; private Throwable ex;

@ -248,6 +248,10 @@ public class WorkerCleanup extends Worker {
} }
} }
// Cleanup contact info
if (!manual)
ContactInfo.cleanup(context);
Log.i("Cleanup FTS=" + fts); Log.i("Cleanup FTS=" + fts);
if (fts) { if (fts) {
int deleted = 0; int deleted = 0;

@ -1,5 +1,7 @@
FairEmail is easy to setup and works with virtually all email providers, including Gmail, Outlook and Yahoo! FairEmail is easy to setup and works with virtually all email providers, including Gmail, Outlook and Yahoo!
<b>Note that OAuth was not approved for the F-Droid build. For this you'll need to use the Play store version or the GitHub release.</b>
FairEmail might be for you if you value your privacy. FairEmail might be for you if you value your privacy.
<i>FairEmail is an email client only, so you need to bring your own email address.</i> <i>FairEmail is an email client only, so you need to bring your own email address.</i>

Loading…
Cancel
Save