Files
FairEmail/app/src/main/java/eu/faircode/email/AdapterMessage.java

4435 lines
195 KiB
Java
Raw Normal View History

2018-08-02 13:33:06 +00:00
package eu.faircode.email;
/*
2018-08-14 05:53:24 +00:00
This file is part of FairEmail.
2018-08-02 13:33:06 +00:00
2018-08-14 05:53:24 +00:00
FairEmail is free software: you can redistribute it and/or modify
2018-08-02 13:33:06 +00:00
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.
2018-10-29 10:46:49 +00:00
FairEmail is distributed in the hope that it will be useful,
2018-08-02 13:33:06 +00:00
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
2018-10-29 10:46:49 +00:00
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
2018-08-02 13:33:06 +00:00
2018-12-31 08:04:33 +00:00
Copyright 2018-2019 by Marcel Bokhorst (M66B)
2018-08-02 13:33:06 +00:00
*/
2018-09-08 14:57:03 +00:00
import android.Manifest;
2019-08-16 14:31:31 +02:00
import android.animation.Animator;
2019-01-27 11:17:45 +00:00
import android.annotation.SuppressLint;
2019-03-07 12:29:03 +00:00
import android.annotation.TargetApi;
2019-04-19 08:31:00 +02:00
import android.app.Dialog;
2019-03-07 12:29:03 +00:00
import android.app.Notification;
import android.app.NotificationChannel;
2018-11-19 18:10:55 +01:00
import android.app.NotificationManager;
2018-09-08 14:57:03 +00:00
import android.content.ContentResolver;
2018-08-02 13:33:06 +00:00
import android.content.Context;
2018-09-19 10:00:58 +00:00
import android.content.DialogInterface;
2018-08-02 13:33:06 +00:00
import android.content.Intent;
import android.content.SharedPreferences;
2018-09-08 14:57:03 +00:00
import android.content.pm.PackageManager;
2018-12-10 19:27:04 +01:00
import android.content.res.ColorStateList;
2018-10-15 10:05:42 +00:00
import android.database.Cursor;
2018-09-12 16:54:48 +00:00
import android.graphics.Color;
2019-10-02 13:36:07 +02:00
import android.graphics.Paint;
2019-01-12 14:08:00 +00:00
import android.graphics.Rect;
2018-08-02 13:33:06 +00:00
import android.graphics.Typeface;
2019-09-07 20:13:58 +02:00
import android.graphics.drawable.AnimatedImageDrawable;
2018-09-08 14:57:03 +00:00
import android.graphics.drawable.Drawable;
import android.net.Uri;
2019-03-07 12:29:03 +00:00
import android.os.Build;
2018-09-01 06:27:13 +00:00
import android.os.Bundle;
2019-06-26 12:58:36 +02:00
import android.os.Handler;
2019-09-24 11:21:31 +02:00
import android.os.Looper;
2019-09-30 13:40:14 +02:00
import android.provider.CalendarContract;
2018-09-08 14:57:03 +00:00
import android.provider.ContactsContract;
2019-03-07 12:29:03 +00:00
import android.provider.Settings;
2019-06-28 17:06:13 +02:00
import android.text.Editable;
2018-10-15 10:05:42 +00:00
import android.text.Html;
import android.text.Layout;
import android.text.Spannable;
2018-12-20 21:12:28 +01:00
import android.text.SpannableStringBuilder;
2018-10-15 10:05:42 +00:00
import android.text.Spanned;
import android.text.TextUtils;
2019-06-28 17:06:13 +02:00
import android.text.TextWatcher;
2019-08-09 18:42:36 +02:00
import android.text.format.DateUtils;
2019-02-09 10:41:28 +00:00
import android.text.method.ArrowKeyMovementMethod;
2019-07-07 11:11:31 +02:00
import android.text.method.LinkMovementMethod;
2019-05-02 13:29:05 +02:00
import android.text.style.DynamicDrawableSpan;
2018-10-15 10:05:42 +00:00
import android.text.style.ImageSpan;
2018-12-20 21:12:28 +01:00
import android.text.style.QuoteSpan;
2018-10-15 10:05:42 +00:00
import android.text.style.URLSpan;
2019-10-04 18:59:37 +02:00
import android.util.Pair;
2018-12-30 16:17:22 +00:00
import android.util.TypedValue;
2019-08-10 10:42:28 +02:00
import android.view.KeyEvent;
2018-08-02 13:33:06 +00:00
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
2018-10-15 10:05:42 +00:00
import android.view.MotionEvent;
2019-09-01 15:42:53 +02:00
import android.view.ScaleGestureDetector;
2019-01-12 14:08:00 +00:00
import android.view.TouchDelegate;
2018-08-02 13:33:06 +00:00
import android.view.View;
2019-08-16 14:31:31 +02:00
import android.view.ViewAnimationUtils;
2019-06-26 12:58:36 +02:00
import android.view.ViewConfiguration;
2018-08-02 13:33:06 +00:00
import android.view.ViewGroup;
2019-05-30 19:46:58 +02:00
import android.view.ViewStub;
2019-08-16 14:31:31 +02:00
import android.view.animation.AccelerateDecelerateInterpolator;
2019-02-03 09:38:48 +00:00
import android.webkit.DownloadListener;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
2018-10-15 10:05:42 +00:00
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
2018-10-15 10:05:42 +00:00
import android.widget.EditText;
2018-12-21 15:19:07 +01:00
import android.widget.ImageButton;
import android.widget.ImageView;
2018-08-02 13:33:06 +00:00
import android.widget.TextView;
2019-08-13 10:27:17 +02:00
import android.widget.Toast;
2018-08-02 13:33:06 +00:00
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
2019-06-30 20:56:31 +02:00
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Group;
2019-09-09 14:06:43 +02:00
import androidx.core.content.FileProvider;
import androidx.core.graphics.ColorUtils;
2019-06-30 16:55:15 +02:00
import androidx.fragment.app.Fragment;
2019-04-19 08:31:00 +02:00
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.Observer;
2019-04-19 08:31:00 +02:00
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.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;
2019-07-06 12:52:00 +02:00
import com.github.chrisbanes.photoview.PhotoView;
2019-08-16 08:12:04 +02:00
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.bottomnavigation.LabelVisibilityMode;
2018-10-24 10:34:02 +00:00
import com.google.android.material.snackbar.Snackbar;
2018-10-15 10:05:42 +00:00
2019-02-03 09:38:48 +00:00
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
2018-10-15 10:05:42 +00:00
import java.io.File;
2018-10-15 10:05:42 +00:00
import java.io.IOException;
import java.io.UnsupportedEncodingException;
2019-09-24 11:21:31 +02:00
import java.lang.reflect.Field;
2019-08-13 10:27:17 +02:00
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
2019-03-10 08:02:17 +00:00
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
2018-10-15 10:05:42 +00:00
import java.util.ArrayList;
2018-11-26 12:42:06 +01:00
import java.util.Arrays;
2018-10-15 10:05:42 +00:00
import java.util.Collections;
import java.util.Date;
2019-08-11 16:08:53 +02:00
import java.util.HashMap;
import java.util.List;
2019-09-23 22:07:22 +02:00
import java.util.Locale;
2019-08-11 16:08:53 +02:00
import java.util.Map;
2019-06-25 09:46:31 +02:00
import java.util.Objects;
2019-09-24 11:21:31 +02:00
import java.util.concurrent.Executor;
2018-10-15 10:05:42 +00:00
import javax.mail.Address;
import javax.mail.internet.InternetAddress;
2019-05-18 11:19:39 +02:00
import biweekly.Biweekly;
import biweekly.ICalendar;
import biweekly.component.VEvent;
2019-05-18 19:44:21 +02:00
import biweekly.parameter.ParticipationStatus;
2019-05-18 11:19:39 +02:00
import biweekly.property.Attendee;
2019-05-18 19:44:21 +02:00
import biweekly.property.Method;
import biweekly.property.Organizer;
2019-05-18 11:19:39 +02:00
import biweekly.util.ICalDate;
2019-06-30 21:43:48 +02:00
import static android.app.Activity.RESULT_OK;
2018-12-03 08:21:02 +01:00
public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHolder> {
2019-06-30 16:55:15 +02:00
private Fragment parentFragment;
2019-07-19 08:27:44 +02:00
private String type;
2018-08-06 13:36:55 +00:00
private ViewType viewType;
2019-01-15 17:39:12 +00:00
private boolean compact;
2018-12-30 16:33:52 +00:00
private int zoom;
2019-01-21 18:12:22 +00:00
private String sort;
2019-09-03 10:33:52 +02:00
private boolean ascending;
2019-04-28 15:16:26 +02:00
private boolean filter_duplicates;
private IProperties properties;
private Context context;
private LifecycleOwner owner;
private LayoutInflater inflater;
private boolean suitable;
private int dp36;
private int colorPrimary;
private int colorAccent;
private int textColorPrimary;
private int textColorSecondary;
private int colorUnread;
2019-10-02 14:07:55 +02:00
private int colorSeparator;
private boolean hasWebView;
private boolean contacts;
private float textSize;
private boolean date;
private boolean threading;
private boolean name_email;
2019-10-03 10:19:27 +02:00
private boolean distinguish_contacts;
private boolean subject_top;
private boolean subject_italic;
private String subject_ellipsize;
2019-03-02 15:54:38 +00:00
private boolean flags;
private boolean flags_background;
2018-11-04 12:42:41 +00:00
private boolean preview;
private boolean preview_italic;
private boolean attachments_alt;
private boolean contrast;
private boolean monospaced;
2019-10-04 15:25:04 +02:00
private boolean inline;
private boolean collapse_quotes;
private boolean authentication;
2019-06-25 09:46:31 +02:00
private static boolean debug;
2018-09-08 17:04:10 +00:00
private int answers = -1;
2019-05-15 13:37:35 +02:00
private boolean gotoTop = false;
2019-09-29 18:34:12 +02:00
private boolean firstClick = false;
2019-05-04 10:24:42 +02:00
private AsyncPagedListDiffer<TupleMessageEx> differ;
2019-08-11 16:08:53 +02:00
private Map<Long, Integer> keyPosition = new HashMap<>();
2018-10-24 09:09:07 +00:00
private SelectionTracker<Long> selectionTracker = null;
2018-08-02 13:33:06 +00:00
2018-08-26 12:17:09 +00:00
enum ViewType {UNIFIED, FOLDER, THREAD, SEARCH}
2018-08-02 13:33:06 +00:00
private NumberFormat NF = NumberFormat.getNumberInstance();
private DateFormat TF;
2019-07-15 21:28:25 +02:00
private DateFormat DTF;
2018-12-03 08:21:02 +01:00
2019-07-29 22:27:55 +02:00
// https://github.com/newhouse/url-tracking-stripper
2019-04-17 13:37:14 +02:00
private static final List<String> PARANOID_QUERY = Collections.unmodifiableList(Arrays.asList(
"utm_source",
"utm_medium",
"utm_campaign",
"utm_term",
2019-07-29 22:27:55 +02:00
"utm_content",
"utm_name",
"utm_cid",
"utm_reader",
"utm_viz_id",
"utm_pubreferrer",
"utm_swu",
"gclid",
"fbclid"
2019-04-17 13:37:14 +02:00
));
2018-10-15 10:05:42 +00:00
public class ViewHolder extends RecyclerView.ViewHolder implements
2019-09-01 15:42:53 +02:00
View.OnKeyListener,
View.OnClickListener,
View.OnLongClickListener,
View.OnTouchListener,
2019-10-04 15:25:04 +02:00
View.OnLayoutChangeListener,
2019-09-01 15:42:53 +02:00
BottomNavigationView.OnNavigationItemSelectedListener {
private ViewCardOptional card;
2019-03-15 11:53:22 +00:00
private View view;
2019-08-14 13:18:42 +02:00
2019-08-14 17:21:37 +02:00
private View vwColor;
2019-09-01 19:19:50 +02:00
private ImageButton ibExpander;
2019-09-01 19:07:21 +02:00
private ImageView ibFlagged;
2018-10-15 10:05:42 +00:00
private ImageView ivAvatar;
private TextView tvFrom;
private TextView tvSize;
2018-10-15 10:05:42 +00:00
private TextView tvTime;
private ImageView ivType;
2019-09-30 16:55:58 +02:00
private ImageView ivPriority;
2019-09-01 19:07:21 +02:00
private ImageView ibAuth;
private ImageView ibSnoozed;
2019-01-19 16:14:40 +00:00
private ImageView ivBrowsed;
2018-11-24 19:14:28 +01:00
private ImageView ivAnswered;
2019-05-04 21:31:14 +02:00
private ImageView ivPlain;
private ImageView ivReceipt;
2018-10-15 10:05:42 +00:00
private ImageView ivAttachments;
private TextView tvSubject;
private TextView tvFolder;
private TextView tvCount;
private ImageView ivThread;
2018-11-04 16:16:45 +00:00
private TextView tvPreview;
2018-10-15 10:05:42 +00:00
private TextView tvError;
2019-09-13 09:34:00 +02:00
private ImageButton ibHelp;
2018-12-27 12:31:02 +00:00
private ContentLoadingProgressBar pbLoading;
2018-10-15 10:05:42 +00:00
2019-05-30 19:46:58 +02:00
private View vsBody;
2019-09-01 18:59:51 +02:00
private ImageButton ibExpanderAddress;
2019-03-09 10:02:17 +00:00
2019-05-08 16:44:01 +02:00
private ImageButton ibSearchContact;
private ImageButton ibNotifyContact;
private ImageButton ibAddContact;
2019-03-09 10:02:17 +00:00
private TextView tvFromExTitle;
private TextView tvToTitle;
private TextView tvReplyToTitle;
private TextView tvCcTitle;
private TextView tvBccTitle;
private TextView tvIdentityTitle;
private TextView tvTimeExTitle;
private TextView tvSizeExTitle;
private TextView tvFromEx;
2018-10-15 10:05:42 +00:00
private TextView tvTo;
private TextView tvReplyTo;
private TextView tvCc;
private TextView tvBcc;
private TextView tvIdentity;
2018-11-29 19:42:40 +01:00
private TextView tvTimeEx;
private TextView tvSizeEx;
2019-03-09 10:02:17 +00:00
2018-10-15 10:05:42 +00:00
private TextView tvSubjectEx;
2019-01-29 20:15:24 +00:00
private TextView tvFlags;
2018-11-25 18:29:11 +01:00
private TextView tvKeywords;
2018-10-15 10:05:42 +00:00
private TextView tvHeaders;
2018-12-27 12:31:02 +00:00
private ContentLoadingProgressBar pbHeaders;
private TextView tvNoInternetHeaders;
2018-10-15 10:05:42 +00:00
private RecyclerView rvAttachment;
private CheckBox cbInline;
2019-01-05 14:56:19 +00:00
private Button btnSaveAttachments;
2019-08-15 15:01:26 +02:00
private Button btnDownloadAttachments;
private TextView tvNoInternetAttachments;
2019-08-16 08:12:04 +02:00
private BottomNavigationView bnvActions;
2018-10-18 07:56:31 +00:00
private ImageButton ibFull;
2019-08-14 20:08:24 +02:00
private ImageButton ibImages;
2019-09-08 12:57:21 +02:00
private ImageButton ibUnsubscribe;
2019-08-14 20:08:24 +02:00
private ImageButton ibDecrypt;
2018-10-15 10:05:42 +00:00
private TextView tvBody;
2019-10-04 15:25:04 +02:00
private View wvBody;
private ContentLoadingProgressBar pbBody;
private TextView tvNoInternetBody;
2018-10-15 10:05:42 +00:00
2019-05-18 11:19:39 +02:00
private TextView tvCalendarSummary;
private TextView tvCalendarStart;
private TextView tvCalendarEnd;
private TextView tvAttendees;
2019-05-18 19:44:21 +02:00
private Button btnCalendarAccept;
private Button btnCalendarDecline;
private Button btnCalendarMaybe;
2019-09-30 13:40:14 +02:00
private ImageButton ibCalendar;
2019-05-18 11:19:39 +02:00
private ContentLoadingProgressBar pbCalendarWait;
2019-01-06 09:23:57 +00:00
private RecyclerView rvImage;
2019-05-17 18:54:44 +02:00
private Group grpAddresses;
2018-10-15 10:05:42 +00:00
private Group grpHeaders;
2019-05-18 11:19:39 +02:00
private Group grpCalendar;
2019-05-18 19:44:21 +02:00
private Group grpCalendarResponse;
2018-10-15 10:05:42 +00:00
private Group grpAttachments;
2019-05-13 20:44:08 +02:00
private Group grpImages;
2019-01-06 09:23:57 +00:00
private AdapterAttachment adapterAttachment;
private AdapterImage adapterImage;
2019-05-03 18:59:27 +02:00
private TwoStateOwner cowner = new TwoStateOwner(owner, "MessageAttachments");
private TwoStateOwner powner = new TwoStateOwner(owner, "MessagePopup");
2018-12-05 11:50:07 +01:00
2019-08-14 20:08:24 +02:00
private boolean hasJunk;
private boolean delete;
2019-09-01 15:42:53 +02:00
private ScaleGestureDetector gestureDetector;
2019-01-12 14:08:00 +00:00
ViewHolder(final View itemView) {
2018-08-02 13:33:06 +00:00
super(itemView);
2019-08-16 14:31:31 +02:00
card = itemView.findViewById(R.id.card);
2019-03-15 11:53:22 +00:00
view = itemView.findViewById(R.id.clItem);
2018-09-12 16:54:48 +00:00
vwColor = itemView.findViewById(R.id.vwColor);
2019-09-01 19:19:50 +02:00
ibExpander = itemView.findViewById(R.id.ibExpander);
2019-09-01 19:07:21 +02:00
ibFlagged = itemView.findViewById(R.id.ibFlagged);
ivAvatar = itemView.findViewById(R.id.ivAvatar);
tvFrom = itemView.findViewById(subject_top ? R.id.tvSubject : R.id.tvFrom);
tvSize = itemView.findViewById(R.id.tvSize);
2018-08-02 13:33:06 +00:00
tvTime = itemView.findViewById(R.id.tvTime);
ivType = itemView.findViewById(R.id.ivType);
2019-09-30 16:55:58 +02:00
ivPriority = itemView.findViewById(R.id.ivPriority);
2019-09-01 19:07:21 +02:00
ibAuth = itemView.findViewById(R.id.ibAuth);
ibSnoozed = itemView.findViewById(R.id.ibSnoozed);
2019-01-19 16:14:40 +00:00
ivBrowsed = itemView.findViewById(R.id.ivBrowsed);
2018-11-24 19:14:28 +01:00
ivAnswered = itemView.findViewById(R.id.ivAnswered);
2019-05-04 21:31:14 +02:00
ivPlain = itemView.findViewById(R.id.ivPlain);
ivReceipt = itemView.findViewById(R.id.ivReceipt);
ivAttachments = itemView.findViewById(R.id.ivAttachments);
tvSubject = itemView.findViewById(subject_top ? R.id.tvFrom : R.id.tvSubject);
2018-11-04 12:42:41 +00:00
tvPreview = itemView.findViewById(R.id.tvPreview);
2018-08-22 16:37:10 +00:00
tvFolder = itemView.findViewById(R.id.tvFolder);
2018-08-02 13:33:06 +00:00
tvCount = itemView.findViewById(R.id.tvCount);
2018-09-04 07:56:30 +00:00
ivThread = itemView.findViewById(R.id.ivThread);
tvError = itemView.findViewById(R.id.tvError);
2019-09-13 09:34:00 +02:00
ibHelp = itemView.findViewById(R.id.ibHelp);
2018-08-07 06:38:00 +00:00
pbLoading = itemView.findViewById(R.id.pbLoading);
2019-10-01 13:56:48 +02:00
if (compact && tvSubject != null)
if ("start".equals(subject_ellipsize))
tvSubject.setEllipsize(TextUtils.TruncateAt.START);
else if ("end".equals(subject_ellipsize))
tvSubject.setEllipsize(TextUtils.TruncateAt.END);
else
tvSubject.setEllipsize(TextUtils.TruncateAt.MIDDLE);
2019-05-30 19:46:58 +02:00
}
2018-10-15 10:05:42 +00:00
2019-05-30 19:46:58 +02:00
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);
2019-09-01 18:59:51 +02:00
ibExpanderAddress = vsBody.findViewById(R.id.ibExpanderAddress);
2019-05-30 19:46:58 +02:00
ibSearchContact = vsBody.findViewById(R.id.ibSearchContact);
ibNotifyContact = vsBody.findViewById(R.id.ibNotifyContact);
ibAddContact = vsBody.findViewById(R.id.ibAddContact);
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);
tvTimeExTitle = vsBody.findViewById(R.id.tvTimeExTitle);
tvSizeExTitle = vsBody.findViewById(R.id.tvSizeExTitle);
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);
tvTimeEx = vsBody.findViewById(R.id.tvTimeEx);
tvSizeEx = vsBody.findViewById(R.id.tvSizeEx);
tvSubjectEx = vsBody.findViewById(R.id.tvSubjectEx);
tvFlags = vsBody.findViewById(R.id.tvFlags);
tvKeywords = vsBody.findViewById(R.id.tvKeywords);
tvHeaders = vsBody.findViewById(R.id.tvHeaders);
pbHeaders = vsBody.findViewById(R.id.pbHeaders);
tvNoInternetHeaders = vsBody.findViewById(R.id.tvNoInternetHeaders);
tvCalendarSummary = vsBody.findViewById(R.id.tvCalendarSummary);
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);
2019-09-30 13:40:14 +02:00
ibCalendar = vsBody.findViewById(R.id.ibCalendar);
2019-05-30 19:46:58 +02:00
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);
2019-05-30 19:46:58 +02:00
rvAttachment.setAdapter(adapterAttachment);
cbInline = attachments.findViewById(R.id.cbInline);
btnSaveAttachments = attachments.findViewById(R.id.btnSaveAttachments);
2019-08-15 15:01:26 +02:00
btnDownloadAttachments = attachments.findViewById(R.id.btnDownloadAttachments);
2019-05-30 19:46:58 +02:00
tvNoInternetAttachments = attachments.findViewById(R.id.tvNoInternetAttachments);
2019-08-16 08:12:04 +02:00
bnvActions = vsBody.findViewById(R.id.bnvActions);
if (compact) {
bnvActions.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_UNLABELED);
ViewGroup.LayoutParams lparam = bnvActions.getLayoutParams();
lparam.height = dp36;
bnvActions.setLayoutParams(lparam);
}
2019-05-30 19:46:58 +02:00
ibFull = vsBody.findViewById(R.id.ibFull);
2019-08-14 20:08:24 +02:00
ibImages = vsBody.findViewById(R.id.ibImages);
2019-09-08 12:57:21 +02:00
ibUnsubscribe = vsBody.findViewById(R.id.ibUnsubscribe);
2019-08-14 20:08:24 +02:00
ibDecrypt = vsBody.findViewById(R.id.ibDecrypt);
2019-05-30 19:46:58 +02:00
tvBody = vsBody.findViewById(R.id.tvBody);
2019-10-04 15:25:04 +02:00
wvBody = vsBody.findViewById(R.id.wvBody);
pbBody = vsBody.findViewById(R.id.pbBody);
2019-05-30 19:46:58 +02:00
tvNoInternetBody = vsBody.findViewById(R.id.tvNoInternetBody);
rvImage = vsBody.findViewById(R.id.rvImage);
rvImage.setHasFixedSize(false);
StaggeredGridLayoutManager sglm =
new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
rvImage.setLayoutManager(sglm);
adapterImage = new AdapterImage(parentFragment);
2019-05-30 19:46:58 +02:00
rvImage.setAdapter(adapterImage);
grpAddresses = vsBody.findViewById(R.id.grpAddresses);
grpHeaders = vsBody.findViewById(R.id.grpHeaders);
grpCalendar = vsBody.findViewById(R.id.grpCalendar);
grpCalendarResponse = vsBody.findViewById(R.id.grpCalendarResponse);
grpAttachments = attachments.findViewById(R.id.grpAttachments);
grpImages = vsBody.findViewById(R.id.grpImages);
2019-05-30 20:32:29 +02:00
unwire();
wire();
2018-08-02 13:33:06 +00:00
}
2019-01-20 15:22:21 +00:00
Rect getItemRect() {
return new Rect(
super.itemView.getLeft(),
2019-08-14 12:32:29 +02:00
super.itemView.getTop(),
2019-01-20 15:22:21 +00:00
super.itemView.getRight(),
super.itemView.getBottom());
}
2018-08-02 13:33:06 +00:00
private void wire() {
2019-09-01 19:19:50 +02:00
final View touch = (viewType == ViewType.THREAD ? ibExpander : vwColor);
2019-01-20 11:21:09 +00:00
touch.setOnClickListener(this);
2019-03-15 11:53:22 +00:00
view.post(new Runnable() {
2019-01-20 11:21:09 +00:00
@Override
public void run() {
Rect rect = new Rect(
2019-03-15 11:53:22 +00:00
view.getLeft(),
2019-01-24 07:10:51 +00:00
vwColor.getTop(),
2019-03-15 11:53:22 +00:00
view.getRight(),
2019-01-24 07:10:51 +00:00
vwColor.getBottom());
2019-03-15 11:53:22 +00:00
view.setTouchDelegate(new TouchDelegate(rect, touch));
2019-01-20 11:21:09 +00:00
}
});
2019-08-10 10:42:28 +02:00
view.setOnKeyListener(this);
2019-01-15 16:50:32 +00:00
2019-09-01 19:07:21 +02:00
ibAuth.setOnClickListener(this);
ibSnoozed.setOnClickListener(this);
ibFlagged.setOnClickListener(this);
2019-06-28 18:43:36 +02:00
if (viewType == ViewType.THREAD)
2019-09-01 19:07:21 +02:00
ibFlagged.setOnLongClickListener(this);
2019-09-13 09:34:00 +02:00
ibHelp.setOnClickListener(this);
2019-01-15 16:50:32 +00:00
2019-05-30 19:46:58 +02:00
if (vsBody != null) {
2019-09-01 18:59:51 +02:00
ibExpanderAddress.setOnClickListener(this);
2019-05-30 15:46:00 +02:00
ibSearchContact.setOnClickListener(this);
ibNotifyContact.setOnClickListener(this);
ibAddContact.setOnClickListener(this);
2019-01-15 16:50:32 +00:00
2019-05-30 15:46:00 +02:00
btnSaveAttachments.setOnClickListener(this);
2019-08-15 15:01:26 +02:00
btnDownloadAttachments.setOnClickListener(this);
2019-01-15 16:50:32 +00:00
2019-08-16 08:12:04 +02:00
bnvActions.setOnNavigationItemSelectedListener(this);
2019-05-30 15:46:00 +02:00
ibFull.setOnClickListener(this);
2019-08-14 20:08:24 +02:00
ibImages.setOnClickListener(this);
2019-09-08 12:57:21 +02:00
ibUnsubscribe.setOnClickListener(this);
2019-08-14 20:08:24 +02:00
ibDecrypt.setOnClickListener(this);
2019-09-01 15:42:53 +02:00
tvBody.setOnTouchListener(this);
2019-10-04 15:25:04 +02:00
tvBody.addOnLayoutChangeListener(this);
2019-09-01 15:42:53 +02:00
2019-05-30 15:46:00 +02:00
btnCalendarAccept.setOnClickListener(this);
btnCalendarDecline.setOnClickListener(this);
btnCalendarMaybe.setOnClickListener(this);
2019-09-30 13:40:14 +02:00
ibCalendar.setOnClickListener(this);
2019-09-01 15:42:53 +02:00
gestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
TupleMessageEx message = getMessage();
if (message != null) {
float factor = detector.getScaleFactor();
float size = tvBody.getTextSize() * factor;
2019-09-01 17:28:09 +02:00
//Log.i("Gesture factor=" + factor + " size=" + size);
2019-09-01 15:42:53 +02:00
properties.setSize(message.id, size);
tvBody.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}
return true;
}
});
2019-05-30 15:46:00 +02:00
}
2018-08-02 13:33:06 +00:00
}
private void unwire() {
2019-09-01 19:19:50 +02:00
final View touch = (viewType == ViewType.THREAD ? ibExpander : vwColor);
2019-08-10 10:42:28 +02:00
touch.setOnClickListener(null);
view.setOnKeyListener(null);
2019-05-18 19:44:21 +02:00
2019-09-01 19:07:21 +02:00
ibAuth.setOnClickListener(null);
ibSnoozed.setOnClickListener(null);
ibFlagged.setOnClickListener(null);
2019-06-28 18:43:36 +02:00
if (viewType == ViewType.THREAD)
2019-09-01 19:07:21 +02:00
ibFlagged.setOnLongClickListener(null);
2019-09-13 09:34:00 +02:00
ibHelp.setOnClickListener(null);
2019-05-18 19:44:21 +02:00
2019-05-30 19:46:58 +02:00
if (vsBody != null) {
2019-09-01 18:59:51 +02:00
ibExpanderAddress.setOnClickListener(null);
2019-05-30 15:46:00 +02:00
ibSearchContact.setOnClickListener(null);
ibNotifyContact.setOnClickListener(null);
ibAddContact.setOnClickListener(null);
2019-05-18 19:44:21 +02:00
2019-05-30 15:46:00 +02:00
btnSaveAttachments.setOnClickListener(null);
2019-08-15 15:01:26 +02:00
btnDownloadAttachments.setOnClickListener(null);
2019-05-18 19:44:21 +02:00
2019-08-16 08:12:04 +02:00
bnvActions.setOnNavigationItemSelectedListener(null);
2019-05-30 15:46:00 +02:00
ibFull.setOnClickListener(null);
2019-08-14 20:08:24 +02:00
ibImages.setOnClickListener(null);
2019-09-08 12:57:21 +02:00
ibUnsubscribe.setOnClickListener(null);
2019-08-14 20:08:24 +02:00
ibDecrypt.setOnClickListener(null);
2019-09-01 15:42:53 +02:00
tvBody.setOnTouchListener(null);
2019-10-04 15:25:04 +02:00
tvBody.removeOnLayoutChangeListener(this);
2019-09-01 15:42:53 +02:00
2019-05-30 15:46:00 +02:00
btnCalendarAccept.setOnClickListener(null);
btnCalendarDecline.setOnClickListener(null);
btnCalendarMaybe.setOnClickListener(null);
2019-09-30 13:40:14 +02:00
ibCalendar.setOnClickListener(null);
2019-05-30 15:46:00 +02:00
}
2018-08-02 13:33:06 +00:00
}
2018-08-07 06:38:00 +00:00
private void clear() {
2018-10-15 10:05:42 +00:00
vwColor.setVisibility(View.GONE);
2019-09-01 19:19:50 +02:00
ibExpander.setVisibility(View.GONE);
2019-09-01 19:07:21 +02:00
ibFlagged.setVisibility(View.GONE);
2018-09-08 17:39:35 +00:00
ivAvatar.setVisibility(View.GONE);
2018-08-07 06:38:00 +00:00
tvFrom.setText(null);
tvSize.setText(null);
2018-08-07 06:38:00 +00:00
tvTime.setText(null);
ivType.setVisibility(View.GONE);
2019-09-30 16:55:58 +02:00
ivPriority.setVisibility(View.GONE);
2019-09-01 19:07:21 +02:00
ibAuth.setVisibility(View.GONE);
ibSnoozed.setVisibility(View.GONE);
2019-01-19 16:14:40 +00:00
ivBrowsed.setVisibility(View.GONE);
2018-11-24 19:14:28 +01:00
ivAnswered.setVisibility(View.GONE);
2019-05-04 21:31:14 +02:00
ivPlain.setVisibility(View.GONE);
ivReceipt.setVisibility(View.GONE);
2018-08-07 06:38:00 +00:00
ivAttachments.setVisibility(View.GONE);
2018-09-07 15:12:43 +00:00
tvSubject.setText(null);
2018-08-23 06:57:24 +00:00
tvFolder.setText(null);
2018-08-07 06:38:00 +00:00
tvCount.setText(null);
2018-09-04 07:56:30 +00:00
ivThread.setVisibility(View.GONE);
2018-11-04 16:16:45 +00:00
tvPreview.setVisibility(View.GONE);
2018-09-08 17:39:35 +00:00
tvError.setVisibility(View.GONE);
2019-09-13 09:34:00 +02:00
ibHelp.setVisibility(View.GONE);
pbLoading.setVisibility(View.VISIBLE);
2018-11-04 16:16:45 +00:00
2019-06-05 07:59:34 +02:00
clearExpanded(null);
2018-08-07 06:38:00 +00:00
}
2019-01-27 11:17:45 +00:00
@SuppressLint("WrongConstant")
2019-03-04 10:18:42 +00:00
private void bindTo(final TupleMessageEx message) {
pbLoading.setVisibility(View.GONE);
2019-08-11 20:11:48 +02:00
boolean inbox = EntityFolder.INBOX.equals(message.folderType);
boolean outbox = EntityFolder.OUTBOX.equals(message.folderType);
// Text size
2019-01-20 11:21:09 +00:00
if (textSize != 0) {
tvFrom.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * (message.unseen > 0 ? 1.1f : 1f));
2019-03-07 21:20:52 +00:00
tvSubject.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
2019-04-17 09:38:24 +02:00
tvPreview.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize * 0.9f);
2019-03-08 09:15:40 +00:00
2019-10-01 21:12:13 +02:00
int px = Math.round(
textSize * (message.unseen > 0 ? 1.1f : 1f) +
textSize * 0.9f +
(compact ? 0 : textSize * 0.9f));
2019-03-08 09:15:40 +00:00
ViewGroup.LayoutParams lparams = ivAvatar.getLayoutParams();
if (lparams.height != px) {
lparams.width = px;
lparams.height = px;
ivAvatar.requestLayout();
}
2019-01-20 11:21:09 +00:00
}
// Selected / disabled
2019-03-15 11:53:22 +00:00
view.setActivated(selectionTracker != null && selectionTracker.isSelected(message.id));
view.setAlpha(
(EntityFolder.OUTBOX.equals(message.folderType)
? message.identitySynchronize == null || !message.identitySynchronize
2019-09-19 17:41:26 +02:00
: message.uid == null && !message.accountPop)
? Helper.LOW_LIGHT : 1.0f);
2019-01-04 18:37:56 +00:00
// Duplicate
2018-11-27 07:27:32 +01:00
if (viewType == ViewType.THREAD) {
2019-03-07 13:26:36 +00:00
boolean dim = (message.duplicate || EntityFolder.TRASH.equals(message.folderType));
2019-09-01 19:07:21 +02:00
ibFlagged.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
2019-03-07 13:26:36 +00:00
ivAvatar.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);
2019-09-30 16:55:58 +02:00
ivPriority.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
2019-09-01 19:07:21 +02:00
ibAuth.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ibSnoozed.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
2019-03-07 13:26:36 +00:00
ivBrowsed.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivAnswered.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
2019-05-04 21:31:14 +02:00
ivPlain.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
ivReceipt.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
2019-03-07 13:26:36 +00:00
ivAttachments.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvSubject.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
tvFolder.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);
tvError.setAlpha(dim ? Helper.LOW_LIGHT : 1.0f);
2018-11-27 07:27:32 +01:00
}
// Unseen
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);
2018-12-24 18:16:01 +00:00
int colorUnseen = (message.unseen > 0 ? colorUnread : textColorSecondary);
tvFrom.setTextColor(colorUnseen);
tvSize.setTextColor(colorUnseen);
tvTime.setTextColor(colorUnseen);
2018-09-08 14:57:03 +00:00
// Account color
2019-10-02 13:48:26 +02:00
vwColor.setBackgroundColor(message.accountColor == null || !ActivityBilling.isPro(context)
2019-10-02 14:07:55 +02:00
? colorSeparator : message.accountColor);
2018-09-12 16:54:48 +00:00
// Expander
boolean expanded = (viewType == ViewType.THREAD && properties.getValue("expanded", message.id));
2019-09-01 19:19:50 +02:00
ibExpander.setImageLevel(expanded ? 0 /* less */ : 1 /* more */);
if (viewType == ViewType.THREAD)
2019-09-01 19:19:50 +02:00
ibExpander.setVisibility(EntityFolder.DRAFTS.equals(message.folderType) ? View.INVISIBLE : View.VISIBLE);
2018-11-13 10:22:41 +01:00
else
2019-09-01 19:19:50 +02:00
ibExpander.setVisibility(View.GONE);
2018-10-15 10:05:42 +00:00
// Line 1
2019-09-17 19:26:44 +02:00
boolean outgoing = false;
if (viewType != ViewType.THREAD)
if (EntityFolder.isOutgoing(message.folderType))
outgoing = true;
else if (!EntityFolder.ARCHIVE.equals(message.folderType) &&
message.identityEmail != null &&
message.from != null && message.from.length == 1 &&
message.identityEmail.equals(((InternetAddress) message.from[0]).getAddress()))
outgoing = true;
2019-08-04 13:43:37 +02:00
Address[] addresses = (outgoing ? message.to : message.senders);
tvFrom.setText(MessageHelper.formatAddresses(addresses, name_email, false));
2019-10-02 13:36:07 +02:00
tvFrom.setPaintFlags(tvFrom.getPaintFlags() & ~Paint.UNDERLINE_TEXT_FLAG);
2019-04-05 08:57:50 +02:00
Long size = ("size".equals(sort) ? message.totalSize : message.size);
tvSize.setText(size == null ? null : Helper.humanReadableByteCount(size, true));
tvSize.setVisibility(size == null || (message.content && !"size".equals(sort)) ? View.GONE : View.VISIBLE);
tvTime.setText(date && "time".equals(sort)
? TF.format(message.received)
: Helper.getRelativeTimeSpanString(context, message.received));
2018-08-07 06:38:00 +00:00
// Line 2
tvSubject.setText(message.subject);
boolean authenticated =
!(Boolean.FALSE.equals(message.dkim) ||
Boolean.FALSE.equals(message.spf) ||
Boolean.FALSE.equals(message.dmarc) ||
Boolean.FALSE.equals(message.mx));
// Line 3
if (outgoing && !EntityFolder.SENT.equals(message.folderType)) {
ivType.setImageResource(EntityFolder.getIcon(EntityFolder.SENT));
ivType.setVisibility(View.VISIBLE);
} else {
ivType.setImageResource(message.drafts > 0
? R.drawable.baseline_edit_24 : EntityFolder.getIcon(message.folderType));
ivType.setVisibility(message.drafts > 0 ||
(viewType == ViewType.UNIFIED && type == null && !inbox) ||
(viewType == ViewType.THREAD && EntityFolder.SENT.equals(message.folderType))
? View.VISIBLE : View.GONE);
}
2019-09-30 16:55:58 +02:00
ivPriority.setVisibility(EntityMessage.PRIORITIY_HIGH.equals(message.priority) ? View.VISIBLE : View.GONE);
2019-09-01 19:07:21 +02:00
ibAuth.setVisibility(authentication && !authenticated ? View.VISIBLE : View.GONE);
ibSnoozed.setVisibility(message.ui_snoozed == null ? View.GONE : View.VISIBLE);
2019-01-19 16:14:40 +00:00
ivBrowsed.setVisibility(message.ui_browsed ? View.VISIBLE : View.GONE);
2018-11-24 19:14:28 +01:00
ivAnswered.setVisibility(message.ui_answered ? View.VISIBLE : View.GONE);
2019-05-04 21:31:14 +02:00
ivPlain.setVisibility(message.plain_only != null && message.plain_only ? View.VISIBLE : View.GONE);
ivReceipt.setVisibility(message.receipt_request != null && message.receipt_request ? View.VISIBLE : View.GONE);
2018-08-23 06:57:24 +00:00
ivAttachments.setVisibility(message.attachments > 0 ? View.VISIBLE : View.GONE);
2018-08-07 06:38:00 +00:00
2019-01-20 10:18:42 +00:00
if (viewType == ViewType.FOLDER)
2019-08-11 20:11:48 +02:00
tvFolder.setText(outbox ? message.identityEmail : message.accountName);
2019-10-01 13:56:48 +02:00
else if (type == null)
tvFolder.setText((compact ? "" : message.accountName + "/") + message.getFolderName(context));
else
tvFolder.setText(message.accountName + "/" + message.getFolderName(context));
tvFolder.setVisibility(compact &&
2019-08-11 20:11:48 +02:00
((viewType == ViewType.FOLDER && !outbox) || (viewType == ViewType.UNIFIED && type == null && inbox))
? View.GONE : View.VISIBLE);
2018-08-23 06:57:24 +00:00
if (viewType == ViewType.THREAD || !threading) {
2018-08-23 06:57:24 +00:00
tvCount.setVisibility(View.GONE);
2018-09-04 07:56:30 +00:00
ivThread.setVisibility(View.GONE);
} else {
tvCount.setText(NF.format(message.visible));
2018-09-04 07:56:30 +00:00
ivThread.setVisibility(View.VISIBLE);
2018-08-09 05:37:52 +00:00
}
2018-08-07 06:38:00 +00:00
// Starred
bindFlagged(message, expanded);
// Message text preview
2019-07-09 15:19:20 +02:00
tvPreview.setTextColor(contrast ? textColorPrimary : textColorSecondary);
tvPreview.setTypeface(
monospaced ? Typeface.MONOSPACE : Typeface.DEFAULT,
preview_italic ? Typeface.ITALIC : Typeface.NORMAL);
tvPreview.setText(message.preview);
2019-06-05 08:42:46 +02:00
tvPreview.setVisibility(preview && !TextUtils.isEmpty(message.preview) ? View.VISIBLE : View.GONE);
// Error / warning
2019-01-17 10:49:18 +00:00
String error = message.error;
if (message.warning != null)
if (error == null)
error = message.warning;
else
error += " " + message.warning;
if (debug) {
2019-01-17 10:49:18 +00:00
String text = "error=" + error +
"\nuid=" + message.uid + " id=" + message.id + " " + DTF.format(new Date(message.received)) +
2019-09-27 18:25:55 +02:00
"\n" + (message.ui_hide ? "HIDDEN " : "") +
2019-01-05 14:08:07 +00:00
"seen=" + message.seen + "/" + message.ui_seen +
" unseen=" + message.unseen +
2019-01-10 18:55:40 +00:00
" ignored=" + message.ui_ignored +
2018-12-05 11:50:07 +01:00
" found=" + message.ui_found +
2019-01-05 14:08:07 +00:00
"\nmsgid=" + message.msgid +
"\nthread=" + message.thread +
"\nsender=" + message.sender;
2018-12-05 11:50:07 +01:00
tvError.setText(text);
tvError.setVisibility(View.VISIBLE);
2018-10-15 10:05:42 +00:00
} else {
2019-01-17 10:49:18 +00:00
tvError.setText(error);
tvError.setVisibility(error == null ? View.GONE : View.VISIBLE);
2019-09-13 09:34:00 +02:00
ibHelp.setVisibility(error == null ? View.GONE : View.VISIBLE);
}
2018-08-11 09:18:49 +00:00
// Contact info
2019-10-02 13:36:07 +02:00
ContactInfo info = ContactInfo.get(context, message.account, addresses, true);
if (info == null) {
Bundle aargs = new Bundle();
aargs.putLong("id", message.id);
2019-10-02 13:36:07 +02:00
aargs.putLong("account", message.account);
aargs.putSerializable("addresses", addresses);
2019-01-10 17:39:52 +00:00
new SimpleTask<ContactInfo>() {
@Override
protected void onPreExecute(Bundle args) {
2019-08-04 13:43:37 +02:00
//Address[] addresses = (Address[]) args.getSerializable("addresses");
ivAvatar.setVisibility(View.GONE);
//tvFrom.setText(MessageHelper.formatAddresses(addresses, name_email, false));
}
2018-12-05 11:50:07 +01:00
@Override
protected ContactInfo onExecute(Context context, Bundle args) {
2019-10-02 13:36:07 +02:00
long account = args.getLong("account");
Address[] addresses = (Address[]) args.getSerializable("addresses");
2019-10-02 13:36:07 +02:00
return ContactInfo.get(context, account, addresses, false);
2018-12-05 11:50:07 +01:00
}
@Override
protected void onExecuted(Bundle args, ContactInfo info) {
2019-02-01 09:29:26 +00:00
long id = args.getLong("id");
TupleMessageEx amessage = getMessage();
if (amessage == null || !amessage.id.equals(id))
return;
bindContactInfo(info, message);
}
2018-12-05 11:50:07 +01:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2018-12-05 11:50:07 +01:00
}
2019-08-22 08:44:22 +02:00
}.setLog(false).execute(context, owner, aargs, "message:avatar");
2018-12-22 08:48:23 +01:00
} else
bindContactInfo(info, message);
2018-08-07 06:38:00 +00:00
2019-10-04 15:25:04 +02:00
if (viewType == ViewType.THREAD)
2019-06-24 11:05:30 +02:00
if (expanded)
2019-08-29 18:12:34 +02:00
bindExpanded(message, false);
2019-10-04 15:25:04 +02:00
else
2019-06-05 07:59:34 +02:00
clearExpanded(message);
2019-01-30 07:50:20 +00:00
}
2019-06-05 07:59:34 +02:00
private void clearExpanded(TupleMessageEx message) {
if (compact) {
tvFrom.setSingleLine(true);
tvSubject.setSingleLine(true);
}
2019-06-05 08:42:46 +02:00
tvPreview.setVisibility(
preview && message != null && !TextUtils.isEmpty(message.preview)
? View.VISIBLE : View.GONE);
if (vsBody == null)
return;
cowner.stop();
2019-05-17 18:54:44 +02:00
grpAddresses.setVisibility(View.GONE);
grpHeaders.setVisibility(View.GONE);
2019-05-18 11:19:39 +02:00
grpCalendar.setVisibility(View.GONE);
2019-05-18 19:44:21 +02:00
grpCalendarResponse.setVisibility(View.GONE);
grpAttachments.setVisibility(View.GONE);
2019-05-14 19:21:11 +02:00
grpImages.setVisibility(View.GONE);
2019-05-08 16:44:01 +02:00
ibSearchContact.setVisibility(View.GONE);
ibNotifyContact.setVisibility(View.GONE);
ibAddContact.setVisibility(View.GONE);
2019-03-09 10:02:17 +00:00
tvFromExTitle.setVisibility(View.GONE);
tvToTitle.setVisibility(View.GONE);
tvReplyToTitle.setVisibility(View.GONE);
tvCcTitle.setVisibility(View.GONE);
tvBccTitle.setVisibility(View.GONE);
tvIdentityTitle.setVisibility(View.GONE);
tvTimeExTitle.setVisibility(View.GONE);
tvSizeExTitle.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);
tvTimeEx.setVisibility(View.GONE);
tvSizeEx.setVisibility(View.GONE);
tvSubjectEx.setVisibility(View.GONE);
tvFlags.setVisibility(View.GONE);
tvKeywords.setVisibility(View.GONE);
2019-03-09 10:02:17 +00:00
pbHeaders.setVisibility(View.GONE);
tvNoInternetHeaders.setVisibility(View.GONE);
2019-05-18 11:19:39 +02:00
tvCalendarSummary.setVisibility(View.GONE);
tvCalendarStart.setVisibility(View.GONE);
tvCalendarEnd.setVisibility(View.GONE);
tvAttendees.setVisibility(View.GONE);
pbCalendarWait.setVisibility(View.GONE);
cbInline.setVisibility(View.GONE);
btnSaveAttachments.setVisibility(View.GONE);
2019-08-15 15:01:26 +02:00
btnDownloadAttachments.setVisibility(View.GONE);
tvNoInternetAttachments.setVisibility(View.GONE);
2019-08-16 08:12:04 +02:00
bnvActions.setVisibility(View.GONE);
2019-05-17 18:54:44 +02:00
ibFull.setVisibility(View.GONE);
2019-08-14 20:08:24 +02:00
ibImages.setVisibility(View.GONE);
2019-09-08 12:57:21 +02:00
ibUnsubscribe.setVisibility(View.GONE);
2019-08-14 20:08:24 +02:00
ibDecrypt.setVisibility(View.GONE);
tvBody.setVisibility(View.GONE);
2019-10-04 15:25:04 +02:00
wvBody.setVisibility(View.GONE);
pbBody.setVisibility(View.GONE);
tvNoInternetBody.setVisibility(View.GONE);
}
private void bindFlagged(TupleMessageEx message, boolean expanded) {
boolean pro = ActivityBilling.isPro(context);
if (!pro)
message.color = null;
2019-02-04 11:45:38 +00:00
int flagged = (message.count - message.unflagged);
2019-09-01 19:07:21 +02:00
ibFlagged.setImageResource(flagged > 0 ? R.drawable.baseline_star_24 : R.drawable.baseline_star_border_24);
if (message.color == null || expanded || !flags_background)
card.setCardBackgroundColor(Color.TRANSPARENT);
else
card.setCardBackgroundColor(ColorUtils.setAlphaComponent(message.color, 127));
if (message.color == null)
ibFlagged.setImageTintList(ColorStateList.valueOf(flagged > 0 ? colorAccent : textColorSecondary));
else
2019-10-03 09:22:02 +02:00
ibFlagged.setImageTintList(ColorStateList.valueOf(expanded || !flags_background ? message.color : textColorSecondary));
2019-09-28 10:05:45 +02:00
ibFlagged.setEnabled(message.uid != null || message.accountPop);
2019-09-25 21:25:37 +02:00
if (flags)
2019-09-28 10:05:45 +02:00
ibFlagged.setVisibility(message.folderReadOnly ? View.INVISIBLE : View.VISIBLE);
2019-09-25 21:25:37 +02:00
else
ibFlagged.setVisibility(View.GONE);
2019-02-04 11:45:38 +00:00
}
private void bindContactInfo(ContactInfo info, TupleMessageEx message) {
if (info.hasPhoto()) {
2019-04-25 21:20:20 +02:00
ivAvatar.setImageBitmap(info.getPhotoBitmap());
ivAvatar.setVisibility(View.VISIBLE);
} else
ivAvatar.setVisibility(View.GONE);
2019-10-02 13:36:07 +02:00
if (distinguish_contacts && info.isKnown())
tvFrom.setPaintFlags(tvFrom.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
2019-08-04 13:43:37 +02:00
//tvFrom.setText(info.getDisplayName(name_email));
}
2019-09-22 20:34:41 +02:00
private void bindExpanded(final TupleMessageEx message, final boolean scroll) {
DB db = DB.getInstance(context);
2019-05-04 12:49:25 +02:00
boolean show_addresses = !properties.getValue("addresses", message.id);
boolean show_headers = properties.getValue("headers", message.id);
if (compact) {
tvFrom.setSingleLine(false);
tvSubject.setSingleLine(false);
}
2019-06-05 08:42:46 +02:00
tvPreview.setVisibility(View.GONE);
2019-05-30 19:46:58 +02:00
ensureExpanded();
2019-05-17 18:54:44 +02:00
grpAddresses.setVisibility(View.VISIBLE);
2019-03-09 10:02:17 +00:00
boolean hasFrom = (message.from != null && message.from.length > 0);
2019-05-13 14:08:51 +02:00
boolean hasTo = (message.to != null && message.to.length > 0);
2019-03-09 10:02:17 +00:00
boolean hasChannel = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
2019-05-13 14:08:51 +02:00
ibSearchContact.setVisibility(show_addresses && (hasFrom || hasTo) ? View.VISIBLE : View.GONE);
2019-05-08 16:44:01 +02:00
ibNotifyContact.setVisibility(show_addresses && hasChannel && hasFrom ? View.VISIBLE : View.GONE);
ibAddContact.setVisibility(show_addresses && contacts && hasFrom ? View.VISIBLE : View.GONE);
grpHeaders.setVisibility(show_headers ? View.VISIBLE : View.GONE);
if (show_headers && message.headers == null) {
2019-03-16 13:12:31 +00:00
pbHeaders.setVisibility(suitable ? View.VISIBLE : View.GONE);
tvNoInternetHeaders.setVisibility(suitable ? View.GONE : View.VISIBLE);
2019-02-04 12:17:42 +00:00
} else {
pbHeaders.setVisibility(View.GONE);
tvNoInternetHeaders.setVisibility(View.GONE);
}
grpAttachments.setVisibility(message.attachments > 0 ? View.VISIBLE : View.GONE);
2019-08-16 08:12:04 +02:00
bnvActions.setVisibility(View.VISIBLE);
for (int i = 0; i < bnvActions.getMenu().size(); i++)
bnvActions.getMenu().getItem(i).setVisible(false);
2019-08-15 11:35:10 +02:00
2019-10-04 15:25:04 +02:00
ibFull.setEnabled(false);
ibFull.setVisibility(View.VISIBLE);
2019-08-14 20:25:46 +02:00
ibImages.setVisibility(View.GONE);
2019-09-08 12:57:21 +02:00
ibUnsubscribe.setVisibility(message.unsubscribe == null ? View.GONE : View.VISIBLE);
2019-08-14 20:08:24 +02:00
// Addresses
2019-09-01 18:59:51 +02:00
ibExpanderAddress.setImageLevel(show_addresses ? 0 /* less */ : 1 /* more */);
2019-08-04 13:43:37 +02:00
String from = MessageHelper.formatAddresses(message.senders);
2019-03-09 10:02:17 +00:00
String to = MessageHelper.formatAddresses(message.to);
String replyto = MessageHelper.formatAddresses(message.reply);
String cc = MessageHelper.formatAddresses(message.cc);
String bcc = MessageHelper.formatAddresses(message.bcc);
tvFromExTitle.setVisibility(show_addresses && !TextUtils.isEmpty(from) ? View.VISIBLE : View.GONE);
tvFromEx.setVisibility(show_addresses && !TextUtils.isEmpty(from) ? View.VISIBLE : View.GONE);
tvFromEx.setText(from);
tvToTitle.setVisibility(show_addresses && !TextUtils.isEmpty(to) ? View.VISIBLE : View.GONE);
tvTo.setVisibility(show_addresses && !TextUtils.isEmpty(to) ? View.VISIBLE : View.GONE);
tvTo.setText(to);
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(show_addresses && !TextUtils.isEmpty(cc) ? View.VISIBLE : View.GONE);
tvCc.setVisibility(show_addresses && !TextUtils.isEmpty(cc) ? View.VISIBLE : View.GONE);
tvCc.setText(cc);
tvBccTitle.setVisibility(show_addresses && !TextUtils.isEmpty(bcc) ? View.VISIBLE : View.GONE);
tvBcc.setVisibility(show_addresses && !TextUtils.isEmpty(bcc) ? View.VISIBLE : View.GONE);
tvBcc.setText(bcc);
InternetAddress via = null;
if (message.identityEmail != null)
try {
via = new InternetAddress(message.identityEmail, message.identityName);
} catch (UnsupportedEncodingException ignored) {
}
2019-03-31 08:35:31 +02:00
tvIdentityTitle.setVisibility(show_addresses && via != null ? View.VISIBLE : View.GONE);
tvIdentity.setVisibility(show_addresses && via != null ? View.VISIBLE : View.GONE);
2019-03-10 08:53:59 +00:00
tvIdentity.setText(via == null ? null : MessageHelper.formatAddresses(new Address[]{via}));
2019-03-09 10:02:17 +00:00
tvTimeExTitle.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvTimeEx.setVisibility(show_addresses ? View.VISIBLE : View.GONE);
tvTimeEx.setText(DTF.format(message.received));
if (!message.duplicate)
tvSizeEx.setAlpha(message.content ? 1.0f : Helper.LOW_LIGHT);
2019-03-09 10:02:17 +00:00
tvSizeExTitle.setVisibility(!show_addresses || message.size == null ? View.GONE : View.VISIBLE);
2019-09-30 21:00:28 +02:00
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, true))
.append("/")
.append(message.total == null ? "-" : Helper.humanReadableByteCount(message.total, true));
tvSizeEx.setText(size.toString());
2019-03-09 10:02:17 +00:00
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
2019-03-09 10:02:17 +00:00
tvFlags.setVisibility(show_addresses && debug ? View.VISIBLE : View.GONE);
tvFlags.setText(message.flags);
// Keywords
2019-03-09 10:02:17 +00:00
tvKeywords.setVisibility(show_addresses && message.keywords.length > 0 ? View.VISIBLE : View.GONE);
tvKeywords.setText(TextUtils.join(" ", message.keywords));
// Headers
2019-08-29 21:57:04 +02:00
if (show_headers && message.headers != null)
tvHeaders.setText(HtmlHelper.highlightHeaders(context, message.headers));
else
tvHeaders.setText(null);
// Attachments
2019-04-04 14:47:56 +02:00
bindAttachments(message, properties.getAttachments(message.id));
// Setup actions
Bundle sargs = new Bundle();
sargs.putLong("id", message.id);
sargs.putLong("account", message.account);
new SimpleTask<List<EntityFolder>>() {
@Override
protected List<EntityFolder> onExecute(Context context, Bundle args) {
long account = args.getLong("account");
return DB.getInstance(context).folder().getSystemFolders(account);
}
@Override
protected void onExecuted(Bundle args, List<EntityFolder> folders) {
long id = args.getLong("id");
TupleMessageEx amessage = getMessage();
if (amessage == null || !amessage.id.equals(id))
return;
boolean hasArchive = false;
boolean hasTrash = false;
2019-08-14 20:08:24 +02:00
hasJunk = false;
if (folders != null)
for (EntityFolder folder : folders) {
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 inOutbox = EntityFolder.OUTBOX.equals(message.folderType);
boolean inArchive = EntityFolder.ARCHIVE.equals(message.folderType);
boolean inTrash = EntityFolder.TRASH.equals(message.folderType);
2019-05-25 17:37:01 +02:00
boolean inJunk = EntityFolder.JUNK.equals(message.folderType);
2019-08-14 20:08:24 +02:00
delete = (inTrash || !hasTrash || inOutbox);
2019-08-16 08:12:04 +02:00
bnvActions.getMenu().findItem(R.id.action_more).setVisible(!inOutbox);
2019-07-07 09:25:52 +02:00
if (!message.folderReadOnly) {
2019-09-14 16:48:49 +02:00
bnvActions.getMenu().findItem(R.id.action_delete).setVisible(
2019-09-27 15:37:03 +02:00
(delete ? message.uid != null || !TextUtils.isEmpty(message.msgid) : message.uid != null));
2019-08-16 08:12:04 +02:00
bnvActions.getMenu().findItem(R.id.action_delete).setTitle(
delete ? R.string.title_delete : R.string.title_trash);
bnvActions.getMenu().findItem(R.id.action_move).setVisible(
message.uid != null || inOutbox);
bnvActions.getMenu().findItem(R.id.action_move).setTitle(
inOutbox ? R.string.title_folder_drafts : R.string.title_move);
bnvActions.getMenu().findItem(R.id.action_move).setIcon(
2019-07-07 09:25:52 +02:00
inOutbox ? R.drawable.baseline_drafts_24 : R.drawable.baseline_folder_24);
2019-08-16 08:12:04 +02:00
bnvActions.getMenu().findItem(R.id.action_archive).setVisible(
message.uid != null && (inJunk || (!inArchive && hasArchive)));
bnvActions.getMenu().findItem(R.id.action_archive).setTitle(
inJunk ? R.string.title_folder_inbox : R.string.title_archive);
bnvActions.getMenu().findItem(R.id.action_archive).setIcon(
inJunk ? R.drawable.baseline_inbox_24 : R.drawable.baseline_archive_24);
2019-07-07 09:25:52 +02:00
}
2019-05-25 17:37:01 +02:00
2019-08-16 08:12:04 +02:00
bnvActions.getMenu().findItem(R.id.action_reply).setEnabled(message.content);
bnvActions.getMenu().findItem(R.id.action_reply).setVisible(!inOutbox);
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
2019-08-22 08:44:22 +02:00
}.setLog(false).execute(context, owner, sargs, "message:actions");
// Message text
pbBody.setVisibility(suitable || message.content ? View.VISIBLE : View.GONE);
2019-03-16 13:12:31 +00:00
tvNoInternetBody.setVisibility(suitable || message.content ? View.GONE : View.VISIBLE);
2019-09-22 20:34:41 +02:00
cowner.recreate();
2019-10-04 15:25:04 +02:00
bindBody(message);
2019-09-22 20:34:41 +02:00
db.attachment().liveAttachments(message.id).observe(cowner, new Observer<List<EntityAttachment>>() {
private int lastInlineImages = 0;
@Override
public void onChanged(@Nullable List<EntityAttachment> attachments) {
bindAttachments(message, attachments);
int inlineImages = 0;
if (attachments != null)
for (EntityAttachment attachment : attachments)
if (attachment.available && attachment.isInline() && attachment.isImage())
inlineImages++;
if (inlineImages != lastInlineImages) {
lastInlineImages = inlineImages;
2019-10-04 15:25:04 +02:00
bindBody(message);
2019-09-22 20:34:41 +02:00
}
if (scroll)
properties.scrollTo(getAdapterPosition());
}
});
}
2019-10-04 15:25:04 +02:00
private void bindBody(TupleMessageEx message) {
tvBody.setText(null);
2019-10-04 15:25:04 +02:00
if (!message.content)
return;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (message.from != null)
for (Address address : message.from) {
String from = ((InternetAddress) address).getAddress();
if (prefs.getBoolean(from + ".show_full", false)) {
properties.setValue("full", message.id, true);
properties.setValue("full_asked", message.id, true);
}
if (prefs.getBoolean(from + ".show_images", false))
properties.setValue("images", message.id, true);
}
int dp60 = Helper.dp2pixels(context, 60);
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);
float size = properties.getSize(message.id, show_full ? 0 : textSize);
int height = properties.getHeight(message.id, dp60);
2019-10-04 18:59:37 +02:00
Pair<Integer, Integer> position = properties.getPosition(message.id);
2019-10-04 15:25:04 +02:00
Log.i("Bind size=" + size + " height=" + height);
ibFull.setEnabled(hasWebView);
ibFull.setImageResource(show_full ? R.drawable.baseline_fullscreen_exit_24 : R.drawable.baseline_fullscreen_24);
2019-10-04 15:25:04 +02:00
if (show_full) {
// Create web view
WebView webView;
if (wvBody instanceof WebView)
webView = (WebView) wvBody;
else {
webView = new WebView(context) {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (height > dp60)
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
else
super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Unspecified
int mh = getMeasuredHeight();
Log.i("Measured height=" + mh);
if (mh == 0)
setMeasuredDimension(getMeasuredWidth(), height);
}
@Override
protected void onSizeChanged(int w, int h, int ow, int oh) {
super.onSizeChanged(w, h, ow, oh);
Log.i("Size changed height=" + h);
properties.setHeight(message.id, h);
}
};
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());
webView.setVerticalScrollBarEnabled(false);
webView.setOnTouchListener(ViewHolder.this);
webView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.i("Open url=" + url);
Uri uri = Uri.parse(url);
if ("cid".equals(uri.getScheme()) || "data".equals(uri.getScheme()))
return false;
Bundle args = new Bundle();
args.putParcelable("uri", uri);
args.putString("title", null);
FragmentDialogLink fragment = new FragmentDialogLink();
fragment.setArguments(args);
fragment.show(parentFragment.getFragmentManager(), "open:link");
return true;
}
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
Log.i("Changed scale=" + newScale);
properties.setSize(message.id, newScale);
}
});
2019-10-04 18:59:37 +02:00
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
properties.setPosition(message.id, new Pair<Integer, Integer>(scrollX, scrollY));
}
});
2019-10-04 15:25:04 +02:00
webView.setDownloadListener(new DownloadListener() {
public void onDownloadStart(
String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
Log.i("Download url=" + url + " mime type=" + mimetype);
Uri uri = Uri.parse(url);
if ("cid".equals(uri.getScheme()) || "data".equals(uri.getScheme()))
return;
Helper.view(context, uri, true);
}
});
webView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
WebView.HitTestResult result = ((WebView) view).getHitTestResult();
if (result.getType() == WebView.HitTestResult.IMAGE_TYPE ||
result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
Log.i("Long press url=" + result.getExtra());
Uri uri = Uri.parse(result.getExtra());
if ("cid".equals(uri.getScheme()) || "data".equals(uri.getScheme()))
return false;
Helper.view(context, uri, true);
return true;
}
return false;
}
});
wvBody = webView;
}
WebSettings settings = webView.getSettings();
settings.setUseWideViewPort(false);
2019-10-04 15:25:04 +02:00
settings.setLoadWithOverviewMode(true);
settings.setBuiltInZoomControls(true);
settings.setDisplayZoomControls(false);
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
if (textSize != 0) {
int dp = Helper.pixels2dp(context, textSize);
settings.setDefaultFontSize(Math.round(dp));
settings.setDefaultFixedFontSize(Math.round(dp));
2019-10-04 15:25:04 +02:00
}
if (monospaced)
settings.setStandardFontFamily("monospace");
settings.setAllowFileAccess(false);
settings.setLoadsImagesAutomatically(show_images || inline);
settings.setBlockNetworkLoads(!show_images);
settings.setBlockNetworkImage(!show_images);
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
if (size != 0)
webView.setInitialScale(Math.round(size * 100));
wvBody.setMinimumHeight(height);
2019-10-04 18:59:37 +02:00
if (position != null) {
wvBody.setScrollX(position.first);
wvBody.setScrollY(position.second);
}
2019-10-04 15:25:04 +02:00
tvBody.setVisibility(View.GONE);
wvBody.setVisibility(View.VISIBLE);
} else {
if (size != 0)
tvBody.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
tvBody.setMinHeight(height);
tvBody.setTextColor(contrast ? textColorPrimary : textColorSecondary);
tvBody.setTypeface(monospaced ? Typeface.MONOSPACE : Typeface.DEFAULT);
tvBody.setVisibility(View.VISIBLE);
wvBody.setVisibility(View.GONE);
}
Bundle args = new Bundle();
args.putSerializable("message", message);
args.putBoolean("show_images", show_images);
args.putBoolean("show_full", show_full);
args.putBoolean("show_quotes", show_quotes);
args.putInt("zoom", zoom);
new SimpleTask<Object>() {
@Override
protected Object onExecute(final Context context, final Bundle args) throws IOException {
TupleMessageEx message = (TupleMessageEx) args.getSerializable("message");
boolean show_full = args.getBoolean("show_full");
boolean show_images = args.getBoolean("show_images");
boolean show_quotes = args.getBoolean("show_quotes");
int zoom = args.getInt("zoom");
if (message == null || !message.content)
return null;
File file = message.getFile(context);
if (!file.exists())
return null;
String body = Helper.readText(file);
Document document = JsoupEx.parse(body);
// Check for inline encryption
int begin = body.indexOf(Helper.PGP_BEGIN_MESSAGE);
int end = body.indexOf(Helper.PGP_END_MESSAGE);
args.putBoolean("iencrypted", begin >= 0 && begin < end);
// Check for images
boolean has_images = false;
for (Element img : document.select("img")) {
if (inline) {
String src = img.attr("src");
if (!src.startsWith("cid:")) {
has_images = true;
break;
}
} else {
has_images = true;
break;
}
}
args.putBoolean("has_images", has_images);
if (show_full) {
HtmlHelper.removeViewportLimitations(document);
if (inline || show_images)
2019-10-05 11:57:54 +02:00
HtmlHelper.embedInlineImages(context, message.id, document);
2019-10-04 15:25:04 +02:00
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean disable_tracking = prefs.getBoolean("disable_tracking", true);
if (disable_tracking)
HtmlHelper.removeTrackingPixels(context, document);
2019-10-04 23:04:03 +02:00
if (debug) {
Document format = JsoupEx.parse(document.html());
format.outputSettings().prettyPrint(true).outline(true).indentAmount(1);
Element pre = document.createElement("pre");
pre.text(format.html());
document.body().appendChild(pre);
}
2019-10-04 15:25:04 +02:00
return document.html();
} else {
// Collapse quotes
if (!show_quotes) {
for (Element quote : document.select("blockquote"))
quote.html("&#8230;");
body = document.html();
}
// Cleanup message
String html = HtmlHelper.sanitize(context, body, show_images);
if (debug) {
Document format = JsoupEx.parse(html);
format.outputSettings().prettyPrint(true).outline(true).indentAmount(1);
String[] lines = format.html().split("\\r?\\n");
for (int i = 0; i < lines.length; i++)
lines[i] = Html.escapeHtml(lines[i]);
html += "<pre>" + TextUtils.join("<br>", lines) + "</pre>";
}
Spanned spanned = HtmlHelper.fromHtml(html, new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
2019-10-04 21:25:52 +02:00
Drawable drawable = ImageHelper.decodeImage(context, message.id, source, show_images, tvBody);
2019-10-04 15:25:04 +02:00
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (drawable instanceof AnimatedImageDrawable)
((AnimatedImageDrawable) drawable).start();
}
return drawable;
}
}, null);
// Replace quote spans
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
QuoteSpan[] quoteSpans = builder.getSpans(0, builder.length(), QuoteSpan.class);
for (QuoteSpan quoteSpan : quoteSpans) {
builder.setSpan(
new StyledQuoteSpan(context, colorPrimary),
builder.getSpanStart(quoteSpan),
builder.getSpanEnd(quoteSpan),
builder.getSpanFlags(quoteSpan));
builder.removeSpan(quoteSpan);
}
// Make collapsed quotes clickable
if (!show_quotes) {
final int px = Helper.dp2pixels(context, 24 + (zoom) * 8);
StyledQuoteSpan[] squotes = builder.getSpans(0, builder.length(), StyledQuoteSpan.class);
for (StyledQuoteSpan squote : squotes)
builder.setSpan(new DynamicDrawableSpan() {
@Override
public Drawable getDrawable() {
Drawable d = context.getDrawable(R.drawable.baseline_format_quote_24);
d.setTint(colorAccent);
d.setBounds(0, 0, px, px);
return d;
}
},
builder.getSpanStart(squote),
builder.getSpanEnd(squote),
builder.getSpanFlags(squote));
}
return builder;
}
}
@Override
protected void onExecuted(Bundle args, Object result) {
TupleMessageEx message = (TupleMessageEx) args.getSerializable("message");
properties.setValue("iencrypted", message.id, args.getBoolean("iencrypted"));
TupleMessageEx amessage = getMessage();
if (amessage == null || !amessage.id.equals(message.id))
return;
boolean show_expanded = properties.getValue("expanded", message.id);
if (!show_expanded)
return;
boolean has_images = args.getBoolean("has_images");
boolean show_images = properties.getValue("images", message.id);
if (result instanceof Spanned) {
tvBody.setText((Spanned) result);
tvBody.setTextIsSelectable(false);
tvBody.setTextIsSelectable(true);
tvBody.setMovementMethod(new TouchHandler(message));
} else if (result instanceof String)
2019-10-04 15:25:04 +02:00
((WebView) wvBody).loadDataWithBaseURL(null, (String) result, "text/html", "UTF-8", null);
else
2019-10-04 15:25:04 +02:00
throw new IllegalStateException("Result=" + result);
pbBody.setVisibility(View.GONE);
// Show attachments/images
cowner.start();
ibImages.setVisibility(has_images && !show_images ? View.VISIBLE : View.GONE);
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
}.execute(context, owner, args, "message:body");
}
2019-02-04 12:17:42 +00:00
private void bindAttachments(final TupleMessageEx message, @Nullable List<EntityAttachment> attachments) {
if (attachments == null)
attachments = new ArrayList<>();
2019-09-02 11:32:05 +02:00
properties.setAttachments(message.id, attachments);
2019-02-04 12:17:42 +00:00
boolean iencrypted = properties.getValue("iencrypted", message.id);
2019-02-04 12:17:42 +00:00
boolean show_inline = properties.getValue("inline", message.id);
Log.i("Show inline=" + show_inline);
2019-02-17 12:05:58 +00:00
boolean has_inline = false;
2019-07-06 18:59:25 +02:00
boolean is_encrypted = false;
2019-02-04 12:17:42 +00:00
boolean download = false;
boolean save = (attachments.size() > 1);
boolean downloading = false;
2019-05-18 11:19:39 +02:00
boolean calendar = false;
2019-02-04 12:17:42 +00:00
List<EntityAttachment> a = new ArrayList<>();
for (EntityAttachment attachment : attachments) {
2019-07-08 12:37:24 +02:00
boolean inline = (attachment.isInline() && attachment.isImage());
2019-02-17 12:05:58 +00:00
if (inline)
has_inline = true;
2019-07-06 18:59:25 +02:00
if (Objects.equals(attachment.encryption, EntityAttachment.PGP_MESSAGE))
is_encrypted = true;
2019-02-04 12:17:42 +00:00
if (attachment.progress == null && !attachment.available)
download = true;
if (!attachment.available)
save = false;
if (attachment.progress != null)
downloading = true;
2019-02-17 12:05:58 +00:00
if (show_inline || !inline)
2019-02-04 12:17:42 +00:00
a.add(attachment);
2019-05-18 11:19:39 +02:00
2019-05-18 19:44:21 +02:00
if (attachment.available && "text/calendar".equals(attachment.type)) {
2019-05-18 11:19:39 +02:00
calendar = true;
2019-07-22 09:30:58 +02:00
bindCalendar(message, attachment);
2019-05-18 11:19:39 +02:00
}
2019-02-04 12:17:42 +00:00
}
adapterAttachment.set(a);
2019-05-18 11:19:39 +02:00
if (!calendar) {
tvCalendarSummary.setVisibility(View.GONE);
tvCalendarStart.setVisibility(View.GONE);
tvCalendarEnd.setVisibility(View.GONE);
tvAttendees.setVisibility(View.GONE);
pbCalendarWait.setVisibility(View.GONE);
grpCalendar.setVisibility(View.GONE);
2019-05-18 19:44:21 +02:00
grpCalendarResponse.setVisibility(View.GONE);
2019-05-18 11:19:39 +02:00
}
2019-02-04 12:17:42 +00:00
cbInline.setOnCheckedChangeListener(null);
cbInline.setChecked(show_inline);
2019-02-17 12:05:58 +00:00
cbInline.setVisibility(has_inline ? View.VISIBLE : View.GONE);
2019-08-14 20:25:46 +02:00
2019-02-04 12:17:42 +00:00
btnSaveAttachments.setVisibility(save ? View.VISIBLE : View.GONE);
2019-08-15 15:01:26 +02:00
btnDownloadAttachments.setVisibility(download && suitable ? View.VISIBLE : View.GONE);
2019-03-16 13:12:31 +00:00
tvNoInternetAttachments.setVisibility(downloading && !suitable ? View.VISIBLE : View.GONE);
2019-02-04 12:17:42 +00:00
ibDecrypt.setVisibility(iencrypted || is_encrypted ? View.VISIBLE : View.GONE);
2019-08-14 20:25:46 +02:00
2019-02-04 12:17:42 +00:00
cbInline.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
properties.setValue("inline", message.id, isChecked);
2019-03-17 20:31:24 +00:00
cowner.restart();
DB.getInstance(context).attachment().liveAttachments(message.id).observe(cowner, new Observer<List<EntityAttachment>>() {
@Override
public void onChanged(@Nullable List<EntityAttachment> attachments) {
bindAttachments(message, attachments);
}
});
2019-02-04 12:17:42 +00:00
}
});
List<EntityAttachment> images = new ArrayList<>();
for (EntityAttachment attachment : attachments)
2019-05-06 07:36:44 +02:00
if (!attachment.isInline() && attachment.isImage())
2019-02-04 12:17:42 +00:00
images.add(attachment);
adapterImage.set(images);
2019-05-14 19:21:11 +02:00
grpImages.setVisibility(images.size() > 0 ? View.VISIBLE : View.GONE);
2019-02-04 12:17:42 +00:00
}
2019-07-22 09:30:58 +02:00
private void bindCalendar(final TupleMessageEx message, EntityAttachment attachment) {
// https://tools.ietf.org/html/rfc5546
Bundle args = new Bundle();
args.putLong("id", message.id);
args.putSerializable("file", attachment.getFile(context));
new SimpleTask<ICalendar>() {
@Override
protected void onPreExecute(Bundle args) {
grpCalendar.setVisibility(View.VISIBLE);
pbCalendarWait.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Bundle args) {
pbCalendarWait.setVisibility(View.GONE);
}
@Override
protected ICalendar onExecute(Context context, Bundle args) throws IOException {
File file = (File) args.getSerializable("file");
return Biweekly.parse(file).first();
}
@Override
protected void onExecuted(Bundle args, ICalendar icalendar) {
long id = args.getLong("id");
TupleMessageEx amessage = getMessage();
if (amessage == null || !amessage.id.equals(id))
return;
if (icalendar == null ||
icalendar.getMethod() == null ||
icalendar.getEvents().size() == 0) {
tvCalendarSummary.setVisibility(View.GONE);
tvCalendarStart.setVisibility(View.GONE);
tvCalendarEnd.setVisibility(View.GONE);
tvAttendees.setVisibility(View.GONE);
pbCalendarWait.setVisibility(View.GONE);
grpCalendar.setVisibility(View.GONE);
grpCalendarResponse.setVisibility(View.GONE);
return;
}
DateFormat DTF = Helper.getDateTimeInstance(context);
VEvent event = icalendar.getEvents().get(0);
2019-09-30 13:40:14 +02:00
String summary = event.getSummary() == null ? null : event.getSummary().getValue();
2019-07-22 09:30:58 +02:00
ICalDate start = event.getDateStart() == null ? null : event.getDateStart().getValue();
ICalDate end = event.getDateEnd() == null ? null : event.getDateEnd().getValue();
List<String> attendee = new ArrayList<>();
for (Attendee a : event.getAttendees()) {
String email = a.getEmail();
String name = a.getCommonName();
if (TextUtils.isEmpty(name)) {
if (!TextUtils.isEmpty(email))
attendee.add(email);
} else {
if (TextUtils.isEmpty(email) || name.equals(email))
attendee.add(name);
else
attendee.add(name + " (" + email + ")");
}
}
Organizer organizer = event.getOrganizer();
2019-09-30 13:40:14 +02:00
tvCalendarSummary.setText(summary);
2019-07-22 09:30:58 +02:00
tvCalendarSummary.setVisibility(summary == null ? View.GONE : View.VISIBLE);
tvCalendarStart.setText(start == null ? null : DTF.format(start.getTime()));
tvCalendarStart.setVisibility(start == null ? View.GONE : View.VISIBLE);
tvCalendarEnd.setText(end == null ? null : DTF.format(end.getTime()));
tvCalendarEnd.setVisibility(end == null ? View.GONE : View.VISIBLE);
tvAttendees.setText(TextUtils.join(", ", attendee));
tvAttendees.setVisibility(attendee.size() == 0 ? View.GONE : View.VISIBLE);
boolean canRespond =
(icalendar.getMethod().isRequest() &&
organizer != null && organizer.getEmail() != null &&
message.to != null && message.to.length > 0);
grpCalendarResponse.setVisibility(canRespond ? View.VISIBLE : View.GONE);
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
2019-08-22 08:44:22 +02:00
}.setLog(false).execute(context, owner, args, "message:calendar");
2019-07-22 09:30:58 +02:00
}
2019-05-18 19:44:21 +02:00
private void onActionCalendar(TupleMessageEx message, int action) {
2019-08-13 10:27:17 +02:00
if (!ActivityBilling.isPro(context)) {
context.startActivity(new Intent(context, ActivityBilling.class));
2019-05-25 16:12:53 +02:00
return;
}
2019-05-18 19:44:21 +02:00
Bundle args = new Bundle();
args.putLong("id", message.id);
args.putInt("action", action);
2019-09-30 13:40:14 +02:00
new SimpleTask<Object>() {
2019-05-18 19:44:21 +02:00
@Override
2019-09-30 13:40:14 +02:00
protected Object onExecute(Context context, Bundle args) throws Throwable {
2019-05-18 19:44:21 +02:00
long id = args.getLong("id");
int action = args.getInt("action");
DB db = DB.getInstance(context);
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
List<EntityAttachment> attachments = db.attachment().getAttachments(id);
for (EntityAttachment attachment : attachments)
if (attachment.available && "text/calendar".equals(attachment.type)) {
File file = attachment.getFile(context);
ICalendar icalendar = Biweekly.parse(file).first();
VEvent event = icalendar.getEvents().get(0);
2019-09-30 13:40:14 +02:00
if (action == R.id.ibCalendar) {
String summary = event.getSummary() == null ? null : event.getSummary().getValue();
ICalDate start = event.getDateStart() == null ? null : event.getDateStart().getValue();
ICalDate end = event.getDateEnd() == null ? null : event.getDateEnd().getValue();
String location = event.getLocation() == null ? null : event.getLocation().getValue();
List<String> attendee = new ArrayList<>();
for (Attendee a : event.getAttendees()) {
String email = a.getEmail();
if (!TextUtils.isEmpty(email))
attendee.add(email);
}
// https://developer.android.com/guide/topics/providers/calendar-provider.html#intent-insert
Intent intent = new Intent(Intent.ACTION_INSERT)
.setData(CalendarContract.Events.CONTENT_URI)
.putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY);
if (summary != null)
intent.putExtra(CalendarContract.Events.TITLE, summary);
if (start != null)
intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start.getTime());
if (end != null)
intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end.getTime());
if (location != null)
intent.putExtra(CalendarContract.Events.EVENT_LOCATION, location);
if (attendee.size() > 0)
intent.putExtra(Intent.EXTRA_EMAIL, TextUtils.join(",", attendee));
return intent;
}
2019-05-18 19:44:21 +02:00
// https://tools.ietf.org/html/rfc5546#section-4.2.2
VEvent ev = new VEvent();
ev.setOrganizer(event.getOrganizer());
ev.setUid(event.getUid());
if (event.getSequence() != null)
ev.setSequence(event.getSequence());
if (event.getDateStart() != null)
ev.setDateStart(event.getDateStart());
if (event.getDateEnd() != null)
ev.setDateEnd(event.getDateEnd());
2019-05-18 19:44:21 +02:00
InternetAddress to = (InternetAddress) message.to[0];
Attendee attendee = new Attendee(to.getPersonal(), to.getAddress());
switch (action) {
case R.id.btnCalendarAccept:
attendee.setParticipationStatus(ParticipationStatus.ACCEPTED);
break;
case R.id.btnCalendarDecline:
attendee.setParticipationStatus(ParticipationStatus.DECLINED);
break;
case R.id.btnCalendarMaybe:
attendee.setParticipationStatus(ParticipationStatus.TENTATIVE);
break;
}
ev.addAttendee(attendee);
// https://icalendar.org/validator.html
2019-05-18 19:44:21 +02:00
ICalendar response = new ICalendar();
response.setMethod(Method.REPLY);
response.addEvent(ev);
2019-08-03 13:31:18 +02:00
File ics = File.createTempFile("calendar", ".ics", context.getCacheDir());
2019-05-18 19:44:21 +02:00
response.write(ics);
return ics;
}
return null;
}
@Override
2019-09-30 13:40:14 +02:00
protected void onExecuted(Bundle args, Object result) {
if (result instanceof File) {
String status = null;
switch (action) {
case R.id.btnCalendarAccept:
status = context.getString(R.string.title_icalendar_accept);
break;
case R.id.btnCalendarDecline:
status = context.getString(R.string.title_icalendar_decline);
break;
case R.id.btnCalendarMaybe:
status = context.getString(R.string.title_icalendar_maybe);
break;
}
2019-08-16 08:29:11 +02:00
2019-09-30 13:40:14 +02:00
Intent reply = new Intent(context, ActivityCompose.class)
.putExtra("action", "participation")
.putExtra("reference", args.getLong("id"))
.putExtra("ics", (File) result)
.putExtra("status", status);
context.startActivity(reply);
} else if (result instanceof Intent) {
context.startActivity((Intent) result);
}
2019-05-18 19:44:21 +02:00
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2019-05-18 19:44:21 +02:00
}
}.execute(context, owner, args, "message:participation");
}
2019-02-01 09:29:26 +00:00
private TupleMessageEx getMessage() {
int pos = getAdapterPosition();
if (pos == RecyclerView.NO_POSITION)
2019-02-01 09:29:26 +00:00
return null;
return differ.getItem(pos);
}
2018-10-15 10:05:42 +00:00
2019-09-01 15:42:53 +02:00
@Override
public boolean onTouch(View view, MotionEvent ev) {
2019-10-04 15:25:04 +02:00
if (ev.getPointerCount() > 1) {
2019-09-01 17:28:09 +02:00
view.getParent().requestDisallowInterceptTouchEvent(true);
2019-10-04 15:25:04 +02:00
if (view.getId() == R.id.tvBody) {
gestureDetector.onTouchEvent(ev);
return true;
} else
return false;
2019-09-01 17:28:09 +02:00
} else {
view.getParent().requestDisallowInterceptTouchEvent(false);
return false;
2019-09-01 15:42:53 +02:00
}
}
2019-10-04 15:25:04 +02:00
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
TupleMessageEx message = getMessage();
if (message != null)
properties.setHeight(message.id, bottom - top);
}
2019-02-01 09:29:26 +00:00
@Override
public void onClick(View view) {
2019-06-26 12:58:36 +02:00
final TupleMessageEx message = getMessage();
2019-01-26 10:09:40 +00:00
if (message == null)
return;
2018-08-02 13:33:06 +00:00
2019-09-01 19:07:21 +02:00
if (view.getId() == R.id.ibAuth)
onShowAuth(message);
2019-09-01 19:07:21 +02:00
else if (view.getId() == R.id.ibSnoozed)
2019-01-07 15:05:24 +00:00
onShowSnoozed(message);
2019-09-01 19:07:21 +02:00
else if (view.getId() == R.id.ibFlagged)
2018-12-04 17:08:36 +01:00
onToggleFlag(message);
2019-09-13 09:34:00 +02:00
else if (view.getId() == R.id.ibHelp)
2019-09-06 14:02:16 +02:00
onHelp(message);
2019-05-08 16:44:01 +02:00
else if (view.getId() == R.id.ibSearchContact)
2019-01-27 13:50:21 +00:00
onSearchContact(message);
2019-05-08 16:44:01 +02:00
else if (view.getId() == R.id.ibNotifyContact)
2019-03-07 12:29:03 +00:00
onNotifyContact(message);
2019-05-08 16:44:01 +02:00
else if (view.getId() == R.id.ibAddContact)
2018-10-15 10:05:42 +00:00
onAddContact(message);
else if (viewType == ViewType.THREAD) {
2019-05-18 19:44:21 +02:00
switch (view.getId()) {
2019-09-01 18:59:51 +02:00
case R.id.ibExpanderAddress:
2019-05-18 19:44:21 +02:00
onToggleAddresses(message);
break;
2019-08-15 15:01:26 +02:00
2019-05-18 19:44:21 +02:00
case R.id.btnSaveAttachments:
onSaveAttachments(message);
break;
2019-08-15 15:01:26 +02:00
case R.id.btnDownloadAttachments:
onDownloadAttachments(message);
break;
2019-08-14 20:08:24 +02:00
case R.id.ibFull:
2019-10-04 15:25:04 +02:00
onShow(message, true);
2019-07-10 19:00:25 +02:00
break;
2019-05-18 19:44:21 +02:00
case R.id.ibImages:
2019-10-04 15:25:04 +02:00
onShow(message, false);
2019-05-18 19:44:21 +02:00
break;
2019-09-08 12:57:21 +02:00
case R.id.ibUnsubscribe:
onActionUnsubscribe(message);
break;
2019-08-14 20:08:24 +02:00
case R.id.ibDecrypt:
onActionDecrypt(message);
break;
2019-05-18 19:44:21 +02:00
case R.id.btnCalendarAccept:
case R.id.btnCalendarDecline:
case R.id.btnCalendarMaybe:
2019-09-30 13:40:14 +02:00
case R.id.ibCalendar:
2019-05-18 19:44:21 +02:00
onActionCalendar(message, view.getId());
break;
default:
onToggleMessage(message);
}
2018-10-15 10:05:42 +00:00
} else {
2019-08-16 14:31:31 +02:00
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Unreveal
int cx = card.getWidth() / 2;
int cy = card.getHeight() / 2;
int r = Math.max(card.getWidth(), card.getHeight());
Animator anim = ViewAnimationUtils.createCircularReveal(card, cx, cy, r, 0);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
anim.setDuration(context.getResources().getInteger(android.R.integer.config_mediumAnimTime));
anim.start();
} else {
// selectableItemBackground
card.setClickable(true);
card.setPressed(true);
2019-08-18 08:34:12 +02:00
card.setPressed(false);
card.setClickable(false);
2019-08-16 14:31:31 +02:00
}
2019-03-08 10:27:33 +00:00
2019-02-05 10:28:38 +00:00
if (EntityFolder.DRAFTS.equals(message.folderType) && message.visible == 1)
context.startActivity(
2018-10-15 10:05:42 +00:00
new Intent(context, ActivityCompose.class)
.putExtra("action", "edit")
.putExtra("id", message.id));
else {
2019-06-26 13:55:35 +02:00
final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
final Intent viewThread = new Intent(ActivityView.ACTION_VIEW_THREAD)
.putExtra("account", message.account)
.putExtra("thread", message.thread)
.putExtra("id", message.id)
.putExtra("found", viewType == ViewType.SEARCH);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
2019-10-01 15:20:24 +02:00
boolean doubletap = prefs.getBoolean("doubletap", false);
2019-06-26 13:55:35 +02:00
2019-07-07 09:25:52 +02:00
if (!doubletap || message.folderReadOnly || EntityFolder.OUTBOX.equals(message.folderType)) {
2019-06-26 13:55:35 +02:00
lbm.sendBroadcast(viewThread);
return;
}
2019-06-26 12:58:36 +02:00
firstClick = !firstClick;
if (firstClick) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (firstClick) {
firstClick = false;
2019-06-26 13:55:35 +02:00
lbm.sendBroadcast(viewThread);
2019-06-26 12:58:36 +02:00
}
}
}, ViewConfiguration.getDoubleTapTimeout());
} else {
2019-09-29 18:50:30 +02:00
message.ui_seen = !message.ui_seen;
message.unseen = (message.ui_seen ? 0 : message.count);
bindTo(message);
2019-06-26 12:58:36 +02:00
Bundle args = new Bundle();
args.putLong("id", message.id);
2019-09-25 21:23:44 +02:00
args.putBoolean("pop", message.accountPop);
2019-06-26 12:58:36 +02:00
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
2019-09-25 21:23:44 +02:00
boolean pop = args.getBoolean("pop");
2019-06-26 12:58:36 +02:00
DB db = DB.getInstance(context);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
2019-09-25 21:23:44 +02:00
if (message == null)
return null;
2019-09-26 11:57:00 +02:00
if (pop)
EntityOperation.queue(context, message, EntityOperation.SEEN, !message.ui_seen);
else {
2019-07-25 15:38:14 +02:00
List<EntityMessage> messages = db.message().getMessagesByThread(
2019-08-30 08:06:25 +02:00
message.account, message.thread, threading ? null : id, message.ui_seen ? message.folder : null);
for (EntityMessage threaded : messages)
if (threaded.ui_seen == message.ui_seen)
EntityOperation.queue(context, threaded, EntityOperation.SEEN, !message.ui_seen);
}
2019-06-26 12:58:36 +02:00
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2019-06-26 12:58:36 +02:00
}
}.execute(context, owner, args, "message:seen");
}
2018-10-15 10:05:42 +00:00
}
}
}
2019-08-16 08:12:04 +02:00
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
final TupleMessageEx message = getMessage();
if (message == null)
return false;
switch (item.getItemId()) {
case R.id.action_more:
onActionMore(message);
return true;
case R.id.action_delete:
onActionDelete(message);
return true;
case R.id.action_move:
if (EntityFolder.OUTBOX.equals(message.folderType))
onActionMoveOutbox(message);
else
onActionMove(message, false);
return true;
case R.id.action_archive:
if (EntityFolder.JUNK.equals(message.folderType))
onActionMoveJunk(message);
else
onActionArchive(message);
return true;
case R.id.action_reply:
onActionReplyMenu(message);
return true;
default:
return false;
}
}
2019-06-28 18:43:36 +02:00
@Override
public boolean onLongClick(View view) {
final TupleMessageEx message = getMessage();
2019-07-07 09:25:52 +02:00
if (message == null || message.folderReadOnly)
2019-06-28 18:43:36 +02:00
return false;
2019-09-01 19:07:21 +02:00
if (view.getId() == R.id.ibFlagged) {
2019-06-28 18:43:36 +02:00
onMenuColoredStar(message);
return true;
}
return false;
}
2019-08-10 10:42:28 +02:00
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN &&
(keyCode == KeyEvent.KEYCODE_ENTER ||
keyCode == KeyEvent.KEYCODE_DPAD_CENTER ||
keyCode == KeyEvent.KEYCODE_BUTTON_A)) {
onClick(view);
return true;
} else
return false;
}
private void onShowAuth(TupleMessageEx message) {
List<String> result = new ArrayList<>();
if (Boolean.FALSE.equals(message.dkim))
result.add("DKIM");
if (Boolean.FALSE.equals(message.spf))
result.add("SPF");
if (Boolean.FALSE.equals(message.dmarc))
result.add("DMARC");
if (Boolean.FALSE.equals(message.mx))
result.add("MX");
ToastEx.makeText(context,
context.getString(R.string.title_authentication_failed, TextUtils.join(", ", result)),
Toast.LENGTH_LONG)
.show();
}
2019-01-07 17:50:23 +00:00
private void onShowSnoozed(TupleMessageEx message) {
if (message.ui_snoozed != null) {
2019-07-15 21:28:25 +02:00
DateFormat DTF = Helper.getDateTimeInstance(context, SimpleDateFormat.MEDIUM, SimpleDateFormat.SHORT);
DateFormat D = new SimpleDateFormat("E");
2019-09-01 19:08:47 +02:00
ToastEx.makeText(
context,
2019-08-09 18:42:36 +02:00
D.format(message.ui_snoozed) + " " + DTF.format(message.ui_snoozed) + " - " +
DateUtils.getRelativeTimeSpanString(
message.ui_snoozed,
System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE),
2019-09-01 19:08:47 +02:00
Toast.LENGTH_LONG).show();
}
2019-01-07 17:50:23 +00:00
}
private void onToggleFlag(TupleMessageEx message) {
2019-03-30 08:05:51 +00:00
int flagged = (message.count - message.unflagged);
Log.i("Set message id=" + message.id + " flagged=" + flagged);
2019-01-30 07:50:20 +00:00
2019-01-07 17:50:23 +00:00
Bundle args = new Bundle();
args.putLong("id", message.id);
2019-03-30 08:05:51 +00:00
args.putBoolean("flagged", flagged == 0);
2019-01-07 17:50:23 +00:00
args.putBoolean("thread", viewType != ViewType.THREAD);
2019-01-30 07:50:20 +00:00
message.unflagged = message.ui_flagged ? message.count : 0;
message.ui_flagged = !message.ui_flagged;
boolean expanded = properties.getValue("expanded", message.id);
bindFlagged(message, expanded);
2019-01-07 17:50:23 +00:00
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
boolean flagged = args.getBoolean("flagged");
boolean thread = args.getBoolean("thread");
DB db = DB.getInstance(context);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
2019-09-28 10:05:45 +02:00
EntityAccount account = db.account().getAccount(message.account);
if (account == null)
return null;
if (account.pop)
EntityOperation.queue(context, message, EntityOperation.FLAG, flagged);
else {
List<EntityMessage> messages = db.message().getMessagesByThread(
message.account, message.thread, threading && thread ? null : id, null);
for (EntityMessage threaded : messages)
EntityOperation.queue(context, threaded, EntityOperation.FLAG, flagged);
}
2019-01-07 17:50:23 +00:00
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2019-01-07 17:50:23 +00:00
}
}.execute(context, owner, args, "message:flag");
}
2019-09-06 14:02:16 +02:00
private void onHelp(TupleMessageEx message) {
Helper.viewFAQ(context, 130);
2019-09-06 14:02:16 +02:00
}
2019-01-27 13:50:21 +00:00
private void onSearchContact(TupleMessageEx message) {
2019-01-27 14:38:53 +00:00
Bundle args = new Bundle();
args.putLong("id", message.id);
new SimpleTask<Address[]>() {
@Override
protected Address[] onExecute(Context context, Bundle args) {
long id = args.getLong("id");
DB db = DB.getInstance(context);
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
EntityFolder folder = db.folder().getFolder(message.folder);
2019-09-22 20:03:31 +02:00
if (folder == null)
return null;
boolean outgoing = EntityFolder.isOutgoing(folder.type);
2019-01-27 14:38:53 +00:00
2019-09-22 20:03:31 +02:00
if (message.identity != null) {
2019-09-26 12:51:39 +02:00
if (message.from != null && message.from.length > 0) {
2019-09-22 20:03:31 +02:00
EntityIdentity identity = db.identity().getIdentity(message.identity);
if (identity == null)
return null;
2019-09-26 12:51:39 +02:00
for (Address sender : message.from)
2019-09-23 10:47:05 +02:00
if (identity.similarAddress(sender)) {
2019-09-22 20:03:31 +02:00
outgoing = true;
break;
}
}
2019-01-27 14:38:53 +00:00
}
2019-09-27 09:39:41 +02:00
if (outgoing && message.reply != null &&
MessageHelper.equal(message.from, message.to))
return message.reply;
2019-05-13 13:38:27 +02:00
return (outgoing ? message.to : message.from);
2019-01-27 14:38:53 +00:00
}
@Override
protected void onExecuted(Bundle args, Address[] addresses) {
2019-09-22 20:03:31 +02:00
if (addresses == null || addresses.length == 0)
return;
2019-05-13 14:08:51 +02:00
String query = ((InternetAddress) addresses[0]).getAddress();
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(ActivityView.ACTION_SEARCH)
.putExtra("folder", -1L)
.putExtra("query", query));
2019-01-27 14:38:53 +00:00
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2019-01-27 14:38:53 +00:00
}
}.execute(context, owner, args, "message:search");
2019-01-27 13:50:21 +00:00
}
2019-03-07 12:29:03 +00:00
@TargetApi(Build.VERSION_CODES.O)
private void onNotifyContact(final TupleMessageEx message) {
final NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
final InternetAddress from = (InternetAddress) message.from[0];
2019-09-23 22:07:22 +02:00
final String channelId = "notification." + from.getAddress().toLowerCase(Locale.ROOT);
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, ibAddContact);
NotificationChannel channel = nm.getNotificationChannel(channelId);
if (channel == null)
popupMenu.getMenu().add(Menu.NONE, R.string.title_create_channel, 1, R.string.title_create_channel);
else {
popupMenu.getMenu().add(Menu.NONE, R.string.title_edit_channel, 2, R.string.title_edit_channel);
popupMenu.getMenu().add(Menu.NONE, R.string.title_delete_channel, 3, R.string.title_delete_channel);
}
2019-03-07 12:29:03 +00:00
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.string.title_create_channel:
onActionCreateChannel();
return true;
case R.string.title_edit_channel:
onActionEditChannel();
return true;
case R.string.title_delete_channel:
onActionDeleteChannel();
return true;
default:
return false;
}
}
@TargetApi(Build.VERSION_CODES.O)
private void onActionCreateChannel() {
2019-08-13 10:27:17 +02:00
if (!ActivityBilling.isPro(context)) {
context.startActivity(new Intent(context, ActivityBilling.class));
2019-05-16 11:10:01 +02:00
return;
}
NotificationChannel channel = new NotificationChannel(
channelId, from.getAddress(),
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup("contacts");
channel.setDescription(from.getPersonal());
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
channel.enableLights(true);
nm.createNotificationChannel(channel);
onActionEditChannel();
}
private void onActionEditChannel() {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName())
.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
context.startActivity(intent);
}
private void onActionDeleteChannel() {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.deleteNotificationChannel(channelId);
}
});
popupMenu.show();
}
2018-11-13 10:22:41 +01:00
private void onAddContact(TupleMessageEx message) {
2018-10-15 10:05:42 +00:00
for (Address address : message.from) {
InternetAddress ia = (InternetAddress) address;
String name = ia.getPersonal();
String email = ia.getAddress();
// https://developer.android.com/training/contacts-provider/modify-data
Intent edit = new Intent();
if (!TextUtils.isEmpty(name))
edit.putExtra(ContactsContract.Intents.Insert.NAME, name);
if (!TextUtils.isEmpty(email))
edit.putExtra(ContactsContract.Intents.Insert.EMAIL, email);
2019-02-22 16:59:23 +01:00
ContentResolver resolver = context.getContentResolver();
try (Cursor cursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
new String[]{
ContactsContract.CommonDataKinds.Photo.CONTACT_ID,
ContactsContract.Contacts.LOOKUP_KEY
},
ContactsContract.CommonDataKinds.Email.ADDRESS + " = ?",
new String[]{email}, null)) {
2018-12-24 10:51:32 +00:00
if (cursor != null && cursor.moveToNext()) {
2018-10-15 10:05:42 +00:00
int colContactId = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.CONTACT_ID);
int colLookupKey = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
long contactId = cursor.getLong(colContactId);
String lookupKey = cursor.getString(colLookupKey);
Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
edit.setAction(Intent.ACTION_EDIT);
edit.setDataAndType(lookupUri, ContactsContract.Contacts.CONTENT_ITEM_TYPE);
} else {
edit.setAction(Intent.ACTION_INSERT);
edit.setType(ContactsContract.Contacts.CONTENT_TYPE);
}
}
2019-01-23 19:32:37 +00:00
PackageManager pm = context.getPackageManager();
if (edit.resolveActivity(pm) == null)
2019-06-30 16:55:15 +02:00
Snackbar.make(parentFragment.getView(),
R.string.title_no_contacts, Snackbar.LENGTH_LONG).show();
2019-01-23 19:32:37 +00:00
else
context.startActivity(edit);
2018-10-15 10:05:42 +00:00
}
}
2019-02-01 09:29:26 +00:00
private void onToggleMessage(TupleMessageEx message) {
2018-11-13 10:22:41 +01:00
if (EntityFolder.DRAFTS.equals(message.folderType))
context.startActivity(
2018-11-13 10:22:41 +01:00
new Intent(context, ActivityCompose.class)
.putExtra("action", "edit")
.putExtra("id", message.id));
else {
2018-12-21 08:32:26 +01:00
boolean expanded = !properties.getValue("expanded", message.id);
properties.setValue("expanded", message.id, expanded);
2018-12-22 09:06:33 +01:00
2019-09-01 19:19:50 +02:00
ibExpander.setImageLevel(expanded ? 0 /* less*/ : 1 /* more */);
2019-04-17 09:38:24 +02:00
2019-08-29 18:12:34 +02:00
if (expanded)
bindExpanded(message, true);
else
2019-06-05 07:59:34 +02:00
clearExpanded(message);
bindFlagged(message, expanded);
2018-11-13 10:22:41 +01:00
}
2018-11-04 16:16:45 +00:00
}
2019-02-01 09:29:26 +00:00
private void onToggleAddresses(TupleMessageEx message) {
2018-12-21 08:32:26 +01:00
boolean addresses = !properties.getValue("addresses", message.id);
properties.setValue("addresses", message.id, addresses);
2019-08-29 18:12:34 +02:00
bindExpanded(message, false);
2018-11-04 16:16:45 +00:00
}
2019-08-15 15:05:41 +02:00
private void onDownloadAttachments(final TupleMessageEx message) {
Bundle args = new Bundle();
args.putLong("id", message.id);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
DB db = DB.getInstance(context);
try {
db.beginTransaction();
EntityMessage msg = db.message().getMessage(id);
if (msg != null)
for (EntityAttachment attachment : db.attachment().getAttachments(message.id))
if (attachment.progress == null && !attachment.available) {
db.attachment().setProgress(attachment.id, 0);
EntityOperation.queue(context, msg, EntityOperation.ATTACHMENT, attachment.id);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
}.execute(context, owner, args, "message:attachment:download");
}
private void onSaveAttachments(TupleMessageEx message) {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(FragmentMessages.ACTION_STORE_ATTACHMENTS)
.putExtra("id", message.id));
}
2019-10-04 15:25:04 +02:00
private void onShow(final TupleMessageEx message, boolean full) {
if (full) {
boolean current = properties.getValue("full", message.id);
boolean asked = properties.getValue("full_asked", message.id);
if (current || asked) {
if (current) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
for (Address address : message.from) {
String from = ((InternetAddress) address).getAddress();
editor.remove(from + ".show_full");
}
editor.apply();
}
2019-10-04 15:25:04 +02:00
properties.setValue("full", message.id, !current);
onShowFullConfirmed(message);
return;
}
}
2019-10-04 15:25:04 +02:00
View dview = LayoutInflater.from(context).inflate(
full ? R.layout.dialog_show_full : R.layout.dialog_show_images, null);
2019-09-19 09:00:50 +02:00
CheckBox cbNotAgain = dview.findViewById(R.id.cbNotAgain);
2019-01-30 08:47:54 +00:00
2019-09-01 22:33:32 +02:00
if (message.from == null || message.from.length == 0)
cbNotAgain.setVisibility(View.GONE);
else {
List<String> froms = new ArrayList<>();
for (Address address : message.from)
froms.add(((InternetAddress) address).getAddress());
cbNotAgain.setText(context.getString(R.string.title_no_ask_for_again,
TextUtils.join(", ", froms)));
}
2019-01-30 08:47:54 +00:00
2019-09-19 09:00:50 +02:00
cbNotAgain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
for (Address address : message.from) {
String from = ((InternetAddress) address).getAddress();
2019-10-04 15:25:04 +02:00
editor.putBoolean(from + (full ? ".show_full" : ".show_images"), isChecked);
2019-09-19 09:00:50 +02:00
}
editor.apply();
}
});
2019-10-05 10:26:56 +02:00
if (full) {
TextView tvDark = dview.findViewById(R.id.tvDark);
tvDark.setVisibility(Helper.isDarkTheme(context) ? View.VISIBLE : View.GONE);
} else {
2019-10-04 23:04:03 +02:00
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean disable_tracking = prefs.getBoolean("disable_tracking", true);
ImageView ivInfo = dview.findViewById(R.id.ivInfo);
Group grpTracking = dview.findViewById(R.id.grpTracking);
grpTracking.setVisibility(disable_tracking ? View.VISIBLE : View.GONE);
ivInfo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.viewFAQ(context, 82);
}
});
}
2019-07-01 19:54:37 +02:00
// TODO: dialog fragment
final Dialog dialog = new AlertDialog.Builder(context)
2019-01-30 08:47:54 +00:00
.setView(dview)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
2019-10-04 15:25:04 +02:00
if (full) {
properties.setValue("full", message.id, true);
properties.setValue("full_asked", message.id, true);
onShowFullConfirmed(message);
} else {
properties.setValue("images", message.id, true);
onShowImagesConfirmed(message);
}
2019-01-30 08:47:54 +00:00
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
owner.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onCreate() {
dialog.show();
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroyed() {
dialog.dismiss();
}
});
}
2018-10-15 10:05:42 +00:00
2019-10-04 15:25:04 +02:00
private void onShowFullConfirmed(final TupleMessageEx message) {
properties.setSize(message.id, 0);
properties.setHeight(message.id, 0);
bindBody(message);
}
2019-10-04 15:25:04 +02:00
private void onShowImagesConfirmed(final TupleMessageEx message) {
2019-05-04 11:16:33 +02:00
ibImages.setVisibility(View.GONE);
2019-03-28 07:14:45 +00:00
2019-10-04 15:25:04 +02:00
bindBody(message);
// Download inline images
2019-05-04 11:16:33 +02:00
Bundle args = new Bundle();
args.putSerializable("message", message);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
TupleMessageEx message = (TupleMessageEx) args.getSerializable("message");
DB db = DB.getInstance(context);
try {
db.beginTransaction();
List<EntityAttachment> attachments = db.attachment().getAttachments(message.id);
for (EntityAttachment attachment : attachments)
if (!attachment.available && attachment.isInline() && attachment.isImage())
2019-05-18 08:49:20 +02:00
EntityOperation.queue(context, message, EntityOperation.ATTACHMENT, attachment.id);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
}.execute(context, owner, args, "show:images");
2018-10-15 10:05:42 +00:00
}
2019-09-08 12:57:21 +02:00
private void onActionUnsubscribe(TupleMessageEx message) {
Uri uri = Uri.parse(message.unsubscribe);
onOpenLink(uri, context.getString(R.string.title_legend_show_unsubscribe));
}
2019-08-14 20:08:24 +02:00
private void onActionDecrypt(TupleMessageEx message) {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(FragmentMessages.ACTION_DECRYPT)
.putExtra("id", message.id));
2019-05-04 11:16:33 +02:00
}
2019-08-14 20:08:24 +02:00
private void onActionReplyMenu(TupleMessageEx message) {
Bundle args = new Bundle();
args.putSerializable("message", message);
2019-02-06 18:40:12 +00:00
2019-09-23 13:04:34 +02:00
new SimpleTask<List<TupleIdentityEx>>() {
2019-08-14 20:08:24 +02:00
@Override
2019-09-23 13:04:34 +02:00
protected List<TupleIdentityEx> onExecute(Context context, Bundle args) {
2019-10-02 13:10:39 +02:00
TupleMessageEx message = (TupleMessageEx) args.getSerializable("message");
if (message == null)
return null;
2019-08-14 20:08:24 +02:00
DB db = DB.getInstance(context);
2019-10-02 13:10:39 +02:00
return db.identity().getComposableIdentities(message.account);
2019-08-14 20:08:24 +02:00
}
2019-05-13 22:14:21 +02:00
2019-08-14 20:08:24 +02:00
@Override
2019-09-23 13:04:34 +02:00
protected void onExecuted(Bundle args, List<TupleIdentityEx> identities) {
2019-08-14 20:08:24 +02:00
TupleMessageEx message = (TupleMessageEx) args.getSerializable("message");
2018-12-20 21:12:28 +01:00
2019-08-14 20:08:24 +02:00
TupleMessageEx amessage = getMessage();
if (amessage == null || !amessage.id.equals(message.id))
return;
2019-10-03 14:34:48 +02:00
final Address[] to =
message.replySelf(identities, message.account)
? message.to
: (message.reply == null || message.reply.length == 0 ? message.from : message.reply);
2019-10-02 13:10:39 +02:00
Address[] recipients = message.getAllRecipients(identities, message.account);
2018-12-20 21:12:28 +01:00
2019-08-16 08:12:04 +02:00
View anchor = bnvActions.findViewById(R.id.action_reply);
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, anchor);
2019-09-23 16:25:48 +02:00
popupMenu.inflate(R.menu.popup_reply);
2019-08-14 20:08:24 +02:00
popupMenu.getMenu().findItem(R.id.menu_reply_to_all).setVisible(recipients.length > 0);
popupMenu.getMenu().findItem(R.id.menu_reply_list).setVisible(message.list_post != null);
popupMenu.getMenu().findItem(R.id.menu_reply_receipt).setVisible(message.receipt_to != null);
popupMenu.getMenu().findItem(R.id.menu_reply_answer).setVisible(answers != 0 || !ActivityBilling.isPro(context));
2019-10-03 14:34:48 +02:00
popupMenu.getMenu().findItem(R.id.menu_new_message).setVisible(to != null && to.length > 0);
2018-12-20 21:12:28 +01:00
2019-08-14 20:08:24 +02:00
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem target) {
switch (target.getItemId()) {
case R.id.menu_reply_to_sender:
onMenuReply(message, "reply");
return true;
case R.id.menu_reply_to_all:
onMenuReply(message, "reply_all");
return true;
case R.id.menu_reply_list:
onMenuReply(message, "list");
return true;
case R.id.menu_reply_receipt:
onMenuReply(message, "receipt");
return true;
case R.id.menu_reply_answer:
onMenuAnswer(message);
return true;
2019-08-21 16:51:18 +02:00
case R.id.menu_forward:
onMenuReply(message, "forward");
2019-10-03 14:34:48 +02:00
return true;
case R.id.menu_new_message:
onMenuNew(message, to);
return true;
2019-08-14 20:08:24 +02:00
default:
return false;
}
}
});
popupMenu.show();
}
2019-05-03 09:41:50 +02:00
2019-08-14 20:08:24 +02:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2019-05-02 13:29:05 +02:00
}
2019-08-14 20:08:24 +02:00
}.execute(context, owner, args, "message:reply");
}
2019-05-02 13:29:05 +02:00
2019-08-14 20:08:24 +02:00
private void onMenuReply(TupleMessageEx message, String action) {
Intent reply = new Intent(context, ActivityCompose.class)
.putExtra("action", action)
.putExtra("reference", message.id);
context.startActivity(reply);
2019-08-14 20:08:24 +02:00
}
2019-10-03 14:34:48 +02:00
private void onMenuNew(TupleMessageEx message, Address[] to) {
Intent reply = new Intent(context, ActivityCompose.class)
.putExtra("action", "new")
.putExtra("to", MessageHelper.formatAddresses(to, true, true));
context.startActivity(reply);
}
2019-08-14 20:08:24 +02:00
private void onMenuAnswer(TupleMessageEx message) {
new SimpleTask<List<EntityAnswer>>() {
@Override
protected List<EntityAnswer> onExecute(Context context, Bundle args) {
return DB.getInstance(context).answer().getAnswers(false);
}
2019-08-14 20:08:24 +02:00
@Override
protected void onExecuted(Bundle args, List<EntityAnswer> answers) {
if (answers == null || answers.size() == 0) {
Snackbar snackbar = Snackbar.make(
parentFragment.getView(),
context.getString(R.string.title_no_answers),
Snackbar.LENGTH_LONG);
snackbar.setAction(R.string.title_fix, new View.OnClickListener() {
@Override
public void onClick(View v) {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(new Intent(ActivityView.ACTION_EDIT_ANSWERS));
}
});
snackbar.show();
} else {
2019-08-16 08:12:04 +02:00
View anchor = bnvActions.findViewById(R.id.action_reply);
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, anchor);
2019-02-01 09:02:30 +00:00
2019-08-14 20:08:24 +02:00
int order = 0;
for (EntityAnswer answer : answers)
popupMenu.getMenu().add(Menu.NONE, answer.id.intValue(), order++, answer.name);
2018-12-20 21:12:28 +01:00
2019-08-14 20:08:24 +02:00
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem target) {
if (!ActivityBilling.isPro(context)) {
context.startActivity(new Intent(context, ActivityBilling.class));
2019-08-14 20:08:24 +02:00
return true;
}
2019-02-01 09:02:30 +00:00
context.startActivity(new Intent(context, ActivityCompose.class)
.putExtra("action", "reply")
.putExtra("reference", message.id)
.putExtra("answer", (long) target.getItemId()));
2019-08-14 20:08:24 +02:00
return true;
}
});
2019-08-14 20:08:24 +02:00
popupMenu.show();
}
}
2019-08-14 20:08:24 +02:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
}.execute(context, owner, new Bundle(), "message:answer");
}
2018-10-15 10:05:42 +00:00
2019-08-14 20:08:24 +02:00
private void onActionArchive(TupleMessageEx message) {
properties.move(message.id, EntityFolder.ARCHIVE);
2019-02-03 18:38:56 +00:00
}
2019-08-14 20:08:24 +02:00
private void onActionMove(TupleMessageEx message, final boolean copy) {
2019-07-06 12:52:00 +02:00
Bundle args = new Bundle();
2019-08-14 20:08:24 +02:00
args.putString("title", context.getString(copy ? R.string.title_copy_to : R.string.title_move_to_folder));
args.putLong("account", message.account);
args.putLongArray("disabled", new long[]{message.folder});
args.putLong("message", message.id);
args.putBoolean("copy", copy);
args.putBoolean("similar", false);
2019-04-19 08:31:00 +02:00
2019-08-14 20:08:24 +02:00
FragmentDialogFolder fragment = new FragmentDialogFolder();
2019-07-06 12:52:00 +02:00
fragment.setArguments(args);
2019-08-14 20:08:24 +02:00
fragment.setTargetFragment(parentFragment, FragmentMessages.REQUEST_MESSAGE_MOVE);
fragment.show(parentFragment.getFragmentManager(), "message:move");
2019-07-21 11:54:36 +02:00
}
2019-08-14 20:08:24 +02:00
private void onActionMoveOutbox(TupleMessageEx message) {
2018-10-15 10:05:42 +00:00
Bundle args = new Bundle();
2019-06-28 18:49:50 +02:00
args.putLong("id", message.id);
2018-10-15 10:05:42 +00:00
new SimpleTask<Void>() {
@Override
2018-12-31 07:03:48 +00:00
protected Void onExecute(Context context, Bundle args) {
2018-10-15 10:05:42 +00:00
long id = args.getLong("id");
2019-08-14 20:08:24 +02:00
EntityMessage message;
2018-10-15 10:05:42 +00:00
DB db = DB.getInstance(context);
try {
db.beginTransaction();
2019-08-14 20:08:24 +02:00
message = db.message().getMessage(id);
2019-01-02 19:38:32 +01:00
if (message == null)
return null;
2019-08-14 20:08:24 +02:00
db.folder().setFolderError(message.folder, null);
File source = message.getFile(context);
// Insert into drafts
EntityFolder drafts = db.folder().getFolderByType(message.account, EntityFolder.DRAFTS);
message.id = null;
message.folder = drafts.id;
message.ui_snoozed = null;
message.error = null;
message.id = db.message().insertMessage(message);
File target = message.getFile(context);
source.renameTo(target);
List<EntityAttachment> attachments = db.attachment().getAttachments(id);
for (EntityAttachment attachment : attachments)
db.attachment().setMessage(attachment.id, message.id);
EntityOperation.queue(context, message, EntityOperation.ADD);
// Delete from outbox
db.message().deleteMessage(id); // will delete operation too
2018-10-15 10:05:42 +00:00
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
2019-08-14 20:08:24 +02:00
if (message.identity != null) {
// Identity can be deleted
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel("send:" + message.identity, 1);
}
2018-10-15 10:05:42 +00:00
2019-08-14 20:08:24 +02:00
return null;
2018-10-15 10:05:42 +00:00
}
2018-12-01 10:47:08 +01:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2018-12-01 10:47:08 +01:00
}
2019-08-14 20:08:24 +02:00
}.execute(context, owner, args, "message:move:draft");
2018-10-15 10:05:42 +00:00
}
2019-08-14 20:08:24 +02:00
private void onActionMoveJunk(TupleMessageEx message) {
properties.move(message.id, EntityFolder.INBOX);
}
2019-05-15 11:10:47 +02:00
2019-08-14 20:08:24 +02:00
private void onActionDelete(TupleMessageEx message) {
if (delete) {
Bundle aargs = new Bundle();
aargs.putString("question", context.getString(R.string.title_ask_delete));
aargs.putLong("id", message.id);
2019-06-30 16:55:15 +02:00
2019-08-14 20:08:24 +02:00
FragmentDialogAsk ask = new FragmentDialogAsk();
ask.setArguments(aargs);
ask.setTargetFragment(parentFragment, FragmentMessages.REQUEST_MESSAGE_DELETE);
ask.show(parentFragment.getFragmentManager(), "message:delete");
} else
properties.move(message.id, EntityFolder.TRASH);
2019-05-15 11:10:47 +02:00
}
2019-08-14 20:08:24 +02:00
private void onActionMore(TupleMessageEx message) {
boolean show_headers = properties.getValue("headers", message.id);
2019-01-25 09:01:09 +00:00
2019-08-16 08:12:04 +02:00
View anchor = bnvActions.findViewById(R.id.action_more);
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, anchor);
2019-09-23 16:25:48 +02:00
popupMenu.inflate(R.menu.popup_message_more);
2019-01-25 09:01:09 +00:00
2019-08-14 20:08:24 +02:00
popupMenu.getMenu().findItem(R.id.menu_editasnew).setEnabled(message.content);
2019-01-25 09:01:09 +00:00
popupMenu.getMenu().findItem(R.id.menu_unseen).setTitle(message.ui_seen ? R.string.title_unseen : R.string.title_seen);
2019-09-29 12:03:29 +02:00
popupMenu.getMenu().findItem(R.id.menu_unseen).setEnabled(
(message.uid != null && !message.folderReadOnly) || message.accountPop);
2019-09-28 09:52:58 +02:00
2019-09-28 10:05:45 +02:00
popupMenu.getMenu().findItem(R.id.menu_flag_color).setEnabled(
(message.uid != null && !message.folderReadOnly) || message.accountPop);
2019-01-25 09:01:09 +00:00
2019-08-14 20:08:24 +02:00
popupMenu.getMenu().findItem(R.id.menu_copy).setEnabled(message.uid != null && !message.folderReadOnly);
2019-09-28 09:52:58 +02:00
popupMenu.getMenu().findItem(R.id.menu_copy).setVisible(!message.accountPop);
popupMenu.getMenu().findItem(R.id.menu_delete).setVisible(!message.accountPop);
popupMenu.getMenu().findItem(R.id.menu_resync).setEnabled(message.uid != null);
popupMenu.getMenu().findItem(R.id.menu_resync).setVisible(!message.accountPop);
popupMenu.getMenu().findItem(R.id.menu_create_rule).setVisible(!message.accountPop);
popupMenu.getMenu().findItem(R.id.menu_manage_keywords).setEnabled(message.uid != null && !message.folderReadOnly);
popupMenu.getMenu().findItem(R.id.menu_manage_keywords).setVisible(!message.accountPop);
2019-08-14 20:08:24 +02:00
popupMenu.getMenu().findItem(R.id.menu_junk).setEnabled(message.uid != null && !message.folderReadOnly);
popupMenu.getMenu().findItem(R.id.menu_junk).setVisible(hasJunk && !EntityFolder.JUNK.equals(message.folderType));
2019-08-14 20:08:24 +02:00
popupMenu.getMenu().findItem(R.id.menu_share).setEnabled(message.content);
popupMenu.getMenu().findItem(R.id.menu_print).setEnabled(hasWebView && message.content);
popupMenu.getMenu().findItem(R.id.menu_print).setVisible(Helper.canPrint(context));
2019-08-14 20:08:24 +02:00
popupMenu.getMenu().findItem(R.id.menu_show_headers).setChecked(show_headers);
popupMenu.getMenu().findItem(R.id.menu_show_headers).setEnabled(message.uid != null);
2019-09-28 09:52:58 +02:00
popupMenu.getMenu().findItem(R.id.menu_show_headers).setVisible(!message.accountPop);
2019-03-11 16:02:40 +00:00
2019-09-09 14:06:43 +02:00
popupMenu.getMenu().findItem(R.id.menu_raw_download).setEnabled(
message.uid != null && (message.raw == null || !message.raw));
popupMenu.getMenu().findItem(R.id.menu_raw_save).setEnabled(
message.uid != null && (message.raw != null && message.raw));
popupMenu.getMenu().findItem(R.id.menu_raw_send).setEnabled(
message.uid != null && (message.raw != null && message.raw));
2019-04-04 17:54:05 +02:00
2019-09-28 09:52:58 +02:00
popupMenu.getMenu().findItem(R.id.menu_raw_download).setVisible(!message.accountPop);
popupMenu.getMenu().findItem(R.id.menu_raw_save).setVisible(!message.accountPop);
popupMenu.getMenu().findItem(R.id.menu_raw_send).setVisible(!message.accountPop);
2019-09-19 17:41:26 +02:00
2019-08-14 20:08:24 +02:00
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
2019-04-04 17:54:05 +02:00
@Override
2019-08-14 20:08:24 +02:00
public boolean onMenuItemClick(MenuItem target) {
switch (target.getItemId()) {
case R.id.menu_editasnew:
onMenuEditAsNew(message);
return true;
case R.id.menu_unseen:
onMenuUnseen(message);
return true;
case R.id.menu_flag_color:
onMenuColoredStar(message);
return true;
case R.id.menu_copy:
onActionMove(message, true);
return true;
case R.id.menu_delete:
onMenuDelete(message);
return true;
case R.id.menu_junk:
onMenuJunk(message);
return true;
case R.id.menu_resync:
onMenuResync(message);
return true;
case R.id.menu_create_rule:
onMenuCreateRule(message);
return true;
case R.id.menu_manage_keywords:
onMenuManageKeywords(message);
return true;
case R.id.menu_share:
onMenuShare(message);
return true;
case R.id.menu_print:
onMenuPrint(message);
return true;
case R.id.menu_show_headers:
onMenuShowHeaders(message);
return true;
2019-09-09 14:06:43 +02:00
case R.id.menu_raw_download:
onMenuRawDownload(message);
return true;
case R.id.menu_raw_save:
onMenuRawSave(message);
return true;
case R.id.menu_raw_send:
onMenuRawSend(message);
2019-08-14 20:08:24 +02:00
return true;
default:
return false;
}
2019-04-04 17:54:05 +02:00
}
2019-08-14 20:08:24 +02:00
});
popupMenu.show();
2019-04-04 17:54:05 +02:00
}
2019-08-14 20:08:24 +02:00
private class TouchHandler extends ArrowKeyMovementMethod {
private TupleMessageEx message;
2018-10-16 10:56:05 +00:00
2019-08-14 20:08:24 +02:00
TouchHandler(TupleMessageEx message) {
this.message = message;
}
2018-10-16 10:56:05 +00:00
2019-08-14 20:08:24 +02:00
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
int x = (int) event.getX();
int y = (int) event.getY();
2019-01-02 19:38:32 +01:00
2019-08-14 20:08:24 +02:00
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
2019-01-02 19:38:32 +01:00
2019-08-14 20:08:24 +02:00
x += widget.getScrollX();
y += widget.getScrollY();
2019-01-02 19:38:32 +01:00
2019-08-14 20:08:24 +02:00
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
2019-01-02 19:38:32 +01:00
2019-08-14 20:08:24 +02:00
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) {
2019-10-04 21:25:52 +02:00
ImageHelper.AnnotatedSource a = new ImageHelper.AnnotatedSource(image[0].getSource());
2019-08-14 20:08:24 +02:00
Uri uri = Uri.parse(a.getSource());
if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
onOpenLink(uri, null);
return true;
}
2019-01-02 19:38:32 +01:00
}
2018-10-17 18:29:24 +00:00
}
2019-08-14 20:08:24 +02:00
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
if (link.length > 0) {
String url = link[0].getURL();
Uri uri = Uri.parse(url);
if (uri.getScheme() == null)
uri = Uri.parse("https://" + url);
2019-08-14 20:08:24 +02:00
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;
2019-08-14 20:08:24 +02:00
onOpenLink(uri, title);
return true;
}
2019-08-14 20:08:24 +02:00
ImageSpan[] image = buffer.getSpans(off, off, ImageSpan.class);
if (image.length > 0) {
String source = image[0].getSource();
if (source != null) {
onOpenImage(message.id, source);
return true;
}
}
2019-08-14 20:08:24 +02:00
DynamicDrawableSpan[] ddss = buffer.getSpans(off, off, DynamicDrawableSpan.class);
if (ddss.length > 0) {
properties.setValue("quotes", message.id, true);
2019-10-04 15:25:04 +02:00
bindBody(message);
}
2019-08-14 20:08:24 +02:00
}
return super.onTouchEvent(widget, buffer, event);
}
}
private void onOpenLink(final Uri uri, String title) {
Log.i("Opening uri=" + uri);
if (BuildConfig.APPLICATION_ID.equals(uri.getHost()) && "/activate/".equals(uri.getPath())) {
try {
if (ActivityBilling.activatePro(context, uri))
ToastEx.makeText(context, R.string.title_pro_valid, Toast.LENGTH_LONG).show();
else
ToastEx.makeText(context, R.string.title_pro_invalid, Toast.LENGTH_LONG).show();
} catch (NoSuchAlgorithmException ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
} else {
2019-08-14 20:08:24 +02:00
if ("cid".equals(uri.getScheme()))
return;
Bundle args = new Bundle();
args.putParcelable("uri", uri);
args.putString("title", title);
FragmentDialogLink fragment = new FragmentDialogLink();
fragment.setArguments(args);
fragment.show(parentFragment.getFragmentManager(), "open:link");
}
}
2019-08-14 20:08:24 +02:00
private void onOpenImage(long id, String source) {
Log.i("Viewing image source=" + source);
2018-10-16 10:56:05 +00:00
2019-08-14 20:08:24 +02:00
Bundle args = new Bundle();
args.putLong("id", id);
args.putString("source", source);
2018-10-21 14:53:39 +00:00
2019-08-14 20:08:24 +02:00
FragmentDialogImage fragment = new FragmentDialogImage();
fragment.setArguments(args);
fragment.show(parentFragment.getFragmentManager(), "view:image");
}
2018-11-13 20:05:09 +01:00
2019-08-14 20:08:24 +02:00
private void onMenuEditAsNew(final TupleMessageEx message) {
Intent asnew = new Intent(context, ActivityCompose.class)
.putExtra("action", "editasnew")
.putExtra("reference", message.id);
context.startActivity(asnew);
2019-08-14 20:08:24 +02:00
}
2019-08-14 20:08:24 +02:00
private void onMenuUnseen(final TupleMessageEx message) {
Bundle args = new Bundle();
args.putLong("id", message.id);
args.putBoolean("seen", !message.ui_seen);
2019-01-25 09:01:09 +00:00
2019-08-14 20:08:24 +02:00
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
boolean seen = args.getBoolean("seen");
2019-01-03 20:31:45 +01:00
2019-08-14 20:08:24 +02:00
DB db = DB.getInstance(context);
try {
db.beginTransaction();
2018-10-21 14:53:39 +00:00
2019-08-14 20:08:24 +02:00
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
EntityOperation.queue(context, message, EntityOperation.SEEN, seen);
2018-11-26 12:42:06 +01:00
2019-08-14 20:08:24 +02:00
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
2018-10-16 10:56:05 +00:00
2019-08-14 20:08:24 +02:00
return null;
}
2019-04-04 17:54:05 +02:00
2018-10-17 18:29:24 +00:00
@Override
2019-08-14 20:08:24 +02:00
protected void onExecuted(Bundle args, Void ignored) {
boolean seen = args.getBoolean("seen");
if (!seen)
properties.setValue("expanded", message.id, false);
2019-08-14 20:08:24 +02:00
notifyDataSetChanged();
2018-10-17 18:29:24 +00:00
}
2019-08-14 20:08:24 +02:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
}.execute(context, owner, args, "message:unseen");
2018-10-16 10:56:05 +00:00
}
2019-08-14 20:08:24 +02:00
private void onMenuColoredStar(final TupleMessageEx message) {
Bundle args = new Bundle();
args.putLong("id", message.id);
2019-09-29 16:31:13 +02:00
args.putInt("color", message.color == null ? Color.TRANSPARENT : message.color);
args.putString("title", context.getString(R.string.title_flag_color));
2019-08-14 20:08:24 +02:00
FragmentDialogColor fragment = new FragmentDialogColor();
2019-09-29 16:31:13 +02:00
fragment.setArguments(args);
2019-08-14 20:08:24 +02:00
fragment.setTargetFragment(parentFragment, FragmentMessages.REQUEST_MESSAGE_COLOR);
fragment.show(parentFragment.getFragmentManager(), "message:color");
2018-10-15 10:05:42 +00:00
}
2019-08-14 20:08:24 +02:00
private void onMenuDelete(final TupleMessageEx message) {
2019-09-22 21:07:51 +02:00
Bundle aargs = new Bundle();
aargs.putString("question", context.getString(R.string.title_ask_delete));
aargs.putLong("id", message.id);
2019-08-14 20:08:24 +02:00
2019-09-22 21:07:51 +02:00
FragmentDialogAsk ask = new FragmentDialogAsk();
ask.setArguments(aargs);
ask.setTargetFragment(parentFragment, FragmentMessages.REQUEST_MESSAGE_DELETE);
ask.show(parentFragment.getFragmentManager(), "message:delete");
2019-08-14 20:08:24 +02:00
}
private void onMenuJunk(final TupleMessageEx message) {
String who = MessageHelper.formatAddresses(message.from);
Bundle aargs = new Bundle();
aargs.putString("question", context.getString(R.string.title_ask_spam_who, who));
aargs.putLong("id", message.id);
FragmentDialogAsk ask = new FragmentDialogAsk();
ask.setArguments(aargs);
ask.setTargetFragment(parentFragment, FragmentMessages.REQUEST_MESSAGE_JUNK);
ask.show(parentFragment.getFragmentManager(), "message:junk");
2018-10-15 10:05:42 +00:00
}
2019-08-14 20:08:24 +02:00
private void onMenuResync(TupleMessageEx message) {
2019-05-25 17:37:01 +02:00
Bundle args = new Bundle();
2019-08-14 20:08:24 +02:00
args.putLong("id", message.id);
2019-05-25 17:37:01 +02:00
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
DB db = DB.getInstance(context);
try {
db.beginTransaction();
2019-08-14 20:08:24 +02:00
EntityMessage message = db.message().getMessage(id);
if (message == null || message.uid == null)
2019-05-25 17:37:01 +02:00
return null;
2019-08-14 20:08:24 +02:00
db.message().deleteMessage(id);
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
EntityOperation.sync(context, message.folder, true);
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
return null;
}
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
}.execute(context, owner, args, "message:share");
}
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
private void onMenuCreateRule(TupleMessageEx message) {
Intent rule = new Intent(ActivityView.ACTION_EDIT_RULE);
rule.putExtra("account", message.account);
rule.putExtra("folder", message.folder);
if (message.from != null && message.from.length > 0)
rule.putExtra("sender", ((InternetAddress) message.from[0]).getAddress());
if (message.to != null && message.to.length > 0)
rule.putExtra("recipient", ((InternetAddress) message.to[0]).getAddress());
if (!TextUtils.isEmpty(message.subject))
rule.putExtra("subject", message.subject);
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(rule);
}
private void onMenuManageKeywords(TupleMessageEx message) {
Bundle args = new Bundle();
args.putLong("id", message.id);
args.putStringArray("keywords", message.keywords);
new SimpleTask<EntityFolder>() {
@Override
protected EntityFolder onExecute(Context context, Bundle args) {
long id = args.getLong("id");
DB db = DB.getInstance(context);
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
return db.folder().getFolder(message.folder);
}
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
@Override
protected void onExecuted(final Bundle args, EntityFolder folder) {
if (folder == null)
return;
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
args.putStringArray("fkeywords", folder.keywords);
2019-05-25 17:37:01 +02:00
2019-08-14 20:08:24 +02:00
FragmentKeywordManage fragment = new FragmentKeywordManage();
fragment.setArguments(args);
fragment.show(parentFragment.getFragmentManager(), "keyword:manage");
2019-05-25 17:37:01 +02:00
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2019-05-25 17:37:01 +02:00
}
2019-08-14 20:08:24 +02:00
}.execute(context, owner, args, "message:keywords");
2019-05-25 17:37:01 +02:00
}
2019-08-14 20:08:24 +02:00
private void onMenuShare(TupleMessageEx message) {
2019-05-29 14:09:54 +02:00
Bundle args = new Bundle();
2019-08-14 20:08:24 +02:00
args.putLong("id", message.id);
2019-05-29 09:14:56 +02:00
2019-08-14 20:08:24 +02:00
new SimpleTask<String[]>() {
2019-05-29 14:09:54 +02:00
@Override
2019-08-14 20:08:24 +02:00
protected String[] onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
2019-05-22 16:21:04 +02:00
2019-05-29 14:09:54 +02:00
DB db = DB.getInstance(context);
2019-08-14 20:08:24 +02:00
EntityMessage message = db.message().getMessage(id);
if (message == null || !message.content)
return null;
2019-03-02 08:48:02 +00:00
2019-08-14 20:08:24 +02:00
File file = message.getFile(context);
if (!file.exists())
return null;
2019-05-29 14:09:54 +02:00
2019-08-14 20:08:24 +02:00
String from = null;
if (message.from != null && message.from.length > 0)
from = ((InternetAddress) message.from[0]).getAddress();
2019-05-29 14:09:54 +02:00
2019-08-14 20:08:24 +02:00
String html = HtmlHelper.getText(Helper.readText(file));
2019-05-29 14:09:54 +02:00
2019-08-14 20:08:24 +02:00
return new String[]{from, message.subject, html};
}
@Override
protected void onExecuted(Bundle args, String[] text) {
if (text == null)
2019-05-29 14:09:54 +02:00
return;
2019-08-14 20:08:24 +02:00
Intent share = new Intent();
share.setAction(Intent.ACTION_SEND);
share.setType("text/plain");
if (!TextUtils.isEmpty(text[0]))
share.putExtra(Intent.EXTRA_EMAIL, new String[]{text[0]});
if (!TextUtils.isEmpty(text[1]))
share.putExtra(Intent.EXTRA_SUBJECT, text[1]);
if (!TextUtils.isEmpty(text[2]))
share.putExtra(Intent.EXTRA_TEXT, text[2]);
2019-05-29 14:09:54 +02:00
2019-08-14 20:08:24 +02:00
PackageManager pm = context.getPackageManager();
if (share.resolveActivity(pm) == null)
Snackbar.make(parentFragment.getView(),
R.string.title_no_viewer, Snackbar.LENGTH_LONG).show();
else
context.startActivity(share);
2019-03-02 08:48:02 +00:00
}
2019-05-29 14:09:54 +02:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
2019-05-29 14:09:54 +02:00
}
2019-08-14 20:08:24 +02:00
}.execute(context, owner, args, "message:share");
2019-03-02 08:48:02 +00:00
}
2019-08-14 20:08:24 +02:00
private void onMenuPrint(TupleMessageEx message) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("print_html_confirmed", false)) {
Bundle args = new Bundle();
args.putLong("id", message.id);
Intent data = new Intent();
data.putExtra("args", args);
parentFragment.onActivityResult(FragmentMessages.REQUEST_PRINT, RESULT_OK, data);
return;
}
Bundle aargs = new Bundle();
aargs.putString("question", context.getString(R.string.title_ask_show_html));
aargs.putString("notagain", "print_html_confirmed");
aargs.putLong("id", message.id);
FragmentDialogAsk ask = new FragmentDialogAsk();
ask.setArguments(aargs);
ask.setTargetFragment(parentFragment, FragmentMessages.REQUEST_PRINT);
ask.show(parentFragment.getFragmentManager(), "message:print");
}
2018-10-24 09:09:07 +00:00
2019-08-14 20:08:24 +02:00
private void onMenuShowHeaders(TupleMessageEx message) {
boolean show_headers = !properties.getValue("headers", message.id);
properties.setValue("headers", message.id, show_headers);
if (show_headers && message.headers == null) {
grpHeaders.setVisibility(View.VISIBLE);
if (suitable)
pbHeaders.setVisibility(View.VISIBLE);
else
tvNoInternetHeaders.setVisibility(View.VISIBLE);
2019-03-02 08:48:02 +00:00
2019-08-14 20:08:24 +02:00
Bundle args = new Bundle();
args.putLong("id", message.id);
2019-03-02 08:48:02 +00:00
2019-08-14 20:08:24 +02:00
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
Long id = args.getLong("id");
2019-03-02 08:48:02 +00:00
2019-08-14 20:08:24 +02:00
DB db = DB.getInstance(context);
try {
db.beginTransaction();
2019-08-14 20:08:24 +02:00
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
2019-03-02 08:48:02 +00:00
2019-08-14 20:08:24 +02:00
EntityOperation.queue(context, message, EntityOperation.HEADERS);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return null;
2019-03-02 08:48:02 +00:00
}
2019-08-14 20:08:24 +02:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
}.execute(context, owner, args, "message:headers");
} else
notifyDataSetChanged();
}
2019-09-09 14:06:43 +02:00
private void onMenuRawDownload(TupleMessageEx message) {
Bundle args = new Bundle();
args.putLong("id", message.id);
2019-08-14 20:08:24 +02:00
2019-09-09 14:06:43 +02:00
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
Long id = args.getLong("id");
2019-08-14 20:08:24 +02:00
2019-09-09 14:06:43 +02:00
DB db = DB.getInstance(context);
try {
db.beginTransaction();
2019-08-14 20:08:24 +02:00
2019-09-09 14:06:43 +02:00
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
2019-08-14 20:08:24 +02:00
2019-09-09 14:06:43 +02:00
EntityOperation.queue(context, message, EntityOperation.RAW);
2019-08-14 20:08:24 +02:00
2019-09-09 14:06:43 +02:00
db.message().setMessageRaw(message.id, false);
2019-08-14 20:08:24 +02:00
2019-09-09 14:06:43 +02:00
db.setTransactionSuccessful();
} finally {
db.endTransaction();
2019-08-14 20:08:24 +02:00
}
2019-09-09 14:06:43 +02:00
return null;
}
2019-08-14 20:08:24 +02:00
2019-09-09 14:06:43 +02:00
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(parentFragment.getFragmentManager(), ex);
}
}.execute(context, owner, args, "message:raw");
}
private void onMenuRawSave(TupleMessageEx message) {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
new Intent(FragmentMessages.ACTION_STORE_RAW)
.putExtra("id", message.id));
}
private void onMenuRawSend(TupleMessageEx message) {
File file = message.getRawFile(context);
Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, file);
Intent send = new Intent(Intent.ACTION_SEND);
send.putExtra(Intent.EXTRA_STREAM, uri);
send.setType("message/rfc822");
context.startActivity(send);
2019-03-02 08:48:02 +00:00
}
2018-10-24 09:09:07 +00:00
ItemDetailsLookup.ItemDetails<Long> getItemDetails(@NonNull MotionEvent motionEvent) {
2018-12-03 13:18:23 +01:00
return new ItemDetailsMessage(this);
}
Long getKey() {
return getKeyAtPosition(getAdapterPosition());
2018-10-24 09:09:07 +00:00
}
2018-08-02 13:33:06 +00:00
}
AdapterMessage(Fragment parentFragment,
2019-07-19 08:27:44 +02:00
String type, ViewType viewType,
2019-09-03 10:33:52 +02:00
boolean compact, int zoom, String sort, boolean ascending, boolean filter_duplicates,
final IProperties properties) {
2019-06-30 16:55:15 +02:00
this.parentFragment = parentFragment;
2019-07-19 08:27:44 +02:00
this.type = type;
2018-08-02 13:33:06 +00:00
this.viewType = viewType;
2019-01-15 17:39:12 +00:00
this.compact = compact;
2018-12-30 16:33:52 +00:00
this.zoom = zoom;
2019-01-21 18:12:22 +00:00
this.sort = sort;
2019-09-03 10:33:52 +02:00
this.ascending = ascending;
2019-04-28 15:16:26 +02:00
this.filter_duplicates = filter_duplicates;
this.properties = properties;
this.context = parentFragment.getContext();
this.owner = parentFragment.getViewLifecycleOwner();
this.suitable = ConnectionHelper.getNetworkState(context).isSuitable();
this.inflater = LayoutInflater.from(context);
this.TF = Helper.getTimeInstance(context, SimpleDateFormat.SHORT);
2019-07-15 21:28:25 +02:00
this.DTF = Helper.getDateTimeInstance(context, SimpleDateFormat.LONG, SimpleDateFormat.LONG);
this.dp36 = Helper.dp2pixels(context, 36);
this.colorPrimary = Helper.resolveColor(context, R.attr.colorPrimary);
this.colorAccent = Helper.resolveColor(context, R.attr.colorAccent);
this.textColorPrimary = Helper.resolveColor(context, android.R.attr.textColorPrimary);
this.textColorSecondary = Helper.resolveColor(context, android.R.attr.textColorSecondary);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean highlight_unread = prefs.getBoolean("highlight_unread", false);
if (highlight_unread)
this.colorUnread = Helper.resolveColor(context, R.attr.colorUnread);
else
this.colorUnread = this.textColorPrimary;
2019-10-02 14:07:55 +02:00
this.colorSeparator = Helper.resolveColor(context, R.attr.colorSeparator);
this.hasWebView = Helper.hasWebView(context);
2019-02-07 09:02:40 +00:00
this.contacts = Helper.hasPermission(context, Manifest.permission.READ_CONTACTS);
this.textSize = Helper.getTextSize(context, zoom);
this.date = prefs.getBoolean("date", true);
this.threading = prefs.getBoolean("threading", true);
2019-08-25 11:14:16 +02:00
this.name_email = prefs.getBoolean("name_email", false);
2019-10-03 10:19:27 +02:00
this.distinguish_contacts = prefs.getBoolean("distinguish_contacts", false);
this.subject_top = prefs.getBoolean("subject_top", false);
this.subject_italic = prefs.getBoolean("subject_italic", true);
this.subject_ellipsize = prefs.getString("subject_ellipsize", "middle");
2019-03-02 15:54:38 +00:00
this.flags = prefs.getBoolean("flags", true);
this.flags_background = prefs.getBoolean("flags_background", false);
2018-11-04 12:42:41 +00:00
this.preview = prefs.getBoolean("preview", false);
this.preview_italic = prefs.getBoolean("preview_italic", true);
this.attachments_alt = prefs.getBoolean("attachments_alt", false);
this.contrast = prefs.getBoolean("contrast", false);
this.monospaced = prefs.getBoolean("monospaced", false);
2019-10-04 15:25:04 +02:00
this.inline = prefs.getBoolean("inline_images", false);
this.collapse_quotes = prefs.getBoolean("collapse_quotes", false);
this.authentication = prefs.getBoolean("authentication", true);
2019-07-11 20:16:58 +02:00
debug = prefs.getBoolean("debug", false);
2019-05-04 10:24:42 +02:00
AsyncDifferConfig<TupleMessageEx> config = new AsyncDifferConfig.Builder<>(DIFF_CALLBACK)
.build();
this.differ = new AsyncPagedListDiffer<>(new AdapterListUpdateCallback(this), config);
2019-05-04 10:24:42 +02:00
this.differ.addPagedListListener(new AsyncPagedListDiffer.PagedListListener<TupleMessageEx>() {
@Override
public void onCurrentListChanged(@Nullable PagedList<TupleMessageEx> previousList, @Nullable PagedList<TupleMessageEx> currentList) {
2019-10-01 15:20:24 +02:00
boolean autoscroll =
2019-10-02 21:22:59 +02:00
(viewType == AdapterMessage.ViewType.THREAD ||
(viewType != ViewType.SEARCH &&
2019-10-04 15:27:34 +02:00
prefs.getBoolean("autoscroll", true)));
2019-10-02 21:22:59 +02:00
int prev = 0;
if (autoscroll && previousList != null)
for (int i = 0; i < previousList.size(); i++) {
TupleMessageEx message = previousList.get(i);
if (message != null && !message.ui_seen)
prev++;
}
int cur = 0;
if (autoscroll && currentList != null)
for (int i = 0; i < currentList.size(); i++) {
TupleMessageEx message = currentList.get(i);
if (message != null && !message.ui_seen)
cur++;
}
2019-10-01 15:20:24 +02:00
2019-10-03 08:45:19 +02:00
if (gotoTop ||
(previousList != null && currentList != null && cur > prev)) {
2019-05-15 13:37:35 +02:00
gotoTop = false;
2019-05-04 10:24:42 +02:00
properties.scrollTo(0);
}
}
});
2019-09-24 11:21:31 +02:00
try {
// https://issuetracker.google.com/issues/135628748
Handler handler = new Handler(Looper.getMainLooper());
Field mMainThreadExecutor = this.differ.getClass().getDeclaredField("mMainThreadExecutor");
mMainThreadExecutor.setAccessible(true);
mMainThreadExecutor.set(this.differ, new Executor() {
@Override
public void execute(Runnable command) {
try {
handler.post(command);
} catch (Throwable ex) {
Log.e(ex);
}
}
});
} catch (Throwable ex) {
Log.e(ex);
}
owner.getLifecycle().addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroyed() {
Log.i(AdapterMessage.this + " parent destroyed");
AdapterMessage.this.parentFragment = null;
}
});
2018-08-02 13:33:06 +00:00
}
2019-05-15 13:37:35 +02:00
void gotoTop() {
properties.scrollTo(0);
this.gotoTop = true;
}
void submitList(PagedList<TupleMessageEx> list) {
2019-08-11 16:08:53 +02:00
keyPosition.clear();
2019-01-29 11:38:38 +00:00
differ.submitList(list);
2019-01-20 13:27:35 +00:00
}
2018-12-03 08:21:02 +01:00
PagedList<TupleMessageEx> getCurrentList() {
return differ.getCurrentList();
}
2019-01-15 17:39:12 +00:00
void setCompact(boolean compact) {
if (this.compact != compact) {
this.compact = compact;
notifyDataSetChanged();
}
}
2018-12-13 19:26:27 +01:00
void setZoom(int zoom) {
2018-12-30 16:33:52 +00:00
if (this.zoom != zoom) {
this.zoom = zoom;
textSize = Helper.getTextSize(context, zoom);
notifyDataSetChanged();
}
2018-12-13 19:26:27 +01:00
}
2019-01-21 18:12:22 +00:00
2019-04-04 10:11:24 +02:00
int getZoom() {
return this.zoom;
}
2019-01-21 18:12:22 +00:00
void setSort(String sort) {
if (!sort.equals(this.sort)) {
this.sort = sort;
2019-04-11 12:50:09 +02:00
notifyDataSetChanged();
// Needed to redraw item decorators / add/remove size
2019-01-21 18:12:22 +00:00
}
}
2018-12-13 19:26:27 +01:00
2019-04-04 10:11:24 +02:00
String getSort() {
return this.sort;
}
2019-09-03 10:33:52 +02:00
void setAscending(boolean ascending) {
this.ascending = ascending;
}
2019-04-28 15:16:26 +02:00
void setFilterDuplicates(boolean filter_duplicates) {
if (this.filter_duplicates != filter_duplicates) {
this.filter_duplicates = filter_duplicates;
notifyDataSetChanged();
}
}
void checkInternet() {
2019-05-12 18:41:51 +02:00
boolean suitable = ConnectionHelper.getNetworkState(context).isSuitable();
2019-03-16 13:12:31 +00:00
if (this.suitable != suitable) {
this.suitable = suitable;
notifyDataSetChanged();
}
}
2019-05-21 15:43:17 +02:00
void setAnswerCount(int answers) {
this.answers = answers;
Log.i("Answer count=" + answers);
}
2019-01-15 17:39:12 +00:00
@Override
public int getItemViewType(int position) {
2019-10-01 13:56:48 +02:00
TupleMessageEx message = differ.getItem(position);
if (filter_duplicates && message != null && message.duplicate)
return R.layout.item_message_duplicate;
2019-05-30 20:32:29 +02:00
return (compact ? R.layout.item_message_compact : R.layout.item_message_normal);
2019-01-15 17:39:12 +00:00
}
2018-12-03 08:21:02 +01:00
@Override
public int getItemCount() {
return differ.getItemCount();
}
2018-08-12 16:14:20 +00:00
private static final DiffUtil.ItemCallback<TupleMessageEx> DIFF_CALLBACK =
2018-08-07 06:38:00 +00:00
new DiffUtil.ItemCallback<TupleMessageEx>() {
@Override
public boolean areItemsTheSame(
@NonNull TupleMessageEx prev, @NonNull TupleMessageEx next) {
return prev.id.equals(next.id);
}
2018-08-02 13:33:06 +00:00
2018-08-07 06:38:00 +00:00
@Override
public boolean areContentsTheSame(
@NonNull TupleMessageEx prev, @NonNull TupleMessageEx next) {
2019-06-25 09:46:31 +02:00
boolean same = true;
// id
// account
// folder
if (!Objects.equals(prev.identity, next.identity)) {
// via
same = false;
Log.i("Entity changed id=" + next.id);
}
// extra
if (!Objects.equals(prev.uid, next.uid)) {
same = false;
Log.i("uid changed id=" + next.id);
}
if (!Objects.equals(prev.msgid, next.msgid)) {
// debug info
same = false;
Log.i("msgid changed id=" + next.id);
}
// references
// deliveredto
// inreplyto
if (!Objects.equals(prev.thread, next.thread)) {
same = false;
Log.i("thread changed id=" + next.id);
}
// receipt_request
if (!MessageHelper.equal(prev.receipt_to, next.receipt_to)) {
same = false;
Log.i("receipt_to changed id=" + next.id);
}
if (!Objects.equals(prev.dkim, next.dkim)) {
same = false;
Log.i("dkim changed id=" + next.id);
}
if (!Objects.equals(prev.spf, next.spf)) {
same = false;
Log.i("spf changed id=" + next.id);
}
if (!Objects.equals(prev.dmarc, next.dmarc)) {
same = false;
Log.i("dmarc changed id=" + next.id);
}
if (!Objects.equals(prev.mx, next.mx)) {
same = false;
Log.i("mx changed id=" + next.id);
}
2019-06-25 09:46:31 +02:00
if (!Objects.equals(prev.avatar, next.avatar)) {
same = false;
Log.i("avatar changed id=" + next.id);
}
if (!Objects.equals(prev.sender, next.sender)) {
same = false;
Log.i("sender changed id=" + next.id);
}
if (!MessageHelper.equal(prev.from, next.from)) {
same = false;
Log.i("from changed id=" + next.id);
}
if (!MessageHelper.equal(prev.to, next.to)) {
same = false;
Log.i("to changed id=" + next.id);
}
if (!MessageHelper.equal(prev.cc, next.cc)) {
same = false;
Log.i("cc changed id=" + next.id);
}
if (!MessageHelper.equal(prev.bcc, next.bcc)) {
same = false;
Log.i("bcc changed id=" + next.id);
}
if (!MessageHelper.equal(prev.reply, next.reply)) {
same = false;
Log.i("reply changed id=" + next.id);
}
if (!MessageHelper.equal(prev.list_post, next.list_post)) {
same = false;
Log.i("list_post changed id=" + next.id);
}
if (!Objects.equals(prev.headers, next.headers)) {
same = false;
Log.i("headers changed id=" + next.id);
}
if (!Objects.equals(prev.raw, next.raw)) {
same = false;
Log.i("raw changed id=" + next.id);
}
if (!Objects.equals(prev.subject, next.subject)) {
same = false;
Log.i("subject changed id=" + next.id);
}
if (!Objects.equals(prev.size, next.size)) {
same = false;
Log.i("size changed id=" + next.id);
}
2019-09-30 21:00:28 +02:00
if (!Objects.equals(prev.total, next.total)) {
same = false;
Log.i("total changed id=" + next.id);
}
if (!Objects.equals(prev.attachments, next.attachments)) {
same = false;
Log.i("attachments changed id=" + next.id);
}
2019-06-25 09:46:31 +02:00
if (!prev.content.equals(next.content)) {
same = false;
Log.i("content changed id=" + next.id);
}
// plain_only
if (!Objects.equals(prev.preview, next.preview)) {
same = false;
Log.i("preview changed id=" + next.id);
}
2019-06-25 14:25:56 +02:00
if (!Objects.equals(prev.sent, next.sent)) {
2019-06-25 09:46:31 +02:00
same = false;
Log.i("sent changed id=" + next.id);
}
if (!prev.received.equals(next.received)) {
same = false;
Log.i("received changed id=" + next.id);
}
if (!prev.stored.equals(next.stored)) {
// updated after decryption
same = false;
Log.i("stored changed id=" + next.id);
}
// seen
// answered
// flagged
if (debug && !Objects.equals(prev.flags, next.flags)) {
same = false;
Log.i("flags changed id=" + next.id);
}
if (!Helper.equal(prev.keywords, next.keywords)) {
same = false;
Log.i("keywords changed id=" + next.id);
}
if (!prev.ui_seen.equals(next.ui_seen)) {
same = false;
Log.i("ui_seen changed id=" + next.id);
}
if (!prev.ui_answered.equals(next.ui_answered)) {
same = false;
Log.i("ui_answer changed id=" + next.id);
}
if (!prev.ui_flagged.equals(next.ui_flagged)) {
same = false;
Log.i("ui_flagged changed id=" + next.id);
}
if (!prev.ui_hide.equals(next.ui_hide)) {
same = false;
Log.i("ui_hide changed id=" + next.id);
}
if (!prev.ui_found.equals(next.ui_found)) {
same = false;
Log.i("ui_found changed id=" + next.id);
}
// ui_ignored
if (!prev.ui_browsed.equals(next.ui_browsed)) {
same = false;
Log.i("ui_browsed changed id=" + next.id);
}
if (!Objects.equals(prev.ui_snoozed, next.ui_snoozed)) {
same = false;
Log.i("ui_snoozed changed id=" + next.id);
}
if (!Objects.equals(prev.color, next.color)) {
same = false;
Log.i("color changed id=" + next.id);
}
// revision
// revisions
if (!Objects.equals(prev.warning, next.warning)) {
same = false;
Log.i("warning changed id=" + next.id);
}
if (!Objects.equals(prev.error, next.error)) {
same = false;
Log.i("error changed id=" + next.id);
}
// last_attempt
if (!Objects.equals(prev.accountName, next.accountName)) {
same = false;
Log.i("accountName changed id=" + next.id);
}
if (!Objects.equals(prev.accountColor, next.accountColor)) {
same = false;
Log.i("accountColor changed id=" + next.id);
}
// accountNotify
if (!prev.folderName.equals(next.folderName)) {
same = false;
Log.i("folderName changed id=" + next.id);
}
if (!Objects.equals(prev.folderDisplay, next.folderDisplay)) {
same = false;
Log.i("folderDisplay changed id=" + next.id);
}
if (!prev.folderType.equals(next.folderType)) {
same = false;
Log.i("folderType changed id=" + next.id);
}
if (!Objects.equals(prev.identityName, next.identityName)) {
same = false;
Log.i("identityName changed id=" + next.id);
}
if (!Objects.equals(prev.identityEmail, next.identityEmail)) {
same = false;
Log.i("identityEmail changed id=" + next.id);
}
if (!Objects.equals(prev.identitySynchronize, next.identitySynchronize)) {
same = false;
Log.i("identitySynchronize changed id=" + next.id);
}
if (prev.count != next.count) {
same = false;
Log.i("count changed id=" + next.id);
}
if (prev.unseen != next.unseen) {
same = false;
Log.i("unseen changed id=" + next.id);
}
if (prev.unflagged != next.unflagged) {
same = false;
Log.i("unflagged changed id=" + next.id);
}
if (prev.attachments != next.attachments) {
same = false;
Log.i("attachments changed id=" + next.id);
}
if (prev.drafts != next.drafts) {
same = false;
Log.i("drafts changed id=" + next.id);
}
if (prev.visible != next.visible) {
same = false;
Log.i("visible changed id=" + next.id);
}
if (!Objects.equals(prev.totalSize, next.totalSize)) {
same = false;
Log.i("totalSize changed id=" + next.id);
}
if (prev.duplicate != next.duplicate) {
same = false;
Log.i("duplicate changed id=" + next.id);
}
return same;
2018-08-07 06:38:00 +00:00
}
};
2018-08-02 13:33:06 +00:00
@Override
@NonNull
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
2019-01-15 17:39:12 +00:00
return new ViewHolder(inflater.inflate(viewType, parent, false));
2018-08-02 13:33:06 +00:00
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
2019-10-01 13:56:48 +02:00
TupleMessageEx message = differ.getItem(position);
if (filter_duplicates && message != null && message.duplicate) {
holder.tvFolder.setText(context.getString(R.string.title_duplicate_in, message.getFolderName(context)));
2019-10-04 16:16:44 +02:00
holder.tvFolder.setTypeface(message.unseen > 0 ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
holder.tvFolder.setTextColor(message.unseen > 0 ? colorUnread : textColorSecondary);
2019-10-01 13:56:48 +02:00
holder.tvFolder.setAlpha(Helper.LOW_LIGHT);
return;
}
2018-08-02 13:33:06 +00:00
holder.unwire();
2019-07-11 20:19:52 +02:00
if (message == null || context == null)
2018-08-07 06:38:00 +00:00
holder.clear();
else {
2019-03-04 10:18:42 +00:00
holder.bindTo(message);
2018-08-07 06:38:00 +00:00
holder.wire();
2018-08-02 13:33:06 +00:00
}
}
2019-04-11 16:54:00 +02:00
@Override
2019-09-21 20:28:03 +02:00
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
if (holder.wvBody instanceof WebView)
((WebView) holder.wvBody).loadDataWithBaseURL(null, "", "text/html", "UTF-8", null);
2019-04-13 08:02:01 +02:00
holder.cowner.stop();
2019-05-03 18:59:27 +02:00
holder.powner.recreate();
2019-04-11 16:54:00 +02:00
}
2018-10-24 09:09:07 +00:00
void setSelectionTracker(SelectionTracker<Long> selectionTracker) {
this.selectionTracker = selectionTracker;
}
2018-12-03 13:18:23 +01:00
int getPositionForKey(long key) {
2019-08-11 16:08:53 +02:00
if (keyPosition.isEmpty()) {
PagedList<TupleMessageEx> messages = getCurrentList();
if (messages != null) {
for (int i = 0; i < messages.size(); i++) {
TupleMessageEx message = messages.get(i);
if (message != null)
keyPosition.put(message.id, i);
}
Log.i("Mapped keys=" + keyPosition.size());
2018-12-03 13:18:23 +01:00
}
2019-08-11 16:08:53 +02:00
}
if (keyPosition.containsKey(key)) {
int pos = keyPosition.get(key);
Log.d("Position=" + pos + " @Key=" + key);
return pos;
}
2018-12-24 12:27:45 +00:00
Log.i("Position=" + RecyclerView.NO_POSITION + " @Key=" + key);
2018-12-03 13:18:23 +01:00
return RecyclerView.NO_POSITION;
}
TupleMessageEx getItemAtPosition(int pos) {
2019-08-11 16:08:53 +02:00
PagedList<TupleMessageEx> messages = getCurrentList();
if (messages != null && pos >= 0 && pos < messages.size()) {
TupleMessageEx message = messages.get(pos);
2018-12-03 13:18:23 +01:00
Long key = (message == null ? null : message.id);
2019-08-11 15:49:14 +02:00
Log.d("Item=" + key + " @Position=" + pos);
2018-12-03 13:18:23 +01:00
return message;
} else {
2019-08-11 15:49:14 +02:00
Log.d("Item=" + null + " @Position=" + pos);
2018-12-03 13:18:23 +01:00
return null;
}
}
TupleMessageEx getItemForKey(long key) {
2019-08-11 16:08:53 +02:00
int pos = getPositionForKey(key);
if (pos == RecyclerView.NO_POSITION) {
Log.d("Item=" + null + " @Key=" + key);
return null;
} else
return getItemAtPosition(pos);
2018-12-03 13:18:23 +01:00
}
Long getKeyAtPosition(int pos) {
TupleMessageEx message = getItemAtPosition(pos);
Long key = (message == null ? null : message.id);
2019-08-11 15:49:14 +02:00
Log.d("Key=" + key + " @Position=" + pos);
2018-12-03 13:18:23 +01:00
return key;
}
interface IProperties {
2018-12-21 08:32:26 +01:00
void setValue(String name, long id, boolean enabled);
2018-12-21 08:32:26 +01:00
boolean getValue(String name, long id);
2018-11-20 09:44:13 +01:00
2019-09-01 15:42:53 +02:00
void setSize(long id, float size);
float getSize(long id, float defaultSize);
2019-10-04 15:25:04 +02:00
void setHeight(long id, int height);
2018-12-22 08:48:23 +01:00
2019-10-04 15:25:04 +02:00
int getHeight(long id, int defaultHeight);
2018-12-22 08:48:23 +01:00
2019-10-04 18:59:37 +02:00
void setPosition(long id, Pair<Integer, Integer> position);
Pair<Integer, Integer> getPosition(long id);
2019-09-02 11:32:05 +02:00
void setAttachments(long id, List<EntityAttachment> attachments);
2019-04-04 14:47:56 +02:00
List<EntityAttachment> getAttachments(long id);
2019-04-27 10:40:06 +02:00
void scrollTo(int pos);
void move(long id, String type);
void finish();
}
2019-06-30 20:56:31 +02:00
2019-09-11 21:43:27 +02:00
public static class FragmentDialogLink extends FragmentDialogBase {
2019-06-30 20:56:31 +02:00
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final Uri uri = getArguments().getParcelable("uri");
String title = getArguments().getString("title");
final Uri sanitized;
if (uri.isOpaque())
sanitized = uri;
else {
// https://en.wikipedia.org/wiki/UTM_parameters
Uri.Builder builder = uri.buildUpon();
boolean changed = false;
2019-06-30 20:56:31 +02:00
builder.clearQuery();
for (String key : uri.getQueryParameterNames())
2019-09-23 22:07:22 +02:00
if (PARANOID_QUERY.contains(key.toLowerCase(Locale.ROOT)))
changed = true;
else if (!TextUtils.isEmpty(key))
for (String value : uri.getQueryParameters(key)) {
Log.i("Query " + key + "=" + value);
builder.appendQueryParameter(key, value);
}
2019-06-30 20:56:31 +02:00
sanitized = (changed ? builder.build() : uri);
2019-06-30 20:56:31 +02:00
}
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_open_link, null);
TextView tvTitle = view.findViewById(R.id.tvTitle);
final EditText etLink = view.findViewById(R.id.etLink);
final CheckBox cbSecure = view.findViewById(R.id.cbSecure);
CheckBox cbSanitize = view.findViewById(R.id.cbSanitize);
2019-07-07 10:49:16 +02:00
final Button btnOwner = view.findViewById(R.id.btnOwner);
2019-07-07 11:11:31 +02:00
TextView tvOwnerRemark = view.findViewById(R.id.tvOwnerRemark);
2019-07-07 10:49:16 +02:00
final ContentLoadingProgressBar pbWait = view.findViewById(R.id.pbWait);
2019-06-30 20:56:31 +02:00
final TextView tvOwner = view.findViewById(R.id.tvOwner);
2019-07-06 13:17:04 +02:00
final TextView tvHost = view.findViewById(R.id.tvHost);
2019-06-30 20:56:31 +02:00
final Group grpOwner = view.findViewById(R.id.grpOwner);
etLink.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence text, int i, int i1, int i2) {
}
@Override
2019-09-24 09:56:58 +02:00
public void afterTextChanged(Editable editable) {
Uri uri = Uri.parse(editable.toString());
boolean secure = (!uri.isOpaque() &&
"https".equals(uri.getScheme()));
boolean hyperlink = (!uri.isOpaque() &&
("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())));
cbSecure.setTag(secure);
cbSecure.setChecked(secure);
cbSecure.setText(
secure ? R.string.title_link_secured : R.string.title_secure_link);
cbSecure.setTextColor(Helper.resolveColor(getContext(),
secure ? android.R.attr.textColorSecondary : R.attr.colorWarning));
cbSecure.setTypeface(
secure ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD);
cbSecure.setVisibility(hyperlink ? View.VISIBLE : View.GONE);
2019-06-30 20:56:31 +02:00
}
});
2019-07-07 11:11:31 +02:00
2019-06-30 20:56:31 +02:00
cbSecure.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
2019-09-24 09:56:58 +02:00
boolean tag = (Boolean) compoundButton.getTag();
if (tag == checked)
return;
2019-06-30 20:56:31 +02:00
Uri uri = Uri.parse(etLink.getText().toString());
Uri.Builder builder = uri.buildUpon();
builder.scheme(checked ? "https" : "http");
String authority = uri.getEncodedAuthority();
if (authority != null) {
authority = authority.replace(checked ? ":80" : ":443", checked ? ":443" : ":80");
builder.encodedAuthority(authority);
}
etLink.setText(builder.build().toString());
}
});
cbSanitize.setVisibility(uri.equals(sanitized) ? View.GONE : View.VISIBLE);
2019-07-07 11:11:31 +02:00
2019-06-30 20:56:31 +02:00
cbSanitize.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
if (checked)
etLink.setText(sanitized.toString());
else
etLink.setText(uri.toString());
}
});
2019-07-07 11:11:31 +02:00
tvOwnerRemark.setMovementMethod(LinkMovementMethod.getInstance());
2019-07-07 10:49:16 +02:00
pbWait.setVisibility(View.GONE);
2019-06-30 20:56:31 +02:00
grpOwner.setVisibility(View.GONE);
2019-07-07 11:11:31 +02:00
2019-07-07 10:49:16 +02:00
btnOwner.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Bundle args = new Bundle();
args.putParcelable("uri", uri);
2019-06-30 20:56:31 +02:00
2019-07-07 10:49:16 +02:00
new SimpleTask<String[]>() {
@Override
protected void onPreExecute(Bundle args) {
btnOwner.setEnabled(false);
pbWait.setVisibility(View.VISIBLE);
grpOwner.setVisibility(View.GONE);
}
2019-06-30 20:56:31 +02:00
2019-07-07 10:49:16 +02:00
@Override
protected void onPostExecute(Bundle args) {
btnOwner.setEnabled(true);
pbWait.setVisibility(View.GONE);
grpOwner.setVisibility(View.VISIBLE);
}
2019-06-30 20:56:31 +02:00
2019-07-07 10:49:16 +02:00
@Override
protected String[] onExecute(Context context, Bundle args) throws Throwable {
Uri uri = args.getParcelable("uri");
2019-09-08 13:22:44 +02:00
return IPInfo.getOrganization(uri, context);
2019-07-07 10:49:16 +02:00
}
2019-06-30 20:56:31 +02:00
2019-07-07 10:49:16 +02:00
@Override
protected void onExecuted(Bundle args, String[] data) {
String host = data[0];
String organization = data[1];
tvHost.setText(host);
tvOwner.setText(organization == null ? "?" : organization);
}
2019-06-30 20:56:31 +02:00
2019-07-07 10:49:16 +02:00
@Override
protected void onException(Bundle args, Throwable ex) {
2019-06-30 20:56:31 +02:00
tvOwner.setText(ex.getMessage());
2019-07-07 10:49:16 +02:00
}
2019-09-11 14:03:59 +02:00
}.execute(FragmentDialogLink.this, args, "link:owner");
2019-07-07 10:49:16 +02:00
}
});
2019-06-30 20:56:31 +02:00
2019-09-24 09:56:58 +02:00
tvTitle.setText(title);
tvTitle.setVisibility(TextUtils.isEmpty(title) ? View.GONE : View.VISIBLE);
etLink.setText(uri.toString());
2019-06-30 20:56:31 +02:00
return new AlertDialog.Builder(getContext())
.setView(view)
.setPositiveButton(R.string.title_yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Uri uri = Uri.parse(etLink.getText().toString());
2019-07-01 20:34:02 +02:00
Helper.view(getContext(), uri, false);
2019-06-30 20:56:31 +02:00
}
})
.setNeutralButton(R.string.title_browse, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Uri uri = Uri.parse(etLink.getText().toString());
2019-07-01 20:34:02 +02:00
Helper.view(getContext(), uri, true);
2019-06-30 20:56:31 +02:00
}
})
.setNegativeButton(R.string.title_no, null)
2019-07-01 19:54:37 +02:00
.create();
2019-06-30 20:56:31 +02:00
}
}
2019-09-11 21:43:27 +02:00
public static class FragmentDialogImage extends FragmentDialogBase {
2019-07-06 12:52:00 +02:00
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final PhotoView pv = new PhotoView(getContext());
new SimpleTask<Drawable>() {
@Override
protected Drawable onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
String source = args.getString("source");
2019-10-04 21:25:52 +02:00
return ImageHelper.decodeImage(context, id, source, true, null);
2019-07-06 12:52:00 +02:00
}
@Override
protected void onExecuted(Bundle args, Drawable drawable) {
pv.setImageDrawable(drawable);
2019-09-07 20:13:58 +02:00
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (drawable instanceof AnimatedImageDrawable)
((AnimatedImageDrawable) drawable).start();
}
2019-07-06 12:52:00 +02:00
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getFragmentManager(), ex);
}
2019-09-11 14:03:59 +02:00
}.execute(this, getArguments(), "view:image");
2019-07-06 12:52:00 +02:00
final Dialog dialog = new Dialog(getContext(), android.R.style.Theme_Black_NoTitleBar_Fullscreen);
dialog.setContentView(pv);
return dialog;
}
}
2019-09-11 21:43:27 +02:00
public static class FragmentKeywordManage extends FragmentDialogBase {
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final long id = getArguments().getLong("id");
List<String> keywords = Arrays.asList(getArguments().getStringArray("keywords"));
List<String> fkeywords = Arrays.asList(getArguments().getStringArray("fkeywords"));
final List<String> items = new ArrayList<>(keywords);
for (String keyword : fkeywords)
if (!items.contains(keyword))
items.add(keyword);
Collections.sort(items);
final boolean[] selected = new boolean[items.size()];
final boolean[] dirty = new boolean[items.size()];
for (int i = 0; i < selected.length; i++) {
selected[i] = keywords.contains(items.get(i));
dirty[i] = false;
}
return new AlertDialog.Builder(getContext())
.setTitle(R.string.title_manage_keywords)
.setMultiChoiceItems(items.toArray(new String[0]), selected, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
dirty[which] = true;
}
})
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
2019-08-13 10:27:17 +02:00
if (!ActivityBilling.isPro(getContext())) {
getContext().startActivity(new Intent(getContext(), ActivityBilling.class));
return;
}
Bundle args = new Bundle();
args.putLong("id", id);
args.putStringArray("keywords", items.toArray(new String[0]));
args.putBooleanArray("selected", selected);
args.putBooleanArray("dirty", dirty);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
String[] keywords = args.getStringArray("keywords");
boolean[] selected = args.getBooleanArray("selected");
boolean[] dirty = args.getBooleanArray("dirty");
DB db = DB.getInstance(context);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
for (int i = 0; i < selected.length; i++)
if (dirty[i])
EntityOperation.queue(context, message, EntityOperation.KEYWORD, keywords[i], selected[i]);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getFragmentManager(), ex);
}
2019-09-12 13:36:23 +02:00
}.execute(getContext(), getActivity(), args, "message:keywords:manage");
}
})
.setNeutralButton(R.string.title_add, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Bundle args = new Bundle();
args.putLong("id", id);
FragmentKeywordAdd fragment = new FragmentKeywordAdd();
fragment.setArguments(args);
fragment.show(getFragmentManager(), "keyword:add");
}
})
2019-09-12 13:23:32 +02:00
.setNegativeButton(android.R.string.cancel, null)
.create();
}
}
2019-09-11 21:43:27 +02:00
public static class FragmentKeywordAdd extends FragmentDialogBase {
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
final long id = getArguments().getLong("id");
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_keyword, null);
final EditText etKeyword = view.findViewById(R.id.etKeyword);
etKeyword.setText(null);
return new AlertDialog.Builder(getContext())
.setView(view)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
2019-08-13 10:27:17 +02:00
if (!ActivityBilling.isPro(getContext())) {
getContext().startActivity(new Intent(getContext(), ActivityBilling.class));
return;
}
2019-09-26 12:11:46 +02:00
String keyword = MessageHelper.sanitizeKeyword(etKeyword.getText().toString());
if (!TextUtils.isEmpty(keyword)) {
Bundle args = new Bundle();
args.putLong("id", id);
args.putString("keyword", keyword);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) {
long id = args.getLong("id");
String keyword = args.getString("keyword");
DB db = DB.getInstance(context);
EntityMessage message = db.message().getMessage(id);
if (message == null)
return null;
EntityOperation.queue(context, message, EntityOperation.KEYWORD, keyword, true);
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getFragmentManager(), ex);
}
2019-09-12 13:36:23 +02:00
}.execute(getContext(), getActivity(), args, "message:keyword:add");
}
}
2019-09-12 13:23:32 +02:00
})
.setNegativeButton(android.R.string.cancel, null)
.create();
}
}
2018-08-02 13:33:06 +00:00
}