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-2022 by Marcel Bokhorst (M66B)
*/
import static android.app.Activity.RESULT_OK;
import android.Manifest;
import android.animation.Animator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.RemoteAction;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.text.LineBreaker;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.LocaleList;
import android.os.Parcelable;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.provider.Settings;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.DynamicDrawableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
import android.text.util.Linkify;
import android.util.Pair;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.textclassifier.ConversationAction;
import android.view.textclassifier.ConversationActions;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu;
import androidx.constraintlayout.helper.widget.Flow;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Group;
import androidx.core.content.FileProvider;
import androidx.core.content.pm.ShortcutInfoCompat;
import androidx.core.graphics.ColorUtils;
import androidx.core.os.BuildCompat;
import androidx.core.view.MenuCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.paging.AsyncPagedListDiffer;
import androidx.paging.PagedList;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.selection.ItemDetailsLookup;
import androidx.recyclerview.selection.Selection;
import androidx.recyclerview.selection.SelectionTracker;
import androidx.recyclerview.widget.AdapterListUpdateCallback;
import androidx.recyclerview.widget.AsyncDifferConfig;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import androidx.webkit.WebViewFeature;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.w3c.dom.css.CSSStyleSheet;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import javax.mail.Address;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import biweekly.Biweekly;
import biweekly.ICalendar;
import biweekly.component.VEvent;
import biweekly.parameter.ParticipationStatus;
import biweekly.property.Attendee;
import biweekly.property.Method;
import biweekly.property.Organizer;
import biweekly.property.RawProperty;
import biweekly.util.ICalDate;
public class AdapterMessage extends RecyclerView.Adapter {
private Fragment parentFragment;
private String type;
private boolean found;
private String searched;
private ViewType viewType;
private boolean compact;
private int zoom;
private String sort;
private boolean ascending;
private boolean filter_duplicates;
private boolean filter_trash;
private IProperties properties;
private Context context;
private LifecycleOwner owner;
private LayoutInflater inflater;
private SharedPreferences prefs;
private boolean accessibility;
private int dp1;
private int dp12;
private int dp60;
private boolean suitable;
private boolean unmetered;
private int colorStripeWidth;
private int colorAccent;
private int textColorPrimary;
private int textColorSecondary;
private int textColorTertiary;
private int textColorLink;
private int colorUnreadHighlight;
private int colorUnread;
private int colorRead;
private int colorSubject;
private int colorVerified;
private int colorEncrypt;
private int colorSeparator;
private int colorWarning;
private int colorError;
private int colorControlNormal;
private boolean hasWebView;
private boolean pin;
private boolean contacts;
private float textSize;
private boolean date;
private boolean cards;
private boolean shadow_unread;
private boolean shadow_highlight;
private boolean threading;
private boolean threading_unread;
private boolean indentation;
private boolean avatars;
private boolean color_stripe;
private boolean check_authentication;
private boolean check_tls;
private boolean check_reply_domain;
private boolean check_mx;
private boolean check_blocklist;
private MessageHelper.AddressFormat email_format;
private boolean prefer_contact;
private boolean only_contact;
private boolean distinguish_contacts;
private boolean show_recipients;
private Float font_size_sender;
private Float font_size_subject;
private boolean subject_top;
private boolean subject_italic;
private String sender_ellipsize;
private String subject_ellipsize;
private boolean keywords_header;
private boolean labels_header;
private boolean flags;
private boolean flags_background;
private boolean preview;
private boolean preview_italic;
private int preview_lines;
private int message_zoom;
private boolean attachments_alt;
private boolean thumbnails;
private boolean contrast;
private String display_font;
private boolean inline;
private boolean collapse_quotes;
private boolean authentication;
private boolean authentication_indicator;
private boolean infra;
private boolean autoclose_unseen;
private boolean collapse_marked;
private boolean language_detection;
private List languages;
private static boolean debug;
private int level;
private boolean webview_legacy;
private boolean show_recent;
private boolean gotoTop = false;
private Integer gotoPos = null;
private boolean firstClick = false;
private AsyncPagedListDiffer differ;
private Map keyPosition = new HashMap<>();
private Map positionKey = new HashMap<>();
private SelectionTracker selectionTracker = null;
private RecyclerView rv = null;
private Parcelable savedState = null;
enum ViewType {UNIFIED, FOLDER, THREAD, SEARCH}
private NumberFormat NF = NumberFormat.getNumberInstance();
private DateFormat TF;
private DateFormat DTF;
private static final ExecutorService executor =
Helper.getBackgroundExecutor(2, "differ");
private static final int MAX_RECIPIENTS_COMPACT = 3;
private static final int MAX_RECIPIENTS_NORMAL = 7;
public class ViewHolder extends RecyclerView.ViewHolder implements
View.OnClickListener,
View.OnLongClickListener,
View.OnLayoutChangeListener {
private ViewCardOptional card;
private View view;
private View header;
private View vwColor;
private ImageButton ibExpander;
private ImageView ibFlagged;
private ImageButton ibAvatar;
private ImageButton ibVerified;
private ImageButton ibAuth;
private ImageButton ibPriority;
private ImageButton ibSensitivity;
private ImageView ivImportance;
private ImageButton ibSigned;
private ImageButton ibEncrypted;
private TextView tvFrom;
private TextView tvSize;
private TextView tvTime;
private ImageView ivType;
private ImageView ivFound;
private ImageView ivClassified;
private ImageButton ibSnoozed;
private ImageView ivAnswered;
private ImageView ivForwarded;
private ImageView ivAttachments;
private TextView tvSubject;
private TextView tvKeywords;
private TextView tvFolder;
private TextView tvLabels;
private TextView tvCount;
private ImageView ivThread;
private TextView tvExpand;
private TextView tvPreview;
private TextView tvNotes;
private TextView tvError;
private ImageButton ibError;
private View vsBody;
private ImageButton ibExpanderAddress;
private ImageView ivPlain;
private ImageButton ibReceipt;
private ImageView ivAutoSubmitted;
private ImageView ivBrowsed;
private ImageView ivRaw;
private ImageButton ibSearchContact;
private ImageButton ibNotifyContact;
private ImageButton ibPinContact;
private ImageButton ibAddContact;
private TextView tvSubmitterTitle;
private TextView tvDeliveredToTitle;
private TextView tvFromExTitle;
private TextView tvToTitle;
private TextView tvReplyToTitle;
private TextView tvCcTitle;
private TextView tvBccTitle;
private TextView tvIdentityTitle;
private TextView tvSentTitle;
private TextView tvReceivedTitle;
private TextView tvStoredTitle;
private TextView tvSizeExTitle;
private TextView tvLanguageTitle;
private TextView tvThreadTitle;
private TextView tvSubmitter;
private TextView tvDeliveredTo;
private TextView tvFromEx;
private TextView tvTo;
private TextView tvReplyTo;
private TextView tvCc;
private TextView tvBcc;
private TextView tvIdentity;
private TextView tvSent;
private TextView tvReceived;
private TextView tvStored;
private TextView tvSizeEx;
private TextView tvLanguage;
private TextView tvThread;
private TextView tvSubjectEx;
private TextView tvFlags;
private TextView tvKeywordsEx;
private TextView tvHeaders;
private ImageButton ibCopyHeaders;
private ImageButton ibCloseHeaders;
private ContentLoadingProgressBar pbHeaders;
private TextView tvNoInternetHeaders;
private RecyclerView rvAttachment;
private CheckBox cbInline;
private ImageButton ibSaveAttachments;
private ImageButton ibDownloadAttachments;
private TextView tvNoInternetAttachments;
private View vSeparator;
private ImageButton ibFull;
private ImageButton ibImages;
private ImageButton ibAmp;
private ImageButton ibDecrypt;
private ImageButton ibVerify;
private ImageButton ibUndo;
private ImageButton ibRule;
private ImageButton ibUnsubscribe;
private ImageButton ibHeaders;
private ImageButton ibPrint;
private ImageButton ibPin;
private ImageButton ibShare;
private ImageButton ibEvent;
private ImageButton ibSearchText;
private ImageButton ibSearch;
private ImageButton ibTranslate;
private ImageButton ibForceLight;
private ImageButton ibImportance;
private ImageButton ibHide;
private ImageButton ibSeen;
private ImageButton ibAnswer;
private ImageButton ibNotes;
private ImageButton ibLabels;
private ImageButton ibKeywords;
private ImageButton ibCopy;
private ImageButton ibMove;
private ImageButton ibArchive;
private ImageButton ibTrash;
private ImageButton ibJunk;
private ImageButton ibInbox;
private ImageButton ibMore;
private ImageButton ibTools;
private View vwEmpty;
private TextView tvReformatted;
private TextView tvSignedData;
private TextView tvBody;
private View wvBody;
private ContentLoadingProgressBar pbBody;
private View vwRipple;
private TextView tvNoInternetBody;
private ImageButton ibDownloading;
private Group grpDownloading;
private ImageButton ibInfrastructure;
private ImageButton ibTrashBottom;
private ImageButton ibArchiveBottom;
private ImageButton ibMoveBottom;
private ImageButton ibSeenBottom;
private Flow flow;
private ImageButton ibCalendar;
private TextView tvCalendarSummary;
private TextView tvCalendarDescription;
private TextView tvCalendarLocation;
private TextView tvCalendarStart;
private TextView tvCalendarEnd;
private TextView tvAttendees;
private Button btnCalendarAccept;
private Button btnCalendarDecline;
private Button btnCalendarMaybe;
private ContentLoadingProgressBar pbCalendarWait;
private ImageButton ibStoreMedia;
private ImageButton ibShareImages;
private RecyclerView rvImage;
private Group grpAddresses;
private Group grpHeaders;
private Group grpAction;
private Group grpCalendar;
private Group grpCalendarResponse;
private Group grpAttachments;
private Group grpImages;
private AdapterAttachment adapterAttachment;
private AdapterImage adapterImage;
private TwoStateOwner cowner = new TwoStateOwner(owner, "MessageAttachments");
private TwoStateOwner powner = new TwoStateOwner(owner, "MessagePopup");
private View.OnTouchListener touchListener = new View.OnTouchListener() {
private ScaleGestureDetector gestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
private Toast toast = null;
@Override
public boolean onScale(ScaleGestureDetector detector) {
TupleMessageEx message = getMessage();
if (message != null) {
// Scale factor
float factor = detector.getScaleFactor();
float size = tvBody.getTextSize() * factor;
float scale = (textSize == 0 ? 1.0f : size / (textSize * message_zoom / 100f));
if (scale > 10)
return true;
// Text size
properties.setSize(message.id, size);
tvBody.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
// Image size
Spanned spanned = (Spanned) tvBody.getText();
for (ImageSpan img : spanned.getSpans(0, spanned.length(), ImageSpan.class)) {
Drawable d = img.getDrawable();
int w = 0;
int h = 0;
if (img instanceof ImageSpanEx) {
w = ((ImageSpanEx) img).getWidth();
h = ((ImageSpanEx) img).getHeight();
}
ImageHelper.fitDrawable(d, w, h, scale, tvBody);
}
// Feedback
String perc = Math.round(scale * 100) + " %";
if (toast != null)
toast.cancel();
toast = ToastEx.makeText(context, perc, Toast.LENGTH_SHORT);
toast.show();
}
return true;
}
});
@Override
public boolean onTouch(View view, MotionEvent ev) {
if (ev.getPointerCount() > 1) {
view.getParent().requestDisallowInterceptTouchEvent(true);
if (view.getId() == R.id.tvBody) {
gestureDetector.onTouchEvent(ev);
return true;
} else
return false;
} else {
//view.getParent().requestDisallowInterceptTouchEvent(false);
//return (view.getId() == R.id.wvBody && ev.getAction() == MotionEvent.ACTION_MOVE);
boolean intercept = (view.getId() == R.id.wvBody && ((WebViewEx) wvBody).isZoomedY());
view.getParent().requestDisallowInterceptTouchEvent(intercept);
return false;
}
}
};
private MovementMethod movementMethod = new ArrowKeyMovementMethod() {
private GestureDetector gestureDetector = new GestureDetector(context,
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent event) {
return onClick(event);
}
private boolean onClick(MotionEvent event) {
TextView widget = tvBody;
Spannable buffer = (Spannable) tvBody.getText();
int off = Helper.getOffset(widget, buffer, event);
TupleMessageEx message = getMessage();
if (message == null)
return false;
boolean show_images = properties.getValue("images", message.id);
if (!show_images) {
ImageSpan[] image = buffer.getSpans(off, off, ImageSpan.class);
if (image.length > 0 && image[0].getSource() != null) {
Uri uri = Uri.parse(image[0].getSource());
if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
ripple(event);
if (onOpenLink(uri, null, false))
return true;
}
}
}
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
if (link.length > 0) {
String url = link[0].getURL();
Uri uri = Uri.parse(url);
int start = buffer.getSpanStart(link[0]);
int end = buffer.getSpanEnd(link[0]);
String title = (start < 0 || end < 0 || end <= start
? null : buffer.subSequence(start, end).toString());
if (url.equals(title))
title = null;
ripple(event);
if (onOpenLink(uri, title, false))
return true;
}
ImageSpan[] image = buffer.getSpans(off, off, ImageSpan.class);
if (image.length > 0) {
if (image[0] instanceof ImageSpanEx &&
((ImageSpanEx) image[0]).getTracking())
return true;
ripple(event);
onOpenImage(message.id, image[0].getSource());
return true;
}
DynamicDrawableSpan[] ddss = buffer.getSpans(off, off, DynamicDrawableSpan.class);
if (ddss.length > 0) {
properties.setValue("quotes", message.id, true);
bindBody(message, false);
return true;
}
return false;
}
private void ripple(MotionEvent event) {
int r = context.getResources().getDimensionPixelSize(R.dimen.ripple_radius);
vwRipple.setLeft(tvBody.getLeft() + Math.round(event.getX()) - r);
vwRipple.setTop(tvBody.getTop() + Math.round(event.getY()) - r);
vwRipple.setRight(tvBody.getLeft() + Math.round(event.getX()) + r);
vwRipple.setBottom(tvBody.getTop() + Math.round(event.getY()) + r);
vwRipple.setClickable(true);
vwRipple.setPressed(true);
vwRipple.setPressed(false);
vwRipple.setClickable(false);
}
});
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
};
private SimpleTask taskContactInfo;
ViewHolder(final View itemView, long viewType) {
super(itemView);
card = itemView.findViewById(R.id.card);
view = itemView.findViewById(R.id.clItem);
header = itemView.findViewById(R.id.inHeader);
vwColor = itemView.findViewById(R.id.vwColor);
ibExpander = itemView.findViewById(R.id.ibExpander);
ibFlagged = itemView.findViewById(R.id.ibFlagged);
ibAvatar = itemView.findViewById(R.id.ibAvatar);
ibVerified = itemView.findViewById(R.id.ibVerified);
ibAuth = itemView.findViewById(R.id.ibAuth);
ibPriority = itemView.findViewById(R.id.ibPriority);
ibSensitivity = itemView.findViewById(R.id.ibSensitivity);
ivImportance = itemView.findViewById(R.id.ivImportance);
ibSigned = itemView.findViewById(R.id.ibSigned);
ibEncrypted = itemView.findViewById(R.id.ibEncrypted);
tvFrom = itemView.findViewById(subject_top ? R.id.tvSubject : R.id.tvFrom);
tvSize = itemView.findViewById(R.id.tvSize);
tvTime = itemView.findViewById(R.id.tvTime);
ivType = itemView.findViewById(R.id.ivType);
ivFound = itemView.findViewById(R.id.ivFound);
ivClassified = itemView.findViewById(R.id.ivClassified);
ibSnoozed = itemView.findViewById(R.id.ibSnoozed);
ivAnswered = itemView.findViewById(R.id.ivAnswered);
ivForwarded = itemView.findViewById(R.id.ivForwarded);
ivAttachments = itemView.findViewById(R.id.ivAttachments);
tvSubject = itemView.findViewById(subject_top ? R.id.tvFrom : R.id.tvSubject);
tvKeywords = itemView.findViewById(R.id.tvKeywords);
tvExpand = itemView.findViewById(R.id.tvExpand);
tvPreview = itemView.findViewById(R.id.tvPreview);
tvNotes = itemView.findViewById(R.id.tvNotes);
tvFolder = itemView.findViewById(R.id.tvFolder);
tvLabels = itemView.findViewById(R.id.tvLabels);
tvCount = itemView.findViewById(R.id.tvCount);
ivThread = itemView.findViewById(R.id.ivThread);
tvError = itemView.findViewById(R.id.tvError);
if (tvError != null)
tvError.setAutoLinkMask(Linkify.EMAIL_ADDRESSES | Linkify.WEB_URLS);
ibError = itemView.findViewById(R.id.ibError);
if (vwColor != null)
vwColor.getLayoutParams().width = colorStripeWidth;
if (tvFrom != null) {
if (compact) {
boolean full = "full".equals(sender_ellipsize);
tvFrom.setSingleLine(!full);
if ("start".equals(sender_ellipsize))
tvFrom.setEllipsize(TextUtils.TruncateAt.START);
else if ("end".equals(sender_ellipsize))
tvFrom.setEllipsize(TextUtils.TruncateAt.END);
else if ("middle".equals(sender_ellipsize))
tvFrom.setEllipsize(TextUtils.TruncateAt.MIDDLE);
else
tvFrom.setEllipsize(null);
}
}
if (tvSubject != null) {
tvSubject.setTextColor(colorSubject);
if (compact) {
boolean full = "full".equals(subject_ellipsize);
tvSubject.setSingleLine(!full);
if ("start".equals(subject_ellipsize))
tvSubject.setEllipsize(TextUtils.TruncateAt.START);
else if ("end".equals(subject_ellipsize))
tvSubject.setEllipsize(TextUtils.TruncateAt.END);
else if ("middle".equals(subject_ellipsize))
tvSubject.setEllipsize(TextUtils.TruncateAt.MIDDLE);
else
tvSubject.setEllipsize(null);
}
}
if (tvKeywords != null) {
if (compact)
tvKeywords.setSingleLine(true);
}
if (viewType != R.layout.item_message_compact && viewType != R.layout.item_message_normal)
return;
if (!BuildConfig.DEBUG && !accessibility)
return;
}
private void ensureExpanded() {
if (vsBody != null)
return;
vsBody = ((ViewStub) itemView.findViewById(R.id.vsBody)).inflate();
ConstraintLayout inAttachments = vsBody.findViewById(R.id.inAttachments);
ConstraintLayout inAttachmentsAlt = vsBody.findViewById(R.id.inAttachmentsAlt);
inAttachments.setVisibility(attachments_alt ? View.GONE : View.VISIBLE);
inAttachmentsAlt.setVisibility(attachments_alt ? View.VISIBLE : View.GONE);
ConstraintLayout attachments = (attachments_alt ? inAttachmentsAlt : inAttachments);
ibExpanderAddress = vsBody.findViewById(R.id.ibExpanderAddress);
ivPlain = vsBody.findViewById(R.id.ivPlain);
ibReceipt = vsBody.findViewById(R.id.ibReceipt);
ivAutoSubmitted = vsBody.findViewById(R.id.ivAutoSubmitted);
ivBrowsed = vsBody.findViewById(R.id.ivBrowsed);
ivRaw = vsBody.findViewById(R.id.ivRaw);
ibSearchContact = vsBody.findViewById(R.id.ibSearchContact);
ibNotifyContact = vsBody.findViewById(R.id.ibNotifyContact);
ibPinContact = vsBody.findViewById(R.id.ibPinContact);
ibAddContact = vsBody.findViewById(R.id.ibAddContact);
tvSubmitterTitle = vsBody.findViewById(R.id.tvSubmitterTitle);
tvDeliveredToTitle = vsBody.findViewById(R.id.tvDeliveredToTitle);
tvFromExTitle = vsBody.findViewById(R.id.tvFromExTitle);
tvToTitle = vsBody.findViewById(R.id.tvToTitle);
tvReplyToTitle = vsBody.findViewById(R.id.tvReplyToTitle);
tvCcTitle = vsBody.findViewById(R.id.tvCcTitle);
tvBccTitle = vsBody.findViewById(R.id.tvBccTitle);
tvIdentityTitle = vsBody.findViewById(R.id.tvIdentityTitle);
tvSentTitle = vsBody.findViewById(R.id.tvSentTitle);
tvReceivedTitle = vsBody.findViewById(R.id.tvReceivedTitle);
tvStoredTitle = vsBody.findViewById(R.id.tvStoredTitle);
tvSizeExTitle = vsBody.findViewById(R.id.tvSizeExTitle);
tvLanguageTitle = vsBody.findViewById(R.id.tvLanguageTitle);
tvThreadTitle = vsBody.findViewById(R.id.tvThreadTitle);
tvSubmitter = vsBody.findViewById(R.id.tvSubmitter);
tvDeliveredTo = vsBody.findViewById(R.id.tvDeliveredTo);
tvFromEx = vsBody.findViewById(R.id.tvFromEx);
tvTo = vsBody.findViewById(R.id.tvTo);
tvReplyTo = vsBody.findViewById(R.id.tvReplyTo);
tvCc = vsBody.findViewById(R.id.tvCc);
tvBcc = vsBody.findViewById(R.id.tvBcc);
tvIdentity = vsBody.findViewById(R.id.tvIdentity);
tvSent = vsBody.findViewById(R.id.tvSent);
tvReceived = vsBody.findViewById(R.id.tvReceived);
tvStored = vsBody.findViewById(R.id.tvStored);
tvSizeEx = vsBody.findViewById(R.id.tvSizeEx);
tvLanguage = vsBody.findViewById(R.id.tvLanguage);
tvThread = vsBody.findViewById(R.id.tvThread);
tvSubjectEx = vsBody.findViewById(R.id.tvSubjectEx);
tvFlags = vsBody.findViewById(R.id.tvFlags);
tvKeywordsEx = vsBody.findViewById(R.id.tvKeywordsEx);
tvHeaders = vsBody.findViewById(R.id.tvHeaders);
tvHeaders.setMovementMethod(LinkMovementMethod.getInstance());
ibCopyHeaders = vsBody.findViewById(R.id.ibCopyHeaders);
ibCloseHeaders = vsBody.findViewById(R.id.ibCloseHeaders);
pbHeaders = vsBody.findViewById(R.id.pbHeaders);
tvNoInternetHeaders = vsBody.findViewById(R.id.tvNoInternetHeaders);
ibCalendar = vsBody.findViewById(R.id.ibCalendar);
tvCalendarSummary = vsBody.findViewById(R.id.tvCalendarSummary);
tvCalendarDescription = vsBody.findViewById(R.id.tvCalendarDescription);
tvCalendarLocation = vsBody.findViewById(R.id.tvCalendarLocation);
tvCalendarStart = vsBody.findViewById(R.id.tvCalendarStart);
tvCalendarEnd = vsBody.findViewById(R.id.tvCalendarEnd);
tvAttendees = vsBody.findViewById(R.id.tvAttendees);
btnCalendarAccept = vsBody.findViewById(R.id.btnCalendarAccept);
btnCalendarDecline = vsBody.findViewById(R.id.btnCalendarDecline);
btnCalendarMaybe = vsBody.findViewById(R.id.btnCalendarMaybe);
pbCalendarWait = vsBody.findViewById(R.id.pbCalendarWait);
rvAttachment = attachments.findViewById(R.id.rvAttachment);
rvAttachment.setHasFixedSize(false);
LinearLayoutManager llm = new LinearLayoutManager(context);
rvAttachment.setLayoutManager(llm);
rvAttachment.setItemAnimator(null);
adapterAttachment = new AdapterAttachment(parentFragment, true);
rvAttachment.setAdapter(adapterAttachment);
cbInline = attachments.findViewById(R.id.cbInline);
ibSaveAttachments = attachments.findViewById(R.id.ibSaveAttachments);
ibDownloadAttachments = attachments.findViewById(R.id.ibDownloadAttachments);
tvNoInternetAttachments = attachments.findViewById(R.id.tvNoInternetAttachments);
vSeparator = vsBody.findViewById(R.id.vSeparator);
ibFull = vsBody.findViewById(R.id.ibFull);
ibImages = vsBody.findViewById(R.id.ibImages);
ibAmp = vsBody.findViewById(R.id.ibAmp);
ibDecrypt = vsBody.findViewById(R.id.ibDecrypt);
ibVerify = vsBody.findViewById(R.id.ibVerify);
ibUndo = vsBody.findViewById(R.id.ibUndo);
ibRule = vsBody.findViewById(R.id.ibRule);
ibUnsubscribe = vsBody.findViewById(R.id.ibUnsubscribe);
ibHeaders = vsBody.findViewById(R.id.ibHeaders);
ibPrint = vsBody.findViewById(R.id.ibPrint);
ibPin = vsBody.findViewById(R.id.ibPin);
ibShare = vsBody.findViewById(R.id.ibShare);
ibEvent = vsBody.findViewById(R.id.ibEvent);
ibSearchText = vsBody.findViewById(R.id.ibSearchText);
ibSearch = vsBody.findViewById(R.id.ibSearch);
ibTranslate = vsBody.findViewById(R.id.ibTranslate);
ibForceLight = vsBody.findViewById(R.id.ibForceLight);
ibImportance = vsBody.findViewById(R.id.ibImportance);
ibHide = vsBody.findViewById(R.id.ibHide);
ibSeen = vsBody.findViewById(R.id.ibSeen);
ibAnswer = vsBody.findViewById(R.id.ibAnswer);
ibNotes = vsBody.findViewById(R.id.ibNotes);
ibLabels = vsBody.findViewById(R.id.ibLabels);
ibKeywords = vsBody.findViewById(R.id.ibKeywords);
ibCopy = vsBody.findViewById(R.id.ibCopy);
ibMove = vsBody.findViewById(R.id.ibMove);
ibArchive = vsBody.findViewById(R.id.ibArchive);
ibTrash = vsBody.findViewById(R.id.ibTrash);
ibJunk = vsBody.findViewById(R.id.ibJunk);
ibInbox = vsBody.findViewById(R.id.ibInbox);
ibMore = vsBody.findViewById(R.id.ibMore);
ibTools = vsBody.findViewById(R.id.ibTools);
vwEmpty = vsBody.findViewById(R.id.vwEmpty);
tvReformatted = vsBody.findViewById(R.id.tvReformatted);
tvSignedData = vsBody.findViewById(R.id.tvSignedData);
tvBody = vsBody.findViewById(R.id.tvBody);
if (BuildConfig.DEBUG && BuildCompat.isAtLeastT()) {
tvBody.setHyphenationFrequency(4 /* HYPHENATION_FREQUENCY_FULL_FAST */);
tvBody.setBreakStrategy(LineBreaker.BREAK_STRATEGY_HIGH_QUALITY);
}
wvBody = vsBody.findViewById(R.id.wvBody);
pbBody = vsBody.findViewById(R.id.pbBody);
vwRipple = vsBody.findViewById(R.id.vwRipple);
tvNoInternetBody = vsBody.findViewById(R.id.tvNoInternetBody);
ibDownloading = vsBody.findViewById(R.id.ibDownloading);
grpDownloading = vsBody.findViewById(R.id.grpDownloading);
ibInfrastructure = vsBody.findViewById(R.id.ibInfrastructure);
ibTrashBottom = vsBody.findViewById(R.id.ibTrashBottom);
ibArchiveBottom = vsBody.findViewById(R.id.ibArchiveBottom);
ibMoveBottom = vsBody.findViewById(R.id.ibMoveBottom);
ibSeenBottom = vsBody.findViewById(R.id.ibSeenBottom);
flow = vsBody.findViewById(R.id.flow);
ibStoreMedia = vsBody.findViewById(R.id.ibStoreMedia);
ibShareImages = vsBody.findViewById(R.id.ibShareImages);
rvImage = vsBody.findViewById(R.id.rvImage);
rvImage.setHasFixedSize(false);
StaggeredGridLayoutManager sglm =
new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
rvImage.setLayoutManager(sglm);
adapterImage = new AdapterImage(parentFragment);
rvImage.setAdapter(adapterImage);
grpAddresses = vsBody.findViewById(R.id.grpAddresses);
grpHeaders = vsBody.findViewById(R.id.grpHeaders);
grpAction = vsBody.findViewById(R.id.grpAction);
grpCalendar = vsBody.findViewById(R.id.grpCalendar);
grpCalendarResponse = vsBody.findViewById(R.id.grpCalendarResponse);
grpAttachments = attachments.findViewById(R.id.grpAttachments);
grpImages = vsBody.findViewById(R.id.grpImages);
unwire();
wire();
}
Rect getItemRect() {
return new Rect(
super.itemView.getLeft(),
super.itemView.getTop(),
super.itemView.getRight(),
super.itemView.getBottom());
}
private void wire() {
final View touch = (viewType == ViewType.THREAD ? ibExpander : header);
touch.setOnClickListener(this);
if (touch == ibExpander)
header.post(new Runnable() {
@Override
public void run() {
Rect rect = new Rect(
header.getLeft(),
header.getTop(),
header.getRight(),
header.getBottom());
header.setTouchDelegate(new TouchDelegate(rect, touch));
}
});
ibAvatar.setOnClickListener(this);
ibVerified.setOnClickListener(this);
ibAuth.setOnClickListener(this);
ibPriority.setOnClickListener(this);
ibSensitivity.setOnClickListener(this);
ibSigned.setOnClickListener(this);
ibEncrypted.setOnClickListener(this);
ibSnoozed.setOnClickListener(this);
ibFlagged.setOnClickListener(this);
if (viewType == ViewType.THREAD) {
ibFlagged.setOnLongClickListener(this);
tvFolder.setOnLongClickListener(this);
}
tvError.setOnClickListener(this);
ibError.setOnClickListener(this);
if (vsBody != null) {
ibExpanderAddress.setOnClickListener(this);
ibReceipt.setOnClickListener(this);
ibSearchContact.setOnClickListener(this);
ibNotifyContact.setOnClickListener(this);
ibPinContact.setOnClickListener(this);
ibAddContact.setOnClickListener(this);
if (BuildConfig.DEBUG) {
ibPinContact.setOnLongClickListener(this);
ibAddContact.setOnLongClickListener(this);
}
ibCopyHeaders.setOnClickListener(this);
ibCloseHeaders.setOnClickListener(this);
ibSaveAttachments.setOnClickListener(this);
ibDownloadAttachments.setOnClickListener(this);
ibFull.setOnClickListener(this);
ibFull.setOnLongClickListener(this);
ibImages.setOnClickListener(this);
ibAmp.setOnClickListener(this);
ibDecrypt.setOnClickListener(this);
ibVerify.setOnClickListener(this);
ibUndo.setOnClickListener(this);
ibRule.setOnClickListener(this);
ibUnsubscribe.setOnClickListener(this);
ibHeaders.setOnClickListener(this);
ibHeaders.setOnLongClickListener(this);
ibPrint.setOnClickListener(this);
ibPin.setOnClickListener(this);
ibShare.setOnClickListener(this);
ibEvent.setOnClickListener(this);
ibSearchText.setOnClickListener(this);
ibSearch.setOnClickListener(this);
ibTranslate.setOnClickListener(this);
ibTranslate.setOnLongClickListener(this);
ibForceLight.setOnClickListener(this);
ibImportance.setOnClickListener(this);
ibImportance.setOnLongClickListener(this);
ibHide.setOnClickListener(this);
ibSeen.setOnClickListener(this);
ibAnswer.setOnClickListener(this);
ibNotes.setOnClickListener(this);
ibNotes.setOnLongClickListener(this);
ibLabels.setOnClickListener(this);
ibKeywords.setOnClickListener(this);
ibCopy.setOnClickListener(this);
ibMove.setOnClickListener(this);
ibMove.setOnLongClickListener(this);
ibArchive.setOnClickListener(this);
ibTrash.setOnClickListener(this);
ibTrash.setOnLongClickListener(this);
ibJunk.setOnClickListener(this);
ibInbox.setOnClickListener(this);
ibMore.setOnClickListener(this);
ibTools.setOnClickListener(this);
ibDownloading.setOnClickListener(this);
ibInfrastructure.setOnClickListener(this);
ibTrashBottom.setOnClickListener(this);
ibTrashBottom.setOnLongClickListener(this);
ibArchiveBottom.setOnClickListener(this);
ibMoveBottom.setOnClickListener(this);
ibMoveBottom.setOnLongClickListener(this);
ibSeenBottom.setOnClickListener(this);
tvBody.setOnTouchListener(touchListener);
tvBody.setTextIsSelectable(false);
tvBody.setTextIsSelectable(true);
tvBody.setMovementMethod(movementMethod);
tvBody.addOnLayoutChangeListener(this);
ibCalendar.setOnClickListener(this);
btnCalendarAccept.setOnClickListener(this);
btnCalendarDecline.setOnClickListener(this);
btnCalendarMaybe.setOnClickListener(this);
btnCalendarAccept.setOnLongClickListener(this);
btnCalendarDecline.setOnLongClickListener(this);
btnCalendarMaybe.setOnLongClickListener(this);
ibStoreMedia.setOnClickListener(this);
ibShareImages.setOnClickListener(this);
}
if (accessibility) {
view.setAccessibilityDelegate(accessibilityDelegateHeader);
header.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
}
}
private void unwire() {
final View touch = (viewType == ViewType.THREAD ? ibExpander : header);
touch.setOnClickListener(null);
ibAvatar.setOnClickListener(null);
ibVerified.setOnClickListener(null);
ibAuth.setOnClickListener(null);
ibPriority.setOnClickListener(null);
ibSensitivity.setOnClickListener(null);
ibSigned.setOnClickListener(null);
ibEncrypted.setOnClickListener(null);
ibSnoozed.setOnClickListener(null);
ibFlagged.setOnClickListener(null);
if (viewType == ViewType.THREAD) {
ibFlagged.setOnLongClickListener(null);
tvFolder.setOnLongClickListener(null);
}
tvError.setOnClickListener(null);
ibError.setOnClickListener(null);
if (vsBody != null) {
ibExpanderAddress.setOnClickListener(null);
ibReceipt.setOnLongClickListener(null);
ibSearchContact.setOnClickListener(null);
ibNotifyContact.setOnClickListener(null);
ibPinContact.setOnClickListener(null);
ibAddContact.setOnClickListener(null);
if (BuildConfig.DEBUG) {
ibPinContact.setOnLongClickListener(null);
ibAddContact.setOnLongClickListener(null);
}
ibCopyHeaders.setOnClickListener(null);
ibCloseHeaders.setOnClickListener(null);
ibSaveAttachments.setOnClickListener(null);
ibDownloadAttachments.setOnClickListener(null);
ibFull.setOnClickListener(null);
ibFull.setOnLongClickListener(null);
ibImages.setOnClickListener(null);
ibAmp.setOnClickListener(null);
ibDecrypt.setOnClickListener(null);
ibVerify.setOnClickListener(null);
ibUndo.setOnClickListener(null);
ibRule.setOnClickListener(null);
ibUnsubscribe.setOnClickListener(null);
ibHeaders.setOnClickListener(null);
ibHeaders.setOnLongClickListener(null);
ibPrint.setOnClickListener(null);
ibPin.setOnClickListener(null);
ibShare.setOnClickListener(null);
ibEvent.setOnClickListener(null);
ibSearchText.setOnClickListener(null);
ibSearch.setOnClickListener(null);
ibTranslate.setOnClickListener(null);
ibTranslate.setOnLongClickListener(null);
ibForceLight.setOnClickListener(null);
ibImportance.setOnClickListener(null);
ibImportance.setOnLongClickListener(null);
ibHide.setOnClickListener(null);
ibSeen.setOnClickListener(null);
ibAnswer.setOnClickListener(null);
ibNotes.setOnClickListener(null);
ibNotes.setOnLongClickListener(null);
ibLabels.setOnClickListener(null);
ibKeywords.setOnClickListener(null);
ibCopy.setOnClickListener(null);
ibMove.setOnClickListener(null);
ibMove.setOnClickListener(null);
ibArchive.setOnClickListener(null);
ibTrash.setOnClickListener(null);
ibTrash.setOnLongClickListener(null);
ibJunk.setOnClickListener(null);
ibInbox.setOnClickListener(null);
ibMore.setOnClickListener(null);
ibTools.setOnClickListener(null);
ibDownloading.setOnClickListener(null);
ibInfrastructure.setOnClickListener(null);
ibTrashBottom.setOnClickListener(null);
ibTrashBottom.setOnLongClickListener(null);
ibArchiveBottom.setOnClickListener(null);
ibMoveBottom.setOnClickListener(null);
ibMoveBottom.setOnLongClickListener(null);
ibSeenBottom.setOnClickListener(null);
tvBody.setOnTouchListener(null);
tvBody.setMovementMethod(null);
tvBody.removeOnLayoutChangeListener(this);
btnCalendarAccept.setOnClickListener(null);
btnCalendarDecline.setOnClickListener(null);
btnCalendarMaybe.setOnClickListener(null);
ibCalendar.setOnClickListener(null);
btnCalendarAccept.setOnLongClickListener(null);
btnCalendarDecline.setOnLongClickListener(null);
btnCalendarMaybe.setOnLongClickListener(null);
ibStoreMedia.setOnClickListener(null);
ibShareImages.setOnClickListener(null);
}
if (accessibility)
view.setAccessibilityDelegate(null);
}
@SuppressLint("WrongConstant")
private void bindTo(final TupleMessageEx message, boolean scroll) {
boolean inbox = EntityFolder.INBOX.equals(message.folderType);
boolean outbox = EntityFolder.OUTBOX.equals(message.folderType);
boolean outgoing = isOutgoing(message);
boolean reverse = (outgoing && viewType != ViewType.THREAD &&
(EntityFolder.isOutgoing(type) || viewType == ViewType.SEARCH) ||
(viewType == ViewType.UNIFIED && type == null &&
message.folderUnified && EntityFolder.isOutgoing(message.folderType)));
String selector = (reverse ? null : message.bimi_selector);
Address[] addresses = (reverse ? message.to : message.from);
Address[] senders = ContactInfo.fillIn(
reverse && !show_recipients ? message.to : message.senders, prefer_contact, only_contact);
Address[] recipients = ContactInfo.fillIn(
reverse && !show_recipients ? message.from : message.recipients, prefer_contact, only_contact);
boolean authenticated =
!((Boolean.FALSE.equals(message.dkim) && check_authentication) ||
(Boolean.FALSE.equals(message.spf) && check_authentication) ||
(Boolean.FALSE.equals(message.dmarc) && check_authentication) ||
(Boolean.FALSE.equals(message.reply_domain) && check_reply_domain) ||
(Boolean.FALSE.equals(message.mx) && check_mx) ||
(Boolean.TRUE.equals(message.blocklist) && check_blocklist));
boolean expanded = (viewType == ViewType.THREAD && properties.getValue("expanded", message.id));
// Text size
if (textSize != 0) {
// 14, 18, 22 sp
//tvKeywords.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);
tvPreview.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
tvNotes.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
}
// Selected / disabled
view.setActivated(selectionTracker != null && selectionTracker.isSelected(message.id));
view.setAlpha(
(EntityFolder.OUTBOX.equals(message.folderType)
? message.identitySynchronize == null || !message.identitySynchronize
: message.accountProtocol == EntityAccount.TYPE_IMAP && (message.uid == null || message.ui_deleted))
? Helper.LOW_LIGHT : 1.0f);
// Duplicate
if (viewType == ViewType.THREAD) {
boolean dim = (message.duplicate || EntityFolder.TRASH.equals(message.folderType));
ibFlagged.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibAvatar.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibVerified.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibAuth.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibPriority.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibSensitivity.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivImportance.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibSigned.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibEncrypted.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvFrom.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvSize.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvTime.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivType.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivFound.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivClassified.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibSnoozed.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivAnswered.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivForwarded.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivAttachments.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvSubject.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvKeywords.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvFolder.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvLabels.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvCount.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivThread.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvPreview.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvNotes.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvError.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibError.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
}
bindSeen(message);
// Account color
int colorBackground =
(message.accountColor == null || !ActivityBilling.isPro(context)
? colorSeparator : message.accountColor);
if (!Objects.equals(vwColor.getTag() == null, colorBackground)) {
vwColor.setTag(colorBackground);
vwColor.setBackgroundColor(colorBackground);
}
vwColor.setVisibility(color_stripe ? View.VISIBLE : View.GONE);
// Expander
if (!Objects.equals(ibExpander.getTag(), expanded)) {
ibExpander.setTag(expanded);
ibExpander.setImageLevel(expanded ? 0 /* less */ : 1 /* more */);
}
if (viewType == ViewType.THREAD)
ibExpander.setVisibility(EntityFolder.DRAFTS.equals(message.folderType) ? View.INVISIBLE : View.VISIBLE);
else
ibExpander.setVisibility(View.GONE);
// Photo
ibAvatar.setVisibility(avatars ? View.INVISIBLE : View.GONE);
// Line 1
ibVerified.setVisibility(View.GONE);
if (authentication && !authenticated) {
ibAuth.setImageLevel(0);
ibAuth.setImageTintList(ColorStateList.valueOf(colorWarning));
ibAuth.setVisibility(View.VISIBLE);
} else if (authentication && authentication_indicator) {
int auths =
(Boolean.TRUE.equals(message.dkim) ? 1 : 0) +
(Boolean.TRUE.equals(message.spf) ? 1 : 0) +
(Boolean.TRUE.equals(message.dmarc) ? 1 : 0);
// https://en.wikipedia.org/wiki/DMARC#Alignment
if (Boolean.TRUE.equals(message.dkim) &&
!Boolean.FALSE.equals(message.spf) &&
!Boolean.FALSE.equals(message.dmarc))
auths = 3;
boolean verified = (auths == 3 && (!check_tls || Boolean.TRUE.equals(message.tls)));
if (message.dkim == null && message.spf == null && message.dkim == null)
ibAuth.setImageLevel(1);
else
ibAuth.setImageLevel(auths + 2);
ibAuth.setImageTintList(ColorStateList.valueOf(
verified ? colorVerified : colorControlNormal));
ibAuth.setVisibility(auths > 0 || (check_tls && !outgoing) ? View.VISIBLE : View.GONE);
} else
ibAuth.setVisibility(View.GONE);
if (EntityMessage.PRIORITIY_HIGH.equals(message.ui_priority)) {
ibPriority.setImageLevel(message.ui_priority);
ibPriority.setVisibility(View.VISIBLE);
} else if (EntityMessage.PRIORITIY_LOW.equals(message.ui_priority)) {
ibPriority.setImageLevel(message.ui_priority);
ibPriority.setVisibility(View.VISIBLE);
} else
ibPriority.setVisibility(View.GONE);
ibSensitivity.setImageLevel(message.sensitivity == null ? 0 : message.sensitivity);
ibSensitivity.setVisibility(message.sensitivity == null ? View.GONE : View.VISIBLE);
if (EntityMessage.PRIORITIY_HIGH.equals(message.ui_importance)) {
ivImportance.setImageLevel(message.ui_importance);
ivImportance.setVisibility(View.VISIBLE);
} else if (EntityMessage.PRIORITIY_LOW.equals(message.ui_importance)) {
ivImportance.setImageLevel(message.ui_importance);
ivImportance.setVisibility(View.VISIBLE);
} else
ivImportance.setVisibility(View.GONE);
if (!Objects.equals(ibSigned.getTag(), message.verified)) {
ibSigned.setTag(message.verified);
if (message.verified)
ibSigned.setColorFilter(colorEncrypt);
else
ibSigned.clearColorFilter();
}
ibSigned.setVisibility(message.isSigned() ? View.VISIBLE : View.GONE);
ibEncrypted.setVisibility(message.isEncrypted() ? View.VISIBLE : View.GONE);
MessageHelper.AddressFormat format = email_format;
if (show_recipients && recipients != null && recipients.length > 0) {
int maxRecipients = (viewType == ViewType.THREAD
? Integer.MAX_VALUE
: (compact ? MAX_RECIPIENTS_COMPACT : MAX_RECIPIENTS_NORMAL));
tvFrom.setText(context.getString(outgoing && viewType != ViewType.THREAD && compact
? R.string.title_to_from
: R.string.title_from_to,
formatAddresses(senders, format, maxRecipients),
formatAddresses(recipients, format, maxRecipients)));
} else
tvFrom.setText(MessageHelper.formatAddresses(senders, format, false));
tvFrom.setPaintFlags(tvFrom.getPaintFlags() & ~Paint.UNDERLINE_TEXT_FLAG);
tvSize.setText(message.totalSize == null ? null : Helper.humanReadableByteCount(message.totalSize));
tvSize.setVisibility(
message.totalSize != null && ("size".equals(sort) || "attachments".equals(sort))
? View.VISIBLE : View.GONE);
SpannableStringBuilder time = new SpannableStringBuilderEx(
date && FragmentMessages.SORT_DATE_HEADER.contains(sort)
? TF.format(message.received)
: Helper.getRelativeTimeSpanString(context, message.received));
if (show_recent && message.recent)
time.setSpan(new UnderlineSpan(), 0, time.length(), 0);
tvTime.setText(time);
// Line 2
tvSubject.setText(message.subject);
// Workaround layout bug
tvSubject.requestLayout();
tvSubject.invalidate();
if (keywords_header) {
Spanned keywords = getKeywords(message);
tvKeywords.setVisibility(keywords == null ? View.GONE : View.VISIBLE);
tvKeywords.setText(keywords);
} else
tvKeywords.setVisibility(View.GONE);
// Line 3
int icon = (message.drafts > 0
? R.drawable.twotone_edit_24
: EntityFolder.getIcon(outgoing ? EntityFolder.SENT : message.folderType));
ivType.setVisibility(message.drafts > 0 ||
(viewType == ViewType.UNIFIED && type == null && (!inbox || outgoing)) ||
(viewType == ViewType.FOLDER && outgoing && !EntityFolder.SENT.equals(message.folderType)) ||
(viewType == ViewType.THREAD && (outgoing || EntityFolder.SENT.equals(message.folderType))) ||
viewType == ViewType.SEARCH
? View.VISIBLE : View.GONE);
if (!Objects.equals(ivType.getTag(), icon)) {
ivType.setTag(icon);
ivType.setImageResource(icon);
}
ivFound.setVisibility(message.ui_found && found ? View.VISIBLE : View.GONE);
ivClassified.setVisibility(message.auto_classified ? View.VISIBLE : View.GONE);
int snoozy = (message.ui_snoozed != null && message.ui_snoozed == Long.MAX_VALUE
? R.drawable.twotone_visibility_off_24
: R.drawable.twotone_timelapse_24);
if (!Objects.equals(ibSnoozed.getTag(), snoozy)) {
ibSnoozed.setTag(snoozy);
ibSnoozed.setImageResource(snoozy);
}
if (message.ui_unsnoozed)
ibSnoozed.setColorFilter(colorAccent);
else
ibSnoozed.clearColorFilter();
ibSnoozed.setVisibility(message.ui_snoozed == null && !message.ui_unsnoozed ? View.GONE : View.VISIBLE);
ivAnswered.setVisibility(message.ui_answered ? View.VISIBLE : View.GONE);
ivForwarded.setVisibility(message.isForwarded() ? View.VISIBLE : View.GONE);
ivAttachments.setVisibility(message.totalAttachments > 0 ? View.VISIBLE : View.GONE);
if (viewType == ViewType.FOLDER)
tvFolder.setText(outbox ? message.identityEmail : message.accountName);
else if (viewType == ViewType.THREAD || viewType == ViewType.SEARCH)
tvFolder.setText(message.getFolderName(context));
else
tvFolder.setText(message.accountName + "/" + message.getFolderName(context));
tvFolder.setVisibility(compact && viewType != ViewType.THREAD ? View.GONE : View.VISIBLE);
Spanned labels = getLabels(message);
tvLabels.setText(labels);
tvLabels.setVisibility(labels == null ? View.GONE : View.VISIBLE);
boolean selected = properties.getValue("selected", message.id);
if (viewType == ViewType.THREAD || (!threading && !selected)) {
tvCount.setVisibility(View.GONE);
ivThread.setVisibility(View.GONE);
} else {
tvCount.setVisibility(threading && message.visible > 1 ? View.VISIBLE : View.GONE);
ivThread.setVisibility(selected || message.visible > 1 ? View.VISIBLE : View.GONE);
if (threading_unread)
tvCount.setText(context.getString(R.string.title_of,
NF.format(message.visible_unseen),
NF.format(message.visible)));
else
tvCount.setText(NF.format(message.visible));
if (selected)
ivThread.setColorFilter(colorAccent);
else
ivThread.clearColorFilter();
}
// Starred
bindFlagged(message, expanded);
// Expand warning
bindExpandWarning(message, expanded);
// Message text preview
int textColor = (contrast ? textColorPrimary : textColorSecondary);
if (!Objects.equals(tvPreview.getTag(), textColor)) {
tvPreview.setTag(textColor);
tvPreview.setTextColor(textColor);
if (preview_lines == 1)
tvPreview.setSingleLine(true);
else
tvPreview.setMaxLines(preview_lines);
}
tvPreview.setTypeface(
StyleHelper.getTypeface(display_font, context),
preview_italic ? Typeface.ITALIC : Typeface.NORMAL);
tvPreview.setText(message.preview);
tvPreview.setVisibility(preview && !TextUtils.isEmpty(message.preview) ? View.VISIBLE : View.GONE);
tvNotes.setText(message.notes);
tvNotes.setTextColor(message.notes_color == null ? textColorSecondary : message.notes_color);
tvNotes.setVisibility(TextUtils.isEmpty(message.notes) ? View.GONE : View.VISIBLE);
// Error / warning
String error = message.error;
if (message.warning != null)
if (error == null)
error = message.warning;
else
error += " " + message.warning;
if (debug) {
String text = context.getString(R.string.menu_setup) + "/" +
context.getString(R.string.title_advanced_debug) + "/" +
context.getString(R.string.title_advanced_section_misc) + " !!!" +
"\nerror=" + error +
"\nuid=" + message.uid + " id=" + message.id +
" fid=" + message.folder + " aid=" + message.account +
" " + DTF.format(new Date(message.received)) +
"\n" + (message.ui_hide ? "HIDDEN " : "") +
"seen=" + message.seen + "/" + message.ui_seen +
" unseen=" + message.unseen +
" ignored=" + message.ui_ignored +
" found=" + message.ui_found +
" busy=" + (message.ui_busy == null ? null : new Date(message.ui_busy)) +
"\nhash=" + message.hash +
"\nmsgid=" + message.msgid + "/" + message.uidl +
"\nthread=" + message.thread +
"\nsender=" + message.sender;
tvError.setText(text);
tvError.setVisibility(View.VISIBLE);
ibError.setVisibility(View.VISIBLE);
} else {
if (BuildConfig.DEBUG && level <= android.util.Log.INFO)
error = message.thread;
tvError.setText(error);
tvError.setVisibility(error == null ? View.GONE : View.VISIBLE);
ibError.setVisibility(error == null ? View.GONE : View.VISIBLE);
}
// Contact info
ContactInfo[] info = ContactInfo.getCached(context,
message.account, message.folderType, selector, addresses);
if (info == null) {
if (taskContactInfo != null) {
taskContactInfo.cancel(context);
taskContactInfo = null;
}
Bundle aargs = new Bundle();
aargs.putLong("id", message.id);
aargs.putLong("account", message.account);
aargs.putString("folderType", message.folderType);
aargs.putString("selector", selector);
aargs.putSerializable("addresses", addresses);
taskContactInfo = new SimpleTask() {
@Override
protected ContactInfo[] onExecute(Context context, Bundle args) {
long account = args.getLong("account");
String folderType = args.getString("folderType");
String selector = args.getString("selector");
Address[] addresses = (Address[]) args.getSerializable("addresses");
return ContactInfo.get(context, account, folderType, selector, addresses);
}
@Override
protected void onExecuted(Bundle args, ContactInfo[] info) {
taskContactInfo = null;
long id = args.getLong("id");
TupleMessageEx amessage = getMessage();
if (amessage == null || !amessage.id.equals(id))
return;
bindContactInfo(amessage, info, addresses);
}
@Override
protected void onDestroyed(Bundle args) {
taskContactInfo = null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Log.unexpectedError(parentFragment.getParentFragmentManager(), ex);
}
}.setLog(false);
taskContactInfo.execute(context, owner, aargs, "message:avatar");
} else
bindContactInfo(message, info, addresses);
if (viewType == ViewType.THREAD)
if (expanded)
bindExpanded(message, scroll);
else {
clearExpanded(message);
if (scroll)
properties.scrollTo(getAdapterPosition(), 0);
}
if (properties.getValue("raw_save", message.id)) {
properties.setValue("raw_save", message.id, false);
onMenuRawSave(message);
}
}
private void clearExpanded(TupleMessageEx message) {
if (compact) {
if ("full".equals(sender_ellipsize)
? tvFrom.getMaxLines() == 1
: tvFrom.getMaxLines() > 1)
tvFrom.setSingleLine(!"full".equals(sender_ellipsize));
if ("full".equals(subject_ellipsize)
? tvSubject.getMaxLines() == 1
: tvSubject.getMaxLines() > 1)
tvSubject.setSingleLine(!"full".equals(subject_ellipsize));
tvKeywords.setSingleLine(true);
}
tvPreview.setVisibility(
preview && message != null && !TextUtils.isEmpty(message.preview)
? View.VISIBLE : View.GONE);
if (vsBody == null)
return;
cowner.stop();
grpAddresses.setVisibility(View.GONE);
grpHeaders.setVisibility(View.GONE);
grpAction.setVisibility(View.GONE);
grpCalendar.setVisibility(View.GONE);
grpCalendarResponse.setVisibility(View.GONE);
grpAttachments.setVisibility(View.GONE);
grpImages.setVisibility(View.GONE);
ivPlain.setVisibility(View.GONE);
ibReceipt.setVisibility(View.GONE);
ivAutoSubmitted.setVisibility(View.GONE);
ivBrowsed.setVisibility(View.GONE);
ivRaw.setVisibility(View.GONE);
ibSearchContact.setVisibility(View.GONE);
ibNotifyContact.setVisibility(View.GONE);
ibPinContact.setVisibility(View.GONE);
ibAddContact.setVisibility(View.GONE);
tvSubmitterTitle.setVisibility(View.GONE);
tvDeliveredToTitle.setVisibility(View.GONE);
tvFromExTitle.setVisibility(View.GONE);
tvToTitle.setVisibility(View.GONE);
tvReplyToTitle.setVisibility(View.GONE);
tvCcTitle.setVisibility(View.GONE);
tvBccTitle.setVisibility(View.GONE);
tvIdentityTitle.setVisibility(View.GONE);
tvSentTitle.setVisibility(View.GONE);
tvReceivedTitle.setVisibility(View.GONE);
tvStoredTitle.setVisibility(View.GONE);
tvSizeExTitle.setVisibility(View.GONE);
tvLanguageTitle.setVisibility(View.GONE);
tvThreadTitle.setVisibility(View.GONE);
tvSubmitter.setVisibility(View.GONE);
tvDeliveredTo.setVisibility(View.GONE);
tvFromEx.setVisibility(View.GONE);
tvTo.setVisibility(View.GONE);
tvReplyTo.setVisibility(View.GONE);
tvCc.setVisibility(View.GONE);
tvBcc.setVisibility(View.GONE);
tvIdentity.setVisibility(View.GONE);
tvSent.setVisibility(View.GONE);
tvReceived.setVisibility(View.GONE);
tvStored.setVisibility(View.GONE);
tvSizeEx.setVisibility(View.GONE);
tvLanguage.setVisibility(View.GONE);
tvThread.setVisibility(View.GONE);
tvSubjectEx.setVisibility(View.GONE);
tvFlags.setVisibility(View.GONE);
tvKeywordsEx.setVisibility(View.GONE);
ibCopyHeaders.setVisibility(View.GONE);
pbHeaders.setVisibility(View.GONE);
tvNoInternetHeaders.setVisibility(View.GONE);
clearCalendar();
cbInline.setVisibility(View.GONE);
ibSaveAttachments.setVisibility(View.GONE);
ibDownloadAttachments.setVisibility(View.GONE);
tvNoInternetAttachments.setVisibility(View.GONE);
vSeparator.setVisibility(View.GONE);
ibFull.setVisibility(View.GONE);
ibImages.setVisibility(View.GONE);
ibAmp.setVisibility(View.GONE);
ibDecrypt.setVisibility(View.GONE);
ibVerify.setVisibility(View.GONE);
ibUndo.setVisibility(View.GONE);
ibRule.setVisibility(View.GONE);
ibUnsubscribe.setVisibility(View.GONE);
ibHeaders.setVisibility(View.GONE);
ibPrint.setVisibility(View.GONE);
ibPin.setVisibility(View.GONE);
ibShare.setVisibility(View.GONE);
ibEvent.setVisibility(View.GONE);
ibSearchText.setVisibility(View.GONE);
ibSearch.setVisibility(View.GONE);
ibTranslate.setVisibility(View.GONE);
ibForceLight.setVisibility(View.GONE);
ibImportance.setVisibility(View.GONE);
ibHide.setVisibility(View.GONE);
ibSeen.setVisibility(View.GONE);
ibAnswer.setVisibility(View.GONE);
ibNotes.setVisibility(View.GONE);
ibLabels.setVisibility(View.GONE);
ibKeywords.setVisibility(View.GONE);
ibCopy.setVisibility(View.GONE);
ibMove.setVisibility(View.GONE);
ibArchive.setVisibility(View.GONE);
ibTrash.setVisibility(View.GONE);
ibJunk.setVisibility(View.GONE);
ibInbox.setVisibility(View.GONE);
ibMore.setVisibility(View.GONE);
ibTools.setVisibility(View.GONE);
vwEmpty.setVisibility(View.GONE);
tvReformatted.setVisibility(View.GONE);
tvSignedData.setVisibility(View.GONE);
tvNoInternetBody.setVisibility(View.GONE);
grpDownloading.setVisibility(View.GONE);
tvBody.setText(null);
tvBody.setVisibility(View.GONE);
vwRipple.setVisibility(View.GONE);
// TO DO: clear web view?
wvBody.setVisibility(View.GONE);
pbBody.setVisibility(View.GONE);
clearActions();
ibInfrastructure.setVisibility(View.GONE);
ibTrashBottom.setVisibility(View.GONE);
ibArchiveBottom.setVisibility(View.GONE);
ibMoveBottom.setVisibility(View.GONE);
ibSeenBottom.setVisibility(View.GONE);
ibStoreMedia.setVisibility(View.GONE);
}
private void clearActions() {
ConstraintLayout cl = (ConstraintLayout) flow.getParent();
for (int id : flow.getReferencedIds()) {
View v = cl.findViewById(id);
cl.post(new Runnable() {
@Override
public void run() {
try {
flow.removeView(v);
cl.removeView(v);
} catch (Throwable ex) {
Log.e(ex);
}
}
});
// https://github.com/androidx/constraintlayout/issues/430
// v.setVisibility(View.GONE);
}
}
private void clearCalendar() {
ibCalendar.setVisibility(View.GONE);
tvCalendarSummary.setVisibility(View.GONE);
tvCalendarDescription.setVisibility(View.GONE);
tvCalendarLocation.setVisibility(View.GONE);
tvCalendarStart.setVisibility(View.GONE);
tvCalendarEnd.setVisibility(View.GONE);
tvAttendees.setVisibility(View.GONE);
pbCalendarWait.setVisibility(View.GONE);
}
private void bindSeen(TupleMessageEx message) {
if (cards && shadow_unread) {
boolean shadow = (message.unseen > 0);
int color = (shadow
? ColorUtils.setAlphaComponent(shadow_highlight ? colorUnreadHighlight : colorAccent, 127)
: Color.TRANSPARENT);
if (!Objects.equals(itemView.getTag(), shadow)) {
itemView.setTag(shadow);
itemView.setBackgroundColor(color);
ViewGroup.MarginLayoutParams lparam = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
lparam.topMargin = (shadow ? dp1 : 0);
lparam.bottomMargin = (shadow ? dp1 : 0);
itemView.setLayoutParams(lparam);
}
}
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) {
boolean pro = ActivityBilling.isPro(context);
boolean flagged = (message.count - message.unflagged) > 0;
int color = (message.color == null || !pro ? colorAccent : message.color);
int tint = (flagged ? color : textColorSecondary);
if (!Objects.equals(ibFlagged.getTag(), flagged)) {
ibFlagged.setTag(flagged);
ibFlagged.setImageResource(flagged ? R.drawable.baseline_star_24 : R.drawable.twotone_star_border_24);
}
ColorStateList csl = ibFlagged.getImageTintList();
if (csl == null || csl.getColorForState(new int[0], 0) != tint)
ibFlagged.setImageTintList(ColorStateList.valueOf(tint));
ibFlagged.setEnabled(message.uid != null || message.accountProtocol != EntityAccount.TYPE_IMAP);
card.setCardBackgroundColor(
flags_background && flagged && !expanded
? ColorUtils.setAlphaComponent(color, 127) : Color.TRANSPARENT);
ibFlagged.setVisibility(flags || message.ui_flagged ? View.VISIBLE : View.GONE);
}
private void bindContactInfo(TupleMessageEx message, ContactInfo[] info, Address[] addresses) {
if (avatars) {
ContactInfo main = (info.length > 0 ? info[0] : null);
if (main == null || !main.hasPhoto()) {
ibAvatar.setImageDrawable(null);
ibAvatar.setTag(null);
ibAvatar.setEnabled(false);
} else {
ibAvatar.setImageBitmap(main.getPhotoBitmap());
Uri lookupUri = main.getLookupUri();
ibAvatar.setTag(lookupUri);
if (BuildConfig.DEBUG)
ibAvatar.setContentDescription(main.getEmailAddress() + "=" + main.getType());
ibAvatar.setEnabled(lookupUri != null || BuildConfig.DEBUG);
}
ibAvatar.setVisibility(main == null || !main.hasPhoto() ? View.GONE : View.VISIBLE);
if (main != null && "vmc".equals(main.getType()) &&
Boolean.TRUE.equals(message.dkim) &&
Boolean.TRUE.equals(message.spf) &&
Boolean.TRUE.equals(message.dmarc)) {
ibVerified.setImageLevel(main.isVerified() ? 1 : 0);
ibVerified.setImageTintList(ColorStateList.valueOf(main.isVerified()
? colorVerified : colorControlNormal));
ibVerified.setContentDescription(context.getString(main.isVerified()
? R.string.title_advanced_bimi_verified
: R.string.title_advanced_bimi_unverified));
if (main.isVerified() || BuildConfig.DEBUG) {
ibVerified.setVisibility(View.VISIBLE);
if (authentication && authentication_indicator)
ibAuth.setVisibility(View.GONE);
}
}
}
if (distinguish_contacts) {
boolean known = false;
if (addresses != null)
for (int i = 0; i < addresses.length; i++)
if (info[i].isKnown()) {
known = true;
break;
}
if (known)
tvFrom.setPaintFlags(tvFrom.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
}
}
private void bindExpandWarning(TupleMessageEx message, boolean expanded) {
if (viewType != ViewType.THREAD || expanded || message.content || message.uid == null || unmetered)
tvExpand.setVisibility(View.GONE);
else {
tvExpand.setText(context.getString(R.string.title_expand_warning,
message.size == null ? "? kB" : Helper.humanReadableByteCount(message.size)));
tvExpand.setVisibility(View.VISIBLE);
}
}
private void bindExpanded(final TupleMessageEx message, final boolean scroll) {
DB db = DB.getInstance(context);
cowner.recreate();
if (compact) {
tvFrom.setSingleLine(false);
tvSubject.setSingleLine(false);
tvKeywords.setSingleLine(false);
}
tvPreview.setVisibility(View.GONE);
ensureExpanded();
bindAddresses(message);
bindHeaders(message, false);
bindAttachments(message, properties.getAttachments(message.id), false);
// Actions
vSeparator.setVisibility(View.VISIBLE);
ibFull.setEnabled(false);
ibFull.setVisibility(View.VISIBLE);
ibImages.setVisibility(View.INVISIBLE);
ibAmp.setVisibility(View.GONE);
ibDecrypt.setVisibility(View.GONE);
ibVerify.setVisibility(View.GONE);
ibUndo.setVisibility(View.GONE);
ibRule.setVisibility(View.GONE);
ibUnsubscribe.setVisibility(View.GONE);
ibHeaders.setVisibility(View.GONE);
ibPrint.setVisibility(View.GONE);
ibPin.setVisibility(View.GONE);
ibShare.setVisibility(View.GONE);
ibEvent.setVisibility(View.GONE);
ibSearchText.setVisibility(View.GONE);
ibSearch.setVisibility(View.GONE);
ibTranslate.setVisibility(View.GONE);
ibForceLight.setVisibility(View.GONE);
ibImportance.setVisibility(View.GONE);
ibHide.setVisibility(View.GONE);
ibSeen.setVisibility(View.GONE);
ibAnswer.setVisibility(View.GONE);
ibNotes.setVisibility(View.GONE);
ibLabels.setVisibility(View.GONE);
ibKeywords.setVisibility(View.GONE);
ibCopy.setVisibility(View.GONE);
ibMove.setVisibility(View.GONE);
ibArchive.setVisibility(View.GONE);
ibTrash.setVisibility(View.GONE);
ibJunk.setVisibility(View.GONE);
ibInbox.setVisibility(View.GONE);
ibMore.setVisibility(View.GONE);
ibTools.setVisibility(View.GONE);
vwEmpty.setVisibility(View.GONE);
tvReformatted.setVisibility(View.GONE);
tvSignedData.setVisibility(View.GONE);
// Message text
boolean content = (message.content || message.error != null);
tvNoInternetBody.setVisibility(suitable || content ? View.GONE : View.VISIBLE);
grpDownloading.setVisibility(content ? View.GONE : View.VISIBLE);
int height = properties.getHeight(message.id, 0);
if (height == 0) {
tvBody.setVisibility(View.GONE);
wvBody.setVisibility(View.GONE);
} else {
boolean show_full = properties.getValue("full", message.id);
if (show_full) {
wvBody.setVisibility(View.INVISIBLE);
wvBody.setMinimumHeight(height);
} else {
tvBody.setVisibility(View.INVISIBLE);
tvBody.setMinHeight(height);
}
}
vwRipple.setVisibility(View.GONE);
pbBody.setVisibility(View.GONE);
clearCalendar();
grpCalendar.setVisibility(View.GONE);
grpCalendarResponse.setVisibility(View.GONE);
grpAction.setVisibility(View.GONE);
clearActions();
ibInfrastructure.setVisibility(View.GONE);
ibTrashBottom.setVisibility(View.GONE);
ibArchiveBottom.setVisibility(View.GONE);
ibMoveBottom.setVisibility(View.GONE);
ibSeenBottom.setVisibility(View.GONE);
db.attachment().liveAttachments(message.id).observe(cowner, new Observer>() {
@Override
public void onChanged(@Nullable List attachments) {
int inlineImages = 0;
int embeddedMessages = 0;
if (attachments != null)
for (EntityAttachment attachment : attachments)
if (attachment.available)
if (attachment.isInline() && attachment.isImage())
inlineImages++;
else {
String mimeType = attachment.getMimeType();
if ("text/x-amp-html".equals(mimeType) ||
"message/rfc822".equals(mimeType))
embeddedMessages++;
}
int lastInlineImages = 0;
int lastEmbeddedMessages = 0;
List lastAttachments = properties.getAttachments(message.id);
if (lastAttachments != null)
for (EntityAttachment attachment : lastAttachments)
if (attachment.available)
if (attachment.isInline() && attachment.isImage())
lastInlineImages++;
else {
String mimeType = attachment.getMimeType();
if ("text/x-amp-html".equals(mimeType) ||
"message/rfc822".equals(mimeType))
lastEmbeddedMessages++;
}
boolean show_images = properties.getValue("images", message.id);
boolean inline = prefs.getBoolean("inline_images", false);
if (embeddedMessages > lastEmbeddedMessages ||
(inlineImages > lastInlineImages && (show_images || inline)))
bindBody(message, false);
bindAttachments(message, attachments, true);
if (!scroll)
properties.ready(message.id);
}
});
// Setup actions
setupTools(message, scroll, true);
}
private void setupTools(final TupleMessageEx message, final boolean scroll, final boolean bind) {
Bundle sargs = new Bundle();
sargs.putLong("id", message.id);
sargs.putLong("account", message.account);
new SimpleTask() {
@Override
protected ToolData onExecute(Context context, Bundle args) {
long id = args.getLong("id");
long aid = args.getLong("account");
ToolData data = new ToolData();
DB db = DB.getInstance(context);
EntityAccount account = db.account().getAccount(aid);
data.isGmail = (account != null && account.isGmail());
data.folders = db.folder().getSystemFolders(aid);
data.attachments = db.attachment().getAttachments(id);
return data;
}
@Override
protected void onExecuted(Bundle args, ToolData data) {
long id = args.getLong("id");
TupleMessageEx amessage = getMessage();
if (amessage == null || !amessage.id.equals(id))
return;
boolean show_expanded = properties.getValue("expanded", message.id);
if (!show_expanded)
return;
if (!attachments_alt && bind)
bindAttachments(message, data.attachments, false);
boolean hasInbox = false;
boolean hasArchive = false;
boolean hasTrash = false;
if (data.folders != null)
for (EntityFolder folder : data.folders)
if (folder.selectable)
if (EntityFolder.INBOX.equals(folder.type))
hasInbox = true;
else if (EntityFolder.ARCHIVE.equals(folder.type))
hasArchive = true;
else if (EntityFolder.TRASH.equals(folder.type))
hasTrash = true;
boolean pop = (message.accountProtocol == EntityAccount.TYPE_POP);
boolean imap = (message.accountProtocol == EntityAccount.TYPE_IMAP);
boolean inInbox = EntityFolder.INBOX.equals(message.folderType);
boolean inArchive = EntityFolder.ARCHIVE.equals(message.folderType);
boolean inSent = EntityFolder.SENT.equals(message.folderType);
boolean inTrash = EntityFolder.TRASH.equals(message.folderType);
boolean inJunk = EntityFolder.JUNK.equals(message.folderType);
boolean outbox = EntityFolder.OUTBOX.equals(message.folderType);
boolean move = !(message.folderReadOnly || message.uid == null) ||
(pop && EntityFolder.TRASH.equals(message.folderType));
boolean archive = (move && (hasArchive && !inArchive && !inSent && !inTrash && !inJunk));
boolean trash = (move || outbox || debug || pop);
boolean inbox = (move && hasInbox && (inArchive || inTrash || inJunk) && imap) ||
(pop && message.accountLeaveDeleted && inTrash);
boolean keywords = (message.uid != null && imap);
boolean labels = (data.isGmail && move && !inTrash && !inJunk && !outbox);
boolean seen = (message.uid != null || pop);
int froms = (message.from == null ? 0 : message.from.length);
int tos = (message.to == null ? 0 : message.to.length);
boolean delete = (inTrash || !hasTrash || inJunk || outbox || message.uid == null || pop);
boolean forever = (delete && (!pop || !message.accountLeaveDeleted));
boolean report = (pop ? inInbox : !inJunk && move);
boolean headers = (message.uid != null || (pop && message.headers != null));
evalProperties(message); // TODO: done again in bindBody
boolean full = properties.getValue("full", message.id);
boolean dark = Helper.isDarkTheme(context);
boolean force_light = properties.getValue("force_light", message.id);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean expand_all = prefs.getBoolean("expand_all", false);
boolean expand_one = prefs.getBoolean("expand_one", true);
boolean tools = prefs.getBoolean("message_tools", true);
boolean button_junk = prefs.getBoolean("button_junk", true);
boolean button_trash = prefs.getBoolean("button_trash", true);
boolean button_archive = prefs.getBoolean("button_archive", true);
boolean button_move = prefs.getBoolean("button_move", true);
boolean button_copy = prefs.getBoolean("button_copy", false);
boolean button_keywords = prefs.getBoolean("button_keywords", false);
boolean button_notes = prefs.getBoolean("button_notes", false);
boolean button_seen = prefs.getBoolean("button_seen", false);
boolean button_hide = prefs.getBoolean("button_hide", false);
boolean button_importance = prefs.getBoolean("button_importance", false);
boolean button_translate = prefs.getBoolean("button_translate", true);
boolean button_force_light = prefs.getBoolean("button_force_light", false);
boolean button_search = prefs.getBoolean("button_search", false);
boolean button_search_text = prefs.getBoolean("button_search_text", false);
boolean button_event = prefs.getBoolean("button_event", false);
boolean button_share = prefs.getBoolean("button_share", false);
boolean button_pin = prefs.getBoolean("button_pin", false);
boolean button_print = prefs.getBoolean("button_print", false);
boolean button_headers = prefs.getBoolean("button_headers", false);
boolean button_unsubscribe = prefs.getBoolean("button_unsubscribe", true);
boolean button_rule = prefs.getBoolean("button_rule", false);
boolean swipe_reply = prefs.getBoolean("swipe_reply", false);
int importance = (((message.ui_importance == null ? 1 : message.ui_importance) + 1) % 3);
ibImportance.setImageLevel(importance);
ibHide.setImageResource(message.ui_snoozed == null ? R.drawable.twotone_visibility_off_24 : R.drawable.twotone_visibility_24);
ibSeen.setImageResource(message.ui_seen ? R.drawable.twotone_mail_24 : R.drawable.twotone_drafts_24);
ibTrash.setTag(delete);
ibTrash.setImageResource(forever ? R.drawable.twotone_delete_forever_24 : R.drawable.twotone_delete_24);
ibTrash.setImageTintList(ColorStateList.valueOf(outbox ? colorWarning : colorControlNormal));
ibTrashBottom.setImageResource(forever ? R.drawable.twotone_delete_forever_24 : R.drawable.twotone_delete_24);
ibInbox.setImageResource(inJunk ? R.drawable.twotone_report_off_24 : R.drawable.twotone_inbox_24);
ibUndo.setVisibility(outbox ? View.VISIBLE : View.GONE);
ibRule.setVisibility(tools && button_rule && !outbox && !message.folderReadOnly ? View.VISIBLE : View.GONE);
ibUnsubscribe.setVisibility(tools && button_unsubscribe && message.unsubscribe != null ? View.VISIBLE : View.GONE);
ibHeaders.setVisibility(tools && button_headers && headers ? View.VISIBLE : View.GONE);
ibPrint.setVisibility(tools && !outbox && button_print && hasWebView && message.content && Helper.canPrint(context) ? View.VISIBLE : View.GONE);
ibPin.setVisibility(tools && !outbox && button_pin && pin ? View.VISIBLE : View.GONE);
ibShare.setVisibility(tools && !outbox && button_share && message.content ? View.VISIBLE : View.GONE);
ibEvent.setVisibility(tools && !outbox && button_event && message.content ? View.VISIBLE : View.GONE);
ibSearchText.setVisibility(tools && !outbox && button_search_text && message.content && !full ? View.VISIBLE : View.GONE);
ibSearch.setVisibility(tools && !outbox && button_search && (froms > 0 || tos > 0) ? View.VISIBLE : View.GONE);
ibTranslate.setVisibility(tools && !outbox && button_translate && DeepL.isAvailable(context) && message.content ? View.VISIBLE : View.GONE);
ibForceLight.setVisibility(tools && full && dark && button_force_light && message.content ? View.VISIBLE : View.GONE);
ibForceLight.setImageLevel(force_light ? 1 : 0);
ibImportance.setVisibility(tools && button_importance && !outbox && seen ? View.VISIBLE : View.GONE);
ibHide.setVisibility(tools && button_hide && !outbox ? View.VISIBLE : View.GONE);
ibSeen.setVisibility(tools && button_seen && !outbox && seen ? View.VISIBLE : View.GONE);
ibAnswer.setVisibility(!tools || outbox || (!expand_all && expand_one) || !threading || swipe_reply ? View.GONE : View.VISIBLE);
ibNotes.setVisibility(tools && button_notes && !outbox ? View.VISIBLE : View.GONE);
ibLabels.setVisibility(tools && labels_header && labels ? View.VISIBLE : View.GONE);
ibKeywords.setVisibility(tools && button_keywords && keywords ? View.VISIBLE : View.GONE);
ibCopy.setVisibility(tools && button_copy && move ? View.VISIBLE : View.GONE);
ibMove.setVisibility(tools && button_move && move ? View.VISIBLE : View.GONE);
ibArchive.setVisibility(tools && button_archive && archive ? View.VISIBLE : View.GONE);
ibTrash.setVisibility(outbox || (tools && button_trash && trash) ? View.VISIBLE : View.GONE);
ibJunk.setVisibility(tools && button_junk && report ? View.VISIBLE : View.GONE);
ibInbox.setVisibility(tools && inbox ? View.VISIBLE : View.GONE);
ibMore.setVisibility(tools && !outbox ? View.VISIBLE : View.GONE);
ibTools.setImageLevel(tools ? 0 : 1);
ibTools.setContentDescription(context.getString(tools ? R.string.title_less : R.string.title_more));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
ibTools.setTooltipText(ibTools.getContentDescription());
ibTools.setVisibility(outbox ? View.GONE : View.VISIBLE);
vwEmpty.setVisibility(outbox ? View.GONE : View.VISIBLE);
if (bind)
bindBody(message, scroll);
else
bindExtras(message);
}
@Override
protected void onException(Bundle args, Throwable ex) {
Log.unexpectedError(parentFragment.getParentFragmentManager(), ex);
}
}.setLog(false).execute(context, owner, sargs, "message:tools");
}
private String formatAddresses(Address[] addresses, MessageHelper.AddressFormat format, int max) {
List list = new ArrayList<>();
if (addresses != null)
for (Address address : addresses)
if (!list.contains(address))
list.add(address);
Address[] sub = list.subList(0, Math.min(list.size(), max)).toArray(new Address[0]);
String result = MessageHelper.formatAddresses(sub, format, false);
if (list.size() > sub.length)
result = context.getString(R.string.title_name_plus, result, list.size() - sub.length);
return result;
}
private Spanned formatAddresses(Address[] addresses, boolean full) {
return formatAddresses(addresses, full, Integer.MAX_VALUE);
}
private Spanned formatAddresses(Address[] addresses, boolean full, int max) {
SpannableStringBuilder ssb = new SpannableStringBuilderEx();
if (addresses == null || addresses.length == 0)
return ssb;
for (int i = 0; i < addresses.length && i < max; i++) {
if (i > 0)
ssb.append("; ");
if (addresses[i] instanceof InternetAddress) {
InternetAddress address = (InternetAddress) addresses[i];
String email = address.getAddress();
String personal = address.getPersonal();
if (TextUtils.isEmpty(personal)) {
if (!TextUtils.isEmpty(email)) {
int start = ssb.length();
ssb.append(email);
ssb.setSpan(new ForegroundColorSpan(textColorLink), start, ssb.length(), 0);
}
} else {
if (full) {
ssb.append(personal).append(" <");
if (!TextUtils.isEmpty(email)) {
int start = ssb.length();
ssb.append(email);
ssb.setSpan(new ForegroundColorSpan(textColorLink), start, ssb.length(), 0);
}
ssb.append(">");
} else
ssb.append(personal);
}
} else
ssb.append(addresses[i].toString());
}
if (addresses.length > max)
ssb.append(context.getString(R.string.title_name_plus, "", addresses.length - max));
return ssb;
}
private void bindAddresses(TupleMessageEx message) {
boolean show_addresses = properties.getValue("addresses", message.id);
boolean full = (show_addresses || email_format == MessageHelper.AddressFormat.NAME_EMAIL);
int froms = (message.from == null ? 0 : message.from.length);
int tos = (message.to == null ? 0 : message.to.length);
boolean hasChannel = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
int maxRecipients = (compact ? MAX_RECIPIENTS_COMPACT : MAX_RECIPIENTS_NORMAL);
Spanned submitter = formatAddresses(message.submitter, true);
Spanned from = formatAddresses(message.senders, true);
grpAddresses.setVisibility(View.VISIBLE);
ibExpanderAddress.setImageLevel(show_addresses ? 0 /* less */ : 1 /* more */);
ibExpanderAddress.setContentDescription(context.getString(show_addresses ? R.string.title_accessibility_hide_addresses : R.string.title_accessibility_show_addresses));
ivPlain.setVisibility(show_addresses && message.isPlainOnly() ? View.VISIBLE : View.GONE);
ibReceipt.setVisibility(message.receipt_request != null && message.receipt_request ? View.VISIBLE : View.GONE);
ibReceipt.setImageTintList(ColorStateList.valueOf(message.ui_answered ? colorControlNormal : colorError));
ivAutoSubmitted.setVisibility(show_addresses && message.auto_submitted != null && message.auto_submitted ? View.VISIBLE : View.GONE);
ivBrowsed.setVisibility(show_addresses && message.ui_browsed ? View.VISIBLE : View.GONE);
ivRaw.setVisibility(BuildConfig.DEBUG && Boolean.TRUE.equals(message.raw) ? View.VISIBLE : View.GONE);
boolean button_search = prefs.getBoolean("button_search", false);
ibSearchContact.setVisibility(show_addresses && (froms > 0 || tos > 0) && !button_search ? View.VISIBLE : View.GONE);
ibNotifyContact.setVisibility(show_addresses && hasChannel && froms > 0 ? View.VISIBLE : View.GONE);
ibPinContact.setVisibility(show_addresses && pin && contacts && froms > 0 ? View.VISIBLE : View.GONE);
ibAddContact.setVisibility(show_addresses && contacts && froms > 0 ? View.VISIBLE : View.GONE);
tvSubmitterTitle.setVisibility(!TextUtils.isEmpty(submitter) ? View.VISIBLE : View.GONE);
tvSubmitter.setVisibility(!TextUtils.isEmpty(submitter) ? View.VISIBLE : View.GONE);
tvSubmitter.setText(submitter);
InternetAddress deliveredto = new InternetAddress();
deliveredto.setAddress(message.deliveredto);
tvDeliveredToTitle.setVisibility(show_addresses && !TextUtils.isEmpty(message.deliveredto) ? View.VISIBLE : View.GONE);
tvDeliveredTo.setVisibility(show_addresses && !TextUtils.isEmpty(message.deliveredto) ? View.VISIBLE : View.GONE);
tvDeliveredTo.setText(formatAddresses(new Address[]{deliveredto}, true));
tvFromExTitle.setVisibility((froms > 1 || show_addresses) && !TextUtils.isEmpty(from) ? View.VISIBLE : View.GONE);
tvFromEx.setVisibility((froms > 1 || show_addresses) && !TextUtils.isEmpty(from) ? View.VISIBLE : View.GONE);
tvFromEx.setText(from);
tvToTitle.setVisibility((!show_recipients || show_addresses) && (message.to != null && message.to.length > 0) ? View.VISIBLE : View.GONE);
tvTo.setVisibility((!show_recipients || show_addresses) && (message.to != null && message.to.length > 0) ? View.VISIBLE : View.GONE);
tvTo.setText(formatAddresses(message.to, full, show_addresses ? Integer.MAX_VALUE : maxRecipients));
boolean show_reply = (Boolean.FALSE.equals(message.reply_domain) || show_addresses) &&
(message.reply != null && message.reply.length > 0);
tvReplyToTitle.setVisibility(show_reply ? View.VISIBLE : View.GONE);
tvReplyTo.setVisibility(show_reply ? View.VISIBLE : View.GONE);
tvReplyTo.setText(formatAddresses(message.reply, show_addresses));
tvCcTitle.setVisibility(message.cc == null || message.cc.length == 0 ? View.GONE : View.VISIBLE);
tvCc.setVisibility(message.cc == null || message.cc.length == 0 ? View.GONE : View.VISIBLE);
tvCc.setText(formatAddresses(message.cc, full, show_addresses ? Integer.MAX_VALUE : maxRecipients));
tvBccTitle.setVisibility(message.bcc == null || message.bcc.length == 0 ? View.GONE : View.VISIBLE);
tvBcc.setVisibility(message.bcc == null || message.bcc.length == 0 ? View.GONE : View.VISIBLE);
tvBcc.setText(formatAddresses(message.bcc, full, show_addresses ? Integer.MAX_VALUE : maxRecipients));
InternetAddress via = null;
if (message.identityEmail != null)
try {
via = new InternetAddress(message.identityEmail, message.identityName, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException ignored) {
}
tvIdentityTitle.setVisibility(show_addresses && via != null ? View.VISIBLE : View.GONE);
tvIdentity.setVisibility(show_addresses && via != null ? View.VISIBLE : View.GONE);
tvIdentity.setText(via == null ? null : formatAddresses(new Address[]{via}, true));
tvSentTitle.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvSent.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvSent.setText(message.sent == null ? null : DTF.format(message.sent));
tvReceivedTitle.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvReceived.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvReceived.setText(DTF.format(message.received));
tvStoredTitle.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvStored.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvStored.setText(DTF.format(message.stored));
if (!message.duplicate)
tvSizeEx.setAlpha(message.content ? 1.0f : Helper.LOW_LIGHT);
tvSizeExTitle.setVisibility(!show_addresses || message.size == null ? View.GONE : View.VISIBLE);
tvSizeEx.setVisibility(!show_addresses || (message.size == null && message.total == null) ? View.GONE : View.VISIBLE);
StringBuilder size = new StringBuilder();
size
.append(message.size == null ? "-" : Helper.humanReadableByteCount(message.size))
.append("/")
.append(message.total == null ? "-" : Helper.humanReadableByteCount(message.total));
tvSizeEx.setText(size.toString());
boolean showLanguage = (language_detection && message.language != null &&
(show_addresses ||
(languages != null && !languages.contains(message.language))));
tvLanguageTitle.setVisibility(showLanguage ? View.VISIBLE : View.GONE);
tvLanguage.setVisibility(showLanguage ? View.VISIBLE : View.GONE);
tvLanguage.setText(message.language == null ? null : new Locale(message.language).getDisplayLanguage());
boolean show_thread = (show_addresses && (BuildConfig.DEBUG || debug));
tvThreadTitle.setVisibility(show_thread ? View.VISIBLE : View.GONE);
tvThread.setVisibility(show_thread ? View.VISIBLE : View.GONE);
tvThread.setText(message.thread);
tvSubjectEx.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvSubjectEx.setText(message.subject);
if (subject_italic)
tvSubjectEx.setTypeface(Typeface.DEFAULT, Typeface.ITALIC);
else
tvSubjectEx.setTypeface(Typeface.DEFAULT);
// Flags
tvFlags.setVisibility(show_addresses && debug ? View.VISIBLE : View.GONE);
tvFlags.setText(message.flags);
// Keywords
if (keywords_header) {
tvKeywordsEx.setVisibility(show_addresses && message.keywords.length > 0 ? View.VISIBLE : View.GONE);
tvKeywordsEx.setText(TextUtils.join(" ", message.keywords));
} else {
Spanned keywords = getKeywords(message);
tvKeywordsEx.setVisibility(!show_addresses || keywords == null ? View.GONE : View.VISIBLE);
tvKeywordsEx.setText(keywords);
}
}
private void bindHeaders(TupleMessageEx message, boolean scroll) {
boolean show_headers = properties.getValue("headers", message.id);
grpHeaders.setVisibility(show_headers ? View.VISIBLE : View.GONE);
if (show_headers && message.headers == null) {
pbHeaders.setVisibility(suitable ? View.VISIBLE : View.GONE);
tvNoInternetHeaders.setVisibility(suitable ? View.GONE : View.VISIBLE);
} else {
pbHeaders.setVisibility(View.GONE);
tvNoInternetHeaders.setVisibility(View.GONE);
}
if (show_headers && message.headers != null) {
Spanned headers = HtmlHelper.highlightHeaders(context,
message.headers, message.blocklist != null && message.blocklist);
if (BuildConfig.DEBUG && headers instanceof SpannableStringBuilder) {
SpannableStringBuilder ssb = (SpannableStringBuilder) headers;
ssb.append('\n')
.append("TLS=").append(message.tls == null ? "-" : (message.tls ? "✓" : "✗"))
.append(" DKIM=").append(message.dkim == null ? "-" : (message.dkim ? "✓" : "✗"))
.append(" SPF=").append(message.spf == null ? "-" : (message.spf ? "✓" : "✗"))
.append(" DMARC=").append(message.dmarc == null ? "-" : (message.dmarc ? "✓" : "✗"))
.append(" BL=").append(message.blocklist == null ? "-" : (message.blocklist ? "✓" : "✗"))
.append('\n');
}
tvHeaders.setText(headers);
ibCopyHeaders.setVisibility(View.VISIBLE);
} else {
tvHeaders.setText(null);
ibCopyHeaders.setVisibility(View.GONE);
}
if (scroll)
ApplicationEx.getMainHandler().post(new Runnable() {
@Override
public void run() {
View inHeaders = itemView.findViewById(R.id.inHeaders);
Rect rect = new Rect();
inHeaders.getDrawingRect(rect);
((ViewGroup) itemView).offsetDescendantRectToMyCoords(inHeaders, rect);
properties.scrollTo(getAdapterPosition(), rect.top);
}
});
}
private void evalProperties(TupleMessageEx message) {
if (message.from != null)
for (Address sender : message.from) {
String from = ((InternetAddress) sender).getAddress();
if (TextUtils.isEmpty(from))
continue;
int at = from.indexOf('@');
String domain = (at < 0 ? from : from.substring(at));
if (message.show_full ||
prefs.getBoolean(from + ".show_full", false) ||
prefs.getBoolean(domain + ".show_full", false)) {
properties.setValue("full", message.id, true);
properties.setValue("full_asked", message.id, true);
}
if (message.show_images ||
prefs.getBoolean(from + ".show_images", false) ||
prefs.getBoolean(domain + ".show_images", false)) {
properties.setValue("images", message.id, true);
properties.setValue("images_asked", message.id, true);
}
}
boolean confirm_images = prefs.getBoolean("confirm_images", true);
if (!confirm_images &&
!EntityFolder.JUNK.equals(message.folderType) &&
!properties.getValue("images_asked", message.id)) {
properties.setValue("images", message.id, true);
properties.setValue("images_asked", message.id, true);
}
boolean confirm_html = prefs.getBoolean("confirm_html", true);
if (!confirm_html &&
!EntityFolder.JUNK.equals(message.folderType) &&
!properties.getValue("full_asked", message.id)) {
properties.setValue("full", message.id, true);
properties.setValue("full_asked", message.id, true);
}
if (!properties.getValue("force_light_default", message.id)) {
boolean default_light = prefs.getBoolean("default_light", false);
properties.setValue("force_light", message.id, default_light);
properties.setValue("force_light_default", message.id, true);
}
}
private void bindBody(TupleMessageEx message, final boolean scroll) {
if (!Objects.equals(tvBody.getTag(), message.id)) {
tvBody.setTag(message.id);
tvBody.setText(null);
}
properties.endSearch();
clearActions();
if (!message.content) {
bindExtras(message);
if (scroll)
properties.scrollTo(getAdapterPosition(), 0);
return;
}
evalProperties(message);
boolean show_full = properties.getValue("full", message.id);
boolean show_images = properties.getValue("images", message.id);
boolean show_quotes = (properties.getValue("quotes", message.id) || !collapse_quotes);
boolean force_light = properties.getValue("force_light", message.id);
boolean always_images = prefs.getBoolean("html_always_images", false);
if (always_images && show_full) {
show_images = true;
properties.setValue("images", message.id, true);
}
float size = properties.getSize(message.id, show_full ? 0 : textSize * message_zoom / 100f);
int height = properties.getHeight(message.id, dp60);
Pair position = properties.getPosition(message.id);
Log.i("Bind size=" + size + " height=" + height);
ibFull.setEnabled(hasWebView);
ibFull.setImageResource(show_full ? R.drawable.twotone_fullscreen_exit_24 : R.drawable.twotone_fullscreen_24);
ibFull.setContentDescription(context.getString(show_full
? R.string.title_legend_show_reformatted
: R.string.title_legend_show_full));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
ibFull.setTooltipText(ibFull.getContentDescription());
ibImages.setImageResource(show_images ? R.drawable.twotone_article_24 : R.drawable.twotone_image_24);
ibImages.setContentDescription(context.getString(show_images
? R.string.title_legend_hide_images
: R.string.title_legend_show_images));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
ibImages.setTooltipText(ibImages.getContentDescription());
if (show_full) {
// Create web view
WebViewEx webView;
if (wvBody instanceof WebView)
webView = (WebViewEx) wvBody;
else {
try {
webView = new WebViewEx(context);
} catch (Throwable ex) {
/*
android.util.AndroidRuntimeException: java.lang.reflect.InvocationTargetException
at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:270)
at android.webkit.WebView.getFactory(WebView.java:2681)
at android.webkit.WebView.ensureProviderCreated(WebView.java:2676)
at android.webkit.WebView.setOverScrollMode(WebView.java:2741)
at android.view.View.(View.java:4815)
at android.view.View.(View.java:4956)
at android.view.ViewGroup.(ViewGroup.java:659)
at android.widget.AbsoluteLayout.(AbsoluteLayout.java:55)
at android.webkit.WebView.(WebView.java:659)
at android.webkit.WebView.(WebView.java:604)
at android.webkit.WebView.(WebView.java:587)
at android.webkit.WebView.(WebView.java:574)
at android.webkit.WebView.(WebView.java:564)
*/
Log.unexpectedError(parentFragment.getParentFragmentManager(), ex, false);
return;
}
webView.setId(wvBody.getId());
ConstraintLayout cl = (ConstraintLayout) vsBody;
cl.removeView(wvBody);
cl.addView(webView, wvBody.getLayoutParams());
cl.setPadding(
wvBody.getPaddingLeft(), wvBody.getPaddingTop(),
wvBody.getPaddingRight(), wvBody.getPaddingBottom());
wvBody = webView;
}
webView.setMinimumHeight(height);
int maxHeight = (rv == null ? 0 : rv.getHeight() - rv.getPaddingTop());
webView.init(height, maxHeight, size, position, force_light,
new WebViewEx.IWebView() {
@Override
public void onSizeChanged(int w, int h, int ow, int oh) {
if (h > dp60)
properties.setHeight(message.id, h);
}
@Override
public void onScaleChanged(float newScale) {
properties.setSize(message.id, newScale);
}
@Override
public void onScrollChange(int scrollX, int scrollY) {
properties.setPosition(message.id, new Pair(scrollX, scrollY));
}
@Override
public void onOverScrolled(int scrollX, int scrollY, int dx, int dy, boolean clampedX, boolean clampedY) {
if (clampedY && ((WebViewEx) wvBody).isZoomedY()) {
boolean flinged = false;
try {
if (!webview_legacy && rv != null)
flinged = rv.fling(dx * 10, dy * 10);
} catch (Throwable ex) {
Log.e(ex);
}
if (!flinged)
properties.scrollBy(dx, dy);
}
}
@Override
public boolean onOpenLink(String url) {
if (parentFragment == null)
return false;
Uri uri = Uri.parse(url);
return ViewHolder.this.onOpenLink(uri, null, false);
}
});
webView.setImages(show_images, inline);
webView.setOnTouchListener(touchListener);
tvBody.setVisibility(View.GONE);
wvBody.setVisibility(View.VISIBLE);
} else {
tvBody.setMinHeight(height);
if (size != 0)
tvBody.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
tvBody.setTextColor(contrast ? textColorPrimary : colorRead);
tvBody.setTypeface(StyleHelper.getTypeface(display_font, context));
tvBody.setVisibility(View.VISIBLE);
wvBody.setVisibility(View.GONE);
}
final Bundle args = new Bundle();
args.putSerializable("message", message);
args.putBoolean("show_full", show_full);
args.putBoolean("show_images", show_images);
args.putBoolean("show_quotes", show_quotes);
args.putInt("zoom", zoom);
float scale = (size == 0 || textSize == 0 ? 1.0f : size / (textSize * message_zoom / 100f));
args.putFloat("scale", scale);
new SimpleTask