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-2021 by Marcel Bokhorst (M66B)
*/
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.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.database.Cursor;
import android.graphics.Bitmap;
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.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.LocaleList;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.text.Editable;
import android.text.Html;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.format.DateUtils;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.LinkMovementMethod;
import android.text.style.BackgroundColorSpan;
import android.text.style.DynamicDrawableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.text.style.QuoteSpan;
import android.text.style.URLSpan;
import android.util.Pair;
import android.util.TypedValue;
import android.view.Gravity;
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.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.ConversationAction;
import android.view.textclassifier.ConversationActions;
import android.webkit.WebSettings;
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.PopupWindow;
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.content.pm.ShortcutManagerCompat;
import androidx.core.graphics.ColorUtils;
import androidx.core.util.PatternsCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
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 java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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.util.ICalDate;
import static android.app.Activity.RESULT_OK;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
public class AdapterMessage extends RecyclerView.Adapter {
private Fragment parentFragment;
private String type;
private boolean found;
private ViewType viewType;
private boolean compact;
private int zoom;
private String sort;
private boolean ascending;
private boolean filter_duplicates;
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 colorAccent;
private int textColorPrimary;
private int textColorSecondary;
private int textColorLink;
private int colorUnread;
private int colorRead;
private int colorSubject;
private int colorEncrypt;
private int colorSeparator;
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 threading;
private boolean threading_unread;
private boolean indentation;
private boolean avatars;
private boolean color_stripe;
private boolean name_email;
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 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 boolean monospaced;
private boolean inline;
private boolean collapse_quotes;
private boolean authentication;
private boolean language_detection;
private List languages;
private static boolean debug;
private boolean gotoTop = false;
private Integer gotoPos = null;
private boolean firstClick = false;
private int searchResult = 0;
private AsyncPagedListDiffer differ;
private Map keyPosition = new HashMap<>();
private Map positionKey = new HashMap<>();
private SelectionTracker selectionTracker = 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 = 10;
// https://github.com/newhouse/url-tracking-stripper
private static final List PARANOID_QUERY = Collections.unmodifiableList(Arrays.asList(
// https://en.wikipedia.org/wiki/UTM_parameters
"icid", // Adobe
"gclid", // Google
"gclsrc", // Google ads
"dclid", // DoubleClick (Google)
"fbclid", // Facebook
"igshid", // Instagram
"mc_cid", // MailChimp
"mc_eid", // MailChimp
"zanpid", // Zanox (Awin)
"kclickid" // https://support.freespee.com/hc/en-us/articles/202577831-Kenshoo-integration
));
// https://github.com/snarfed/granary/blob/master/granary/facebook.py#L1789
private static final List FACEBOOK_WHITELIST_PATH = Collections.unmodifiableList(Arrays.asList(
"/nd/", "/n/", "/story.php"
));
private static final List FACEBOOK_WHITELIST_QUERY = Collections.unmodifiableList(Arrays.asList(
"story_fbid", "fbid", "id", "comment_id"
));
// https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml
private static final List IMAP_KEYWORDS_BLACKLIST = Collections.unmodifiableList(Arrays.asList(
"$MDNSent".toLowerCase(Locale.ROOT), // https://tools.ietf.org/html/rfc3503
"$Forwarded".toLowerCase(Locale.ROOT),
"$SubmitPending".toLowerCase(Locale.ROOT),
"$Submitted".toLowerCase(Locale.ROOT),
"$Junk".toLowerCase(Locale.ROOT),
"$NotJunk".toLowerCase(Locale.ROOT),
"Junk".toLowerCase(Locale.ROOT),
"NonJunk".toLowerCase(Locale.ROOT),
"$recent".toLowerCase(Locale.ROOT),
"DTAG_document".toLowerCase(Locale.ROOT),
"DTAG_image".toLowerCase(Locale.ROOT),
"$X-Me-Annot-1".toLowerCase(Locale.ROOT),
"$X-Me-Annot-2".toLowerCase(Locale.ROOT),
"\\Unseen".toLowerCase(Locale.ROOT), // Mail.ru
"$sent".toLowerCase(Locale.ROOT), // Kmail
"$attachment".toLowerCase(Locale.ROOT), // Kmail
"$signed".toLowerCase(Locale.ROOT), // Kmail
"$encrypted".toLowerCase(Locale.ROOT) // Kmail
));
public class ViewHolder extends RecyclerView.ViewHolder implements
View.OnClickListener,
View.OnLongClickListener,
View.OnTouchListener,
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 ibAuth;
private ImageView ivPriorityHigh;
private ImageView ivPriorityLow;
private ImageView ivImportance;
private ImageView ivSigned;
private ImageView ivEncrypted;
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 ibHelp;
private View vsBody;
private ImageButton ibExpanderAddress;
private ImageView ivPlain;
private ImageButton ibReceipt;
private ImageView ivAutoSubmitted;
private ImageView ivBrowsed;
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 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 tvSubjectEx;
private TextView tvFlags;
private TextView tvKeywordsEx;
private TextView tvHeaders;
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 ibDecrypt;
private ImageButton ibVerify;
private ImageButton ibUndo;
private ImageButton ibRule;
private ImageButton ibUnsubscribe;
private ImageButton ibPrint;
private ImageButton ibShare;
private ImageButton ibEvent;
private ImageButton ibSearch;
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 TextView tvReformatted;
private TextView tvSignedData;
private TextView tvBody;
private View wvBody;
private ContentLoadingProgressBar pbBody;
private TextView tvNoInternetBody;
private ImageButton ibDownloading;
private Group grpDownloading;
private ImageButton ibTrashBottom;
private ImageButton ibArchiveBottom;
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 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 ScaleGestureDetector gestureDetector;
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);
ibAuth = itemView.findViewById(R.id.ibAuth);
ivPriorityHigh = itemView.findViewById(R.id.ivPriorityHigh);
ivPriorityLow = itemView.findViewById(R.id.ivPriorityLow);
ivImportance = itemView.findViewById(R.id.ivImportance);
ivSigned = itemView.findViewById(R.id.ivSigned);
ivEncrypted = itemView.findViewById(R.id.ivEncrypted);
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);
ibHelp = itemView.findViewById(R.id.ibHelp);
if (tvFrom != null) {
if (compact)
tvFrom.setSingleLine(true);
}
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 (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);
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);
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);
tvSubjectEx = vsBody.findViewById(R.id.tvSubjectEx);
tvFlags = vsBody.findViewById(R.id.tvFlags);
tvKeywordsEx = vsBody.findViewById(R.id.tvKeywordsEx);
tvHeaders = vsBody.findViewById(R.id.tvHeaders);
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);
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);
ibPrint = vsBody.findViewById(R.id.ibPrint);
ibShare = vsBody.findViewById(R.id.ibShare);
ibEvent = vsBody.findViewById(R.id.ibEvent);
ibSearch = vsBody.findViewById(R.id.ibSearch);
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);
tvReformatted = vsBody.findViewById(R.id.tvReformatted);
tvSignedData = vsBody.findViewById(R.id.tvSignedData);
tvBody = vsBody.findViewById(R.id.tvBody);
wvBody = vsBody.findViewById(R.id.wvBody);
pbBody = vsBody.findViewById(R.id.pbBody);
tvNoInternetBody = vsBody.findViewById(R.id.tvNoInternetBody);
ibDownloading = vsBody.findViewById(R.id.ibDownloading);
grpDownloading = vsBody.findViewById(R.id.grpDownloading);
ibTrashBottom = vsBody.findViewById(R.id.ibTrashBottom);
ibArchiveBottom = vsBody.findViewById(R.id.ibArchiveBottom);
ibSeenBottom = vsBody.findViewById(R.id.ibSeenBottom);
flow = vsBody.findViewById(R.id.flow);
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);
ibAuth.setOnClickListener(this);
ibSnoozed.setOnClickListener(this);
ibFlagged.setOnClickListener(this);
if (viewType == ViewType.THREAD)
ibFlagged.setOnLongClickListener(this);
ibHelp.setOnClickListener(this);
if (vsBody != null) {
ibExpanderAddress.setOnClickListener(this);
ibReceipt.setOnClickListener(this);
ibSearchContact.setOnClickListener(this);
ibNotifyContact.setOnClickListener(this);
ibPinContact.setOnClickListener(this);
ibAddContact.setOnClickListener(this);
ibSaveAttachments.setOnClickListener(this);
ibDownloadAttachments.setOnClickListener(this);
ibFull.setOnClickListener(this);
ibFull.setOnLongClickListener(this);
ibImages.setOnClickListener(this);
ibDecrypt.setOnClickListener(this);
ibVerify.setOnClickListener(this);
ibUndo.setOnClickListener(this);
ibRule.setOnClickListener(this);
ibUnsubscribe.setOnClickListener(this);
ibPrint.setOnClickListener(this);
ibShare.setOnClickListener(this);
ibEvent.setOnClickListener(this);
ibSearch.setOnClickListener(this);
ibSeen.setOnClickListener(this);
ibAnswer.setOnClickListener(this);
ibNotes.setOnClickListener(this);
ibLabels.setOnClickListener(this);
ibKeywords.setOnClickListener(this);
ibCopy.setOnClickListener(this);
ibMove.setOnClickListener(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);
ibTrashBottom.setOnClickListener(this);
ibTrashBottom.setOnLongClickListener(this);
ibArchiveBottom.setOnClickListener(this);
ibSeenBottom.setOnClickListener(this);
tvBody.setOnTouchListener(this);
tvBody.addOnLayoutChangeListener(this);
ibCalendar.setOnClickListener(this);
btnCalendarAccept.setOnClickListener(this);
btnCalendarDecline.setOnClickListener(this);
btnCalendarMaybe.setOnClickListener(this);
btnCalendarAccept.setOnLongClickListener(this);
btnCalendarDecline.setOnLongClickListener(this);
btnCalendarMaybe.setOnLongClickListener(this);
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));
// 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();
ImageHelper.AnnotatedSource a = new ImageHelper.AnnotatedSource(img.getSource());
ImageHelper.fitDrawable(d, a, 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;
}
});
}
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);
ibAuth.setOnClickListener(null);
ibSnoozed.setOnClickListener(null);
ibFlagged.setOnClickListener(null);
if (viewType == ViewType.THREAD)
ibFlagged.setOnLongClickListener(null);
ibHelp.setOnClickListener(null);
if (vsBody != null) {
ibExpanderAddress.setOnClickListener(null);
ibReceipt.setOnLongClickListener(null);
ibSearchContact.setOnClickListener(null);
ibNotifyContact.setOnClickListener(null);
ibPinContact.setOnClickListener(null);
ibAddContact.setOnClickListener(null);
ibSaveAttachments.setOnClickListener(null);
ibDownloadAttachments.setOnClickListener(null);
ibFull.setOnClickListener(null);
ibFull.setOnLongClickListener(null);
ibImages.setOnClickListener(null);
ibDecrypt.setOnClickListener(null);
ibVerify.setOnClickListener(null);
ibUndo.setOnClickListener(null);
ibRule.setOnClickListener(null);
ibUnsubscribe.setOnClickListener(null);
ibPrint.setOnClickListener(null);
ibShare.setOnClickListener(null);
ibEvent.setOnClickListener(null);
ibSearch.setOnClickListener(null);
ibSeen.setOnClickListener(null);
ibAnswer.setOnClickListener(null);
ibNotes.setOnClickListener(null);
ibLabels.setOnClickListener(null);
ibKeywords.setOnClickListener(null);
ibCopy.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);
ibTrashBottom.setOnClickListener(null);
ibTrashBottom.setOnLongClickListener(null);
ibArchiveBottom.setOnClickListener(null);
ibSeenBottom.setOnClickListener(null);
tvBody.setOnTouchListener(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);
}
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 || !threading));
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) ||
Boolean.FALSE.equals(message.spf) ||
Boolean.FALSE.equals(message.dmarc) ||
Boolean.FALSE.equals(message.mx) ||
Boolean.FALSE.equals(message.reply_domain));
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);
ibAuth.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivPriorityHigh.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivPriorityLow.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivImportance.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivSigned.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivEncrypted.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);
}
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
ibAuth.setVisibility(authentication && !authenticated ? View.VISIBLE : View.GONE);
ivPriorityHigh.setVisibility(
EntityMessage.PRIORITIY_HIGH.equals(message.ui_priority)
? View.VISIBLE : View.GONE);
ivPriorityLow.setVisibility(
EntityMessage.PRIORITIY_LOW.equals(message.ui_priority)
? View.VISIBLE : View.GONE);
ivImportance.setImageLevel(
EntityMessage.PRIORITIY_HIGH.equals(message.ui_importance) ? 0 : 1);
ivImportance.setVisibility(
EntityMessage.PRIORITIY_LOW.equals(message.ui_importance) ||
EntityMessage.PRIORITIY_HIGH.equals(message.ui_importance)
? View.VISIBLE : View.GONE);
ivSigned.setVisibility(message.signed > 0 ? View.VISIBLE : View.GONE);
if (message.verified)
ivSigned.setColorFilter(colorEncrypt);
else
ivSigned.clearColorFilter();
ivEncrypted.setVisibility(message.encrypted > 0 ? View.VISIBLE : View.GONE);
if (show_recipients && recipients != null && recipients.length > 0)
tvFrom.setText(context.getString(outgoing && viewType != ViewType.THREAD && compact
? R.string.title_to_from
: R.string.title_from_to,
MessageHelper.formatAddresses(senders, name_email, false),
MessageHelper.formatAddresses(recipients, name_email, false)));
else
tvFrom.setText(MessageHelper.formatAddresses(senders, name_email, 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);
tvTime.setText(date && FragmentMessages.SORT_DATE_HEADER.contains(sort)
? TF.format(message.received)
: Helper.getRelativeTimeSpanString(context, message.received));
// Line 2
tvSubject.setText(message.subject);
// Workaround layout bug
tvSubject.requestLayout();
tvSubject.invalidate();
if (keywords_header) {
SpannableStringBuilder keywords = getKeywords(message);
tvKeywords.setVisibility(keywords.length() > 0 ? View.VISIBLE : View.GONE);
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.attachments > 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);
tvLabels.setText(message.labels == null ? null : TextUtils.join(", ", message.labels));
tvLabels.setVisibility(
labels_header && message.labels != null && message.labels.length > 0
? View.VISIBLE : View.GONE);
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(
monospaced ? Typeface.MONOSPACE : Typeface.DEFAULT,
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.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 + " " + 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 +
"\nhash=" + message.hash +
"\nmsgid=" + message.msgid + "/" + message.uidl +
"\nthread=" + message.thread +
"\nsender=" + message.sender;
tvError.setText(text);
tvError.setVisibility(View.VISIBLE);
} else {
tvError.setText(error);
tvError.setVisibility(error == null ? View.GONE : View.VISIBLE);
ibHelp.setVisibility(error == null ? View.GONE : View.VISIBLE);
}
// Contact info
ContactInfo[] info = ContactInfo.getCached(context, message.account, message.folderType, addresses);
if (info == null) {
if (taskContactInfo != null)
taskContactInfo.cancel(context);
Bundle aargs = new Bundle();
aargs.putLong("id", message.id);
aargs.putLong("account", message.account);
aargs.putString("folderType", message.folderType);
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");
Address[] addresses = (Address[]) args.getSerializable("addresses");
return ContactInfo.get(context, account, folderType, 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 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 (properties.getValue("raw_save", message.id)) {
properties.setValue("raw_save", message.id, false);
onMenuRawSave(message);
}
if (properties.getValue("raw_send", message.id)) {
properties.setValue("raw_send", message.id, false);
onMenuRawSend(message);
}
}
private void clearExpanded(TupleMessageEx message) {
if (compact) {
if (tvFrom.getMaxLines() > 1)
tvFrom.setSingleLine(true);
boolean full = "full".equals(subject_ellipsize);
if (full ? tvSubject.getMaxLines() == 1 : tvSubject.getMaxLines() > 1)
tvSubject.setSingleLine(!full);
}
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);
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);
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);
tvSubjectEx.setVisibility(View.GONE);
tvFlags.setVisibility(View.GONE);
tvKeywordsEx.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);
ibDecrypt.setVisibility(View.GONE);
ibVerify.setVisibility(View.GONE);
ibUndo.setVisibility(View.GONE);
ibRule.setVisibility(View.GONE);
ibUnsubscribe.setVisibility(View.GONE);
ibPrint.setVisibility(View.GONE);
ibShare.setVisibility(View.GONE);
ibEvent.setVisibility(View.GONE);
ibSearch.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);
tvReformatted.setVisibility(View.GONE);
tvSignedData.setVisibility(View.GONE);
tvNoInternetBody.setVisibility(View.GONE);
grpDownloading.setVisibility(View.GONE);
tvBody.setText(null);
tvBody.setVisibility(View.GONE);
// TO DO: clear web view?
wvBody.setVisibility(View.GONE);
pbBody.setVisibility(View.GONE);
grpAction.setVisibility(View.GONE);
clearActions();
ibTrashBottom.setVisibility(View.GONE);
ibArchiveBottom.setVisibility(View.GONE);
ibSeenBottom.setVisibility(View.GONE);
}
private void clearActions() {
ConstraintLayout cl = (ConstraintLayout) flow.getParent();
for (int id : flow.getReferencedIds()) {
View v = cl.findViewById(id);
flow.removeView(v);
cl.removeView(v);
}
}
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(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);
if (flags)
ibFlagged.setVisibility(message.folderReadOnly ? View.INVISIBLE : View.VISIBLE);
else
ibFlagged.setVisibility(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);
} else {
ibAvatar.setImageBitmap(main.getPhotoBitmap());
Uri lookupUri = main.getLookupUri();
ibAvatar.setTag(lookupUri);
ibAvatar.setEnabled(lookupUri != null);
}
ibAvatar.setVisibility(main == null || !main.hasPhoto() ? View.GONE : View.VISIBLE);
}
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 ? "?" : 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);
}
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.GONE);
ibDecrypt.setVisibility(View.GONE);
ibVerify.setVisibility(View.GONE);
ibUndo.setVisibility(View.GONE);
ibRule.setVisibility(View.GONE);
ibUnsubscribe.setVisibility(View.GONE);
ibPrint.setVisibility(View.GONE);
ibShare.setVisibility(View.GONE);
ibEvent.setVisibility(View.GONE);
ibSearch.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);
tvReformatted.setVisibility(View.GONE);
tvSignedData.setVisibility(View.GONE);
// Message text
tvNoInternetBody.setVisibility(suitable || message.content ? View.GONE : View.VISIBLE);
grpDownloading.setVisibility(message.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);
}
}
pbBody.setVisibility(View.GONE);
clearCalendar();
grpCalendar.setVisibility(View.GONE);
grpCalendarResponse.setVisibility(View.GONE);
grpAction.setVisibility(View.GONE);
clearActions();
ibTrashBottom.setVisibility(View.GONE);
ibArchiveBottom.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 if ("message/rfc822".equals(attachment.getMimeType()))
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 if ("message/rfc822".equals(attachment.getMimeType()))
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);
}
});
// 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;
boolean hasJunk = 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;
else if (EntityFolder.JUNK.equals(folder.type))
hasJunk = true;
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) ||
(message.accountProtocol == EntityAccount.TYPE_POP &&
EntityFolder.TRASH.equals(message.folderType));
boolean archive = (move && (hasArchive && !inArchive && !inSent && !inTrash && !inJunk));
boolean trash = (move || outbox || debug ||
message.accountProtocol == EntityAccount.TYPE_POP);
boolean junk = (move && hasJunk && hasInbox);
boolean inbox = (move && hasInbox && (inArchive || inTrash || inJunk));
boolean keywords = (!message.folderReadOnly && message.uid != null &&
message.accountProtocol == EntityAccount.TYPE_IMAP);
boolean labels = (data.isGmail && move && !inTrash && !inJunk && !outbox);
boolean seen = (!(message.folderReadOnly || message.uid == null) ||
message.accountProtocol == EntityAccount.TYPE_POP);
int froms = (message.from == null ? 0 : message.from.length);
int tos = (message.to == null ? 0 : message.to.length);
final boolean delete = (inTrash || !hasTrash || inJunk || outbox ||
message.uid == null || message.accountProtocol == EntityAccount.TYPE_POP);
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_search = prefs.getBoolean("button_search", false);
boolean button_event = prefs.getBoolean("button_event", false);
boolean button_share = prefs.getBoolean("button_share", false);
boolean button_print = prefs.getBoolean("button_print", false);
boolean button_unsubscribe = prefs.getBoolean("button_unsubscribe", true);
boolean button_rule = prefs.getBoolean("button_rule", false);
ibSeen.setImageResource(message.ui_seen ? R.drawable.twotone_visibility_off_24 : R.drawable.twotone_visibility_24);
ibTrash.setTag(delete);
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);
ibPrint.setVisibility(tools && button_print && hasWebView && message.content && Helper.canPrint(context) ? View.VISIBLE : View.GONE);
ibShare.setVisibility(tools && button_share && message.content ? View.VISIBLE : View.GONE);
ibEvent.setVisibility(tools && button_event && message.content ? View.VISIBLE : View.GONE);
ibSearch.setVisibility(tools && button_search && (froms > 0 || tos > 0) ? View.VISIBLE : View.GONE);
ibSeen.setVisibility(tools && button_seen && !outbox && seen ? View.VISIBLE : View.GONE);
ibAnswer.setVisibility(!tools || outbox || (!expand_all && expand_one) ? View.GONE : View.VISIBLE);
ibNotes.setVisibility(tools && button_notes ? 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 && junk ? 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.setVisibility(outbox ? View.GONE : View.VISIBLE);
ibTrashBottom.setVisibility(button_trash && trash ? View.VISIBLE : View.GONE);
ibArchiveBottom.setVisibility(button_archive && archive ? View.VISIBLE : View.GONE);
if (bind)
bindBody(message, scroll);
}
@Override
protected void onException(Bundle args, Throwable ex) {
Log.unexpectedError(parentFragment.getParentFragmentManager(), ex);
}
}.setLog(false).execute(context, owner, sargs, "message:tools");
}
private Spanned formatAddresses(Address[] addresses, boolean full) {
SpannableStringBuilder ssb = new SpannableStringBuilder();
if (addresses == null || addresses.length == 0)
return ssb;
for (int i = 0; i < addresses.length; 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 (email != null) {
int start = ssb.length();
ssb.append(email);
ssb.setSpan(new ForegroundColorSpan(textColorLink), start, ssb.length(), 0);
}
} else {
if (full) {
ssb.append(personal).append(" <");
if (email != null) {
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());
}
return ssb;
}
private void bindAddresses(TupleMessageEx message) {
boolean show_addresses = properties.getValue("addresses", message.id);
boolean full = (show_addresses || 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);
Spanned submitter = formatAddresses(message.submitter, true);
Spanned from = formatAddresses(message.senders, true);
Spanned to = formatAddresses(message.to, full);
Spanned replyto = formatAddresses(message.reply, true);
Spanned cc = formatAddresses(message.cc, full);
Spanned bcc = formatAddresses(message.bcc, full);
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.plain_only != null && message.plain_only ? 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);
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 && 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) && !TextUtils.isEmpty(to) ? View.VISIBLE : View.GONE);
tvTo.setVisibility((!show_recipients || show_addresses) && !TextUtils.isEmpty(to) ? View.VISIBLE : View.GONE);
if (show_addresses || (message.to == null || message.to.length < MAX_RECIPIENTS))
tvTo.setText(to);
else
tvTo.setText(context.getString(R.string.title_recipients, message.to.length));
tvReplyToTitle.setVisibility(show_addresses && !TextUtils.isEmpty(replyto) ? View.VISIBLE : View.GONE);
tvReplyTo.setVisibility(show_addresses && !TextUtils.isEmpty(replyto) ? View.VISIBLE : View.GONE);
tvReplyTo.setText(replyto);
tvCcTitle.setVisibility(!TextUtils.isEmpty(cc) ? View.VISIBLE : View.GONE);
tvCc.setVisibility(!TextUtils.isEmpty(cc) ? View.VISIBLE : View.GONE);
if (show_addresses || (message.cc == null || message.cc.length < MAX_RECIPIENTS))
tvCc.setText(cc);
else
tvCc.setText(context.getString(R.string.title_recipients, message.cc.length));
tvBccTitle.setVisibility(!TextUtils.isEmpty(bcc) ? View.VISIBLE : View.GONE);
tvBcc.setVisibility(!TextUtils.isEmpty(bcc) ? View.VISIBLE : View.GONE);
if (show_addresses || (message.bcc == null || message.bcc.length < MAX_RECIPIENTS))
tvBcc.setText(bcc);
else
tvBcc.setText(context.getString(R.string.title_recipients, message.bcc.length));
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 && (debug || BuildConfig.DEBUG) ? View.VISIBLE : View.GONE);
tvStored.setVisibility(show_addresses && (debug || BuildConfig.DEBUG) ? 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());
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 {
SpannableStringBuilder keywords = getKeywords(message);
tvKeywordsEx.setVisibility(show_addresses && keywords.length() > 0 ? View.VISIBLE : View.GONE);
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)
tvHeaders.setText(HtmlHelper.highlightHeaders(context, message.headers));
else
tvHeaders.setText(null);
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 bindBody(TupleMessageEx message, final boolean scroll) {
if (!Objects.equals(tvBody.getTag(), message.id)) {
tvBody.setTag(message.id);
tvBody.setText(null);
}
clearActions();
ibSeenBottom.setImageResource(message.ui_seen
? R.drawable.twotone_visibility_off_24 : R.drawable.twotone_visibility_24);
ibSeenBottom.setVisibility(!(message.folderReadOnly || message.uid == null) ||
message.accountProtocol == EntityAccount.TYPE_POP
? View.VISIBLE : View.GONE);
if (!message.content) {
if (scroll)
properties.scrollTo(getAdapterPosition(), 0);
return;
}
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 (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 (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);
}
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 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);
ibImages.setImageResource(show_images ? R.drawable.twotone_article_24 : R.drawable.twotone_image_24);
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);
webView.init(height, size, position,
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())
properties.scrollBy(0, 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(ViewHolder.this);
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(monospaced ? Typeface.MONOSPACE : Typeface.DEFAULT);
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