2074 lines
100 KiB
Java
2074 lines
100 KiB
Java
/*
|
|
* Copyright (c) 2022 Proton AG
|
|
*
|
|
* This file is part of Proton Mail.
|
|
*
|
|
* Proton Mail is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Proton Mail is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Proton Mail. If not, see https://www.gnu.org/licenses/.
|
|
*/
|
|
package ch.protonmail.android.activities.composeMessage;
|
|
|
|
import static ch.protonmail.android.attachments.ImportAttachmentsWorkerKt.KEY_INPUT_DATA_COMPOSER_INSTANCE_ID;
|
|
import static ch.protonmail.android.attachments.ImportAttachmentsWorkerKt.KEY_INPUT_DATA_FILE_URIS_STRING_ARRAY;
|
|
|
|
import android.Manifest;
|
|
import android.app.ActivityManager;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.res.ColorStateList;
|
|
import android.database.Cursor;
|
|
import android.graphics.Color;
|
|
import android.graphics.PorterDuff;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.provider.ContactsContract;
|
|
import android.text.Editable;
|
|
import android.text.Spannable;
|
|
import android.text.SpannableString;
|
|
import android.text.SpannableStringBuilder;
|
|
import android.text.Spanned;
|
|
import android.text.TextUtils;
|
|
import android.text.TextWatcher;
|
|
import android.text.format.Formatter;
|
|
import android.text.util.Linkify;
|
|
import android.util.Log;
|
|
import android.view.Gravity;
|
|
import android.view.KeyEvent;
|
|
import android.view.Menu;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.ViewTreeObserver;
|
|
import android.webkit.WebSettings;
|
|
import android.webkit.WebView;
|
|
import android.widget.AdapterView;
|
|
import android.widget.ArrayAdapter;
|
|
import android.widget.Button;
|
|
import android.widget.EditText;
|
|
import android.widget.ImageButton;
|
|
import android.widget.Spinner;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.StringRes;
|
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
|
import androidx.core.app.ActivityCompat;
|
|
import androidx.core.widget.NestedScrollView;
|
|
import androidx.lifecycle.Observer;
|
|
import androidx.loader.app.LoaderManager;
|
|
import androidx.loader.content.Loader;
|
|
import androidx.work.Data;
|
|
import androidx.work.OneTimeWorkRequest;
|
|
import androidx.work.WorkManager;
|
|
|
|
import com.google.android.material.snackbar.Snackbar;
|
|
import com.squareup.otto.Subscribe;
|
|
import com.tokenautocomplete.TokenCompleteTextView;
|
|
|
|
import org.apache.http.protocol.HTTP;
|
|
import org.jsoup.Jsoup;
|
|
import org.jsoup.nodes.Document;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
import java.lang.reflect.Field;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
|
|
import javax.annotation.Nonnull;
|
|
import javax.inject.Inject;
|
|
|
|
import ch.protonmail.android.R;
|
|
import ch.protonmail.android.activities.AddAttachmentsActivity;
|
|
import ch.protonmail.android.activities.messageDetails.repository.MessageDetailsRepository;
|
|
import ch.protonmail.android.api.models.MessageRecipient;
|
|
import ch.protonmail.android.api.models.SendPreference;
|
|
import ch.protonmail.android.api.models.User;
|
|
import ch.protonmail.android.api.models.address.Address;
|
|
import ch.protonmail.android.api.models.enumerations.MessageEncryption;
|
|
import ch.protonmail.android.api.segments.event.AlarmReceiver;
|
|
import ch.protonmail.android.attachments.DownloadEmbeddedAttachmentsWorker;
|
|
import ch.protonmail.android.attachments.ImportAttachmentsWorker;
|
|
import ch.protonmail.android.compose.ComposeMessageViewModel;
|
|
import ch.protonmail.android.compose.presentation.mapper.SendPreferencesToMessageEncryptionUiModelMapper;
|
|
import ch.protonmail.android.compose.presentation.ui.ComposeMessageKotlinActivity;
|
|
import ch.protonmail.android.compose.presentation.ui.MessageRecipientArrayAdapter;
|
|
import ch.protonmail.android.compose.presentation.util.HtmlToSpanned;
|
|
import ch.protonmail.android.compose.recipients.GroupRecipientsDialogFragment;
|
|
import ch.protonmail.android.contacts.PostResult;
|
|
import ch.protonmail.android.contacts.details.presentation.model.ContactLabelUiModel;
|
|
import ch.protonmail.android.core.Constants;
|
|
import ch.protonmail.android.core.ProtonMailApplication;
|
|
import ch.protonmail.android.crypto.AddressCrypto;
|
|
import ch.protonmail.android.crypto.CipherText;
|
|
import ch.protonmail.android.crypto.Crypto;
|
|
import ch.protonmail.android.data.local.model.LocalAttachment;
|
|
import ch.protonmail.android.data.local.model.Message;
|
|
import ch.protonmail.android.data.local.model.MessageSender;
|
|
import ch.protonmail.android.details.presentation.model.MessageEncryptionUiModel;
|
|
import ch.protonmail.android.di.DefaultSharedPreferences;
|
|
import ch.protonmail.android.events.ContactEvent;
|
|
import ch.protonmail.android.events.DownloadEmbeddedImagesEvent;
|
|
import ch.protonmail.android.events.FetchDraftDetailEvent;
|
|
import ch.protonmail.android.events.PostImportAttachmentEvent;
|
|
import ch.protonmail.android.events.ResignContactEvent;
|
|
import ch.protonmail.android.events.Status;
|
|
import ch.protonmail.android.events.contacts.SendPreferencesEvent;
|
|
import ch.protonmail.android.feature.account.AccountManagerKt;
|
|
import ch.protonmail.android.jobs.contacts.GetSendPreferenceJob;
|
|
import ch.protonmail.android.settings.data.AccountSettingsRepository;
|
|
import ch.protonmail.android.tasks.EmbeddedImagesThread;
|
|
import ch.protonmail.android.ui.view.ComposerBottomAppBar;
|
|
import ch.protonmail.android.usecase.model.FetchPublicKeysRequest;
|
|
import ch.protonmail.android.usecase.model.FetchPublicKeysResult;
|
|
import ch.protonmail.android.utils.AppUtil;
|
|
import ch.protonmail.android.utils.DateUtil;
|
|
import ch.protonmail.android.utils.Event;
|
|
import ch.protonmail.android.utils.HTMLTransformer.AbstractTransformer;
|
|
import ch.protonmail.android.utils.HTMLTransformer.DefaultTransformer;
|
|
import ch.protonmail.android.utils.HTMLTransformer.Transformer;
|
|
import ch.protonmail.android.utils.HTMLTransformer.ViewportTransformer;
|
|
import ch.protonmail.android.utils.MailToData;
|
|
import ch.protonmail.android.utils.MailToUtils;
|
|
import ch.protonmail.android.utils.MessageUtils;
|
|
import ch.protonmail.android.utils.ServerTime;
|
|
import ch.protonmail.android.utils.UiUtil;
|
|
import ch.protonmail.android.utils.crypto.TextDecryptionResult;
|
|
import ch.protonmail.android.utils.extensions.CommonExtensionsKt;
|
|
import ch.protonmail.android.utils.extensions.SerializationUtils;
|
|
import ch.protonmail.android.utils.extensions.TextExtensions;
|
|
import ch.protonmail.android.utils.ui.dialogs.DialogUtils;
|
|
import ch.protonmail.android.utils.ui.screen.RenderDimensionsProvider;
|
|
import ch.protonmail.android.views.MessageExpirationView;
|
|
import ch.protonmail.android.views.MessagePasswordButton;
|
|
import ch.protonmail.android.views.MessageRecipientView;
|
|
import ch.protonmail.android.views.PmWebViewClient;
|
|
import dagger.hilt.android.AndroidEntryPoint;
|
|
import kotlin.collections.CollectionsKt;
|
|
import me.proton.core.accountmanager.domain.AccountManager;
|
|
import me.proton.core.domain.entity.UserId;
|
|
import me.proton.core.user.domain.entity.AddressId;
|
|
import timber.log.Timber;
|
|
|
|
@AndroidEntryPoint
|
|
public class ComposeMessageActivity
|
|
extends ComposeMessageKotlinActivity
|
|
implements MessagePasswordButton.OnMessagePasswordChangedListener,
|
|
MessageExpirationView.OnMessageExpirationChangedListener,
|
|
LoaderManager.LoaderCallbacks<Cursor>,
|
|
GroupRecipientsDialogFragment.IGroupRecipientsListener {
|
|
//region extras
|
|
public static final String EXTRA_PARENT_ID = "parent_id";
|
|
public static final String EXTRA_ACTION_ID = "action_id";
|
|
public static final String EXTRA_MESSAGE_ID = "message_id";
|
|
public static final String EXTRA_TO_RECIPIENTS = "to_recipients";
|
|
public static final String EXTRA_TO_RECIPIENT_GROUPS = "to_recipient_groups";
|
|
public static final String EXTRA_CC_RECIPIENTS = "cc_recipients";
|
|
public static final String EXTRA_PGP_MIME = "pgp_mime";
|
|
public static final String EXTRA_MESSAGE_TITLE = "message_title";
|
|
public static final String EXTRA_MESSAGE_BODY = "message_body";
|
|
public static final String EXTRA_MAIL_TO = "mail_to";
|
|
public static final String EXTRA_MESSAGE_BODY_LARGE = "message_body_large";
|
|
public static final String EXTRA_MESSAGE_ATTACHMENTS = "message_attachments";
|
|
public static final String EXTRA_MESSAGE_EMBEDDED_ATTACHMENTS = "message_attachments_embedded";
|
|
public static final String EXTRA_MESSAGE_TIMESTAMP = "message_timestamp";
|
|
public static final String EXTRA_SENDER_NAME = "sender_name";
|
|
public static final String EXTRA_SENDER_ADDRESS = "sender_address";
|
|
public static final String EXTRA_MESSAGE_RESPONSE_INLINE = "response_inline";
|
|
public static final String EXTRA_MESSAGE_ADDRESS_ID = "address_id";
|
|
public static final String EXTRA_MESSAGE_ADDRESS_EMAIL_ALIAS = "address_email_alias";
|
|
public static final String EXTRA_REPLY_FROM_NOTIFICATION = "reply_from_notification";
|
|
public static final String EXTRA_LOAD_IMAGES = "load_images";
|
|
public static final String EXTRA_LOAD_REMOTE_CONTENT = "load_remote_content";
|
|
private static final int REQUEST_CODE_ADD_ATTACHMENTS = 1;
|
|
private static final String STATE_ADDITIONAL_ROWS_VISIBLE = "additional_rows_visible";
|
|
private static final String STATE_DRAFT_ID = "draft_id";
|
|
private static final String STATE_ADDED_CONTENT = "added_content";
|
|
private static final char[] RECIPIENT_SEPARATORS = {',', ';', ' '};
|
|
//endregion
|
|
|
|
//region views
|
|
private Spinner fromAddressSpinner;
|
|
|
|
private MessageRecipientView toRecipientView;
|
|
private MessageRecipientView ccRecipientView;
|
|
private MessageRecipientView bccRecipientView;
|
|
|
|
private EditText subjectEditText;
|
|
|
|
private EditText messageBodyEditText;
|
|
private Button respondInlineButton;
|
|
|
|
private ComposerBottomAppBar bottomAppBar;
|
|
//endregion
|
|
|
|
private WebView quotedMessageWebView;
|
|
private PmWebViewClient pmWebViewClient;
|
|
final String newline = "<br>";
|
|
private MessageRecipientArrayAdapter recipientAdapter;
|
|
private boolean mAreAdditionalRowsVisible;
|
|
|
|
private int mSelectedAddressPosition = 0;
|
|
private boolean askForPermission;
|
|
|
|
private String mAction;
|
|
private boolean mUpdateDraftPmMeChanged;
|
|
private boolean largeBody;
|
|
private boolean mSendingInProgress;
|
|
|
|
private ComposeMessageViewModel composeMessageViewModel;
|
|
@Inject
|
|
MessageDetailsRepository messageDetailsRepository;
|
|
@Inject
|
|
AccountManager accountManager;
|
|
|
|
@Inject
|
|
DownloadEmbeddedAttachmentsWorker.Enqueuer attachmentsWorker;
|
|
|
|
@Inject
|
|
@DefaultSharedPreferences
|
|
SharedPreferences defaultSharedPreferences;
|
|
|
|
@Inject
|
|
HtmlToSpanned htmlToSpanned;
|
|
|
|
@Inject
|
|
RenderDimensionsProvider renderDimensionsProvider;
|
|
|
|
@Inject
|
|
AccountSettingsRepository accountSettingsRepository;
|
|
|
|
String composerInstanceId;
|
|
|
|
Menu menu;
|
|
|
|
@Override
|
|
public void onPermissionConfirmed(Constants.PermissionType type) {
|
|
|
|
if (type == Constants.PermissionType.CONTACTS) {
|
|
super.onPermissionConfirmed(type);
|
|
return;
|
|
}
|
|
onStoragePermissionGranted();
|
|
}
|
|
|
|
@Override
|
|
public void onHasPermission(Constants.PermissionType type) {
|
|
|
|
if (type == Constants.PermissionType.CONTACTS) {
|
|
super.onHasPermission(type);
|
|
return;
|
|
}
|
|
onStoragePermissionGranted();
|
|
}
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
setSupportActionBar(binding.composerToolbar);
|
|
|
|
// Setup view references
|
|
fromAddressSpinner = binding.composerFromSpinner;
|
|
toRecipientView = binding.composerToRecipientView;
|
|
ccRecipientView = binding.composerCcRecipientView;
|
|
bccRecipientView = binding.composerBccRecipientView;
|
|
subjectEditText = binding.composerSubjectEditText;
|
|
messageBodyEditText = binding.composerMessageBodyEditText;
|
|
respondInlineButton = binding.composerRespondInlineButton;
|
|
bottomAppBar = binding.composerBottomAppBar;
|
|
|
|
// region setup click listeners
|
|
binding.composerExpandRecipientsButton.setOnClickListener((View view) -> {
|
|
mAreAdditionalRowsVisible = !mAreAdditionalRowsVisible;
|
|
setAdditionalRowVisibility(mAreAdditionalRowsVisible);
|
|
});
|
|
// endregion
|
|
|
|
composeMessageViewModel = getComposeViewModel();
|
|
composeMessageViewModel.init(mHtmlProcessor);
|
|
observeSetup();
|
|
|
|
toRecipientView.performBestGuess(false);
|
|
ccRecipientView.performBestGuess(false);
|
|
bccRecipientView.performBestGuess(false);
|
|
|
|
binding.rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(new KeyboardManager());
|
|
|
|
recipientAdapter = new MessageRecipientArrayAdapter(this);
|
|
initRecipientsView(toRecipientView, recipientAdapter, Constants.RecipientLocationType.TO);
|
|
initRecipientsView(ccRecipientView, recipientAdapter, Constants.RecipientLocationType.CC);
|
|
initRecipientsView(bccRecipientView, recipientAdapter, Constants.RecipientLocationType.BCC);
|
|
subjectEditText.setSelection(subjectEditText.getText().length(), subjectEditText.getText().length());
|
|
|
|
setUpQuotedMessageWebView();
|
|
|
|
messageBodyEditText.setOnKeyListener(new ComposeBodyChangeListener());
|
|
respondInlineButton.setOnClickListener(new RespondInlineButtonClickListener());
|
|
subjectEditText.setOnEditorActionListener(new MessageTitleEditorActionListener());
|
|
|
|
Intent intent = getIntent();
|
|
mAction = intent.getAction();
|
|
final String type = intent.getType();
|
|
Bundle extras = intent.getExtras();
|
|
if (savedInstanceState == null) {
|
|
initialiseActivityOnFirstStart(intent, extras, type);
|
|
} else {
|
|
initialiseActivityOnFirstStart(intent, savedInstanceState, type);
|
|
setRespondInlineVisibility(!TextUtils.isEmpty(messageBodyEditText.getText()));
|
|
}
|
|
try {
|
|
if (Arrays.asList(Constants.MessageActionType.FORWARD, Constants.MessageActionType.REPLY, Constants.MessageActionType.REPLY_ALL)
|
|
.contains(composeMessageViewModel.get_actionId()) || extras.getBoolean(EXTRA_MAIL_TO)) {
|
|
// upload attachments if using pgp/mime
|
|
composeMessageViewModel.setBeforeSaveDraft(composeMessageViewModel.getMessageDataResult().isPGPMime(), messageBodyEditText.getText().toString());
|
|
}
|
|
} catch (Exception exc) {
|
|
Timber.tag("588").e(exc, "Exception on create (upload attachments)");
|
|
|
|
}
|
|
|
|
fromAddressSpinner.getBackground().setColorFilter(getResources().getColor(R.color.new_purple), PorterDuff.Mode.SRC_ATOP);
|
|
List<String> senderAddresses = composeMessageViewModel.getSenderAddresses();
|
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.simple_spinner_item_black, senderAddresses);
|
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
fromAddressSpinner.setAdapter(adapter);
|
|
|
|
if (!TextUtils.isEmpty(composeMessageViewModel.getMessageDataResult().getAddressId())) {
|
|
mSelectedAddressPosition = composeMessageViewModel.getPositionByAddressId();
|
|
}
|
|
|
|
// message was sent to our alias address, which we put as first
|
|
if (!TextUtils.isEmpty(composeMessageViewModel.getMessageDataResult().getAddressEmailAlias())) {
|
|
mSelectedAddressPosition = 0;
|
|
}
|
|
|
|
if (!composeMessageViewModel.isPaidUser() && MessageUtils.INSTANCE.isPmMeAddress(senderAddresses.get(mSelectedAddressPosition))) {
|
|
composeMessageViewModel.setOldSenderAddressId(composeMessageViewModel.getMessageDataResult().getAddressId());
|
|
mSelectedAddressPosition = composeMessageViewModel.getUserAddressByIdFromOnlySendAddresses();
|
|
if (!TextUtils.isEmpty(composeMessageViewModel.getMessageDataResult().getAddressEmailAlias())) {
|
|
mSelectedAddressPosition++; // if alias is on the list, index is actually 1 more than usual
|
|
}
|
|
composeMessageViewModel.setSenderAddressIdByEmail(fromAddressSpinner.getAdapter().getItem(mSelectedAddressPosition).toString());
|
|
boolean dialogShowed = defaultSharedPreferences.getBoolean(Constants.Prefs.PREF_PM_ADDRESS_CHANGED, false);
|
|
if (!dialogShowed && !isFinishing()) {
|
|
showPmChangedDialog(senderAddresses.get(mSelectedAddressPosition));
|
|
}
|
|
mUpdateDraftPmMeChanged = true;
|
|
}
|
|
|
|
fromAddressSpinner.setSelection(mSelectedAddressPosition);
|
|
|
|
fromAddressSpinner.getViewTreeObserver().addOnGlobalLayoutListener(new AddressSpinnerGlobalLayoutListener());
|
|
askForPermission = true;
|
|
composeMessageViewModel.setSignature(composeMessageViewModel.getSignatureByEmailAddress((String) fromAddressSpinner.getSelectedItem()));
|
|
|
|
composeMessageViewModel.getFetchedBodyEvents().observe(this, this::setMessageBody);
|
|
}
|
|
|
|
private void setUpQuotedMessageWebView() {
|
|
quotedMessageWebView = new WebView(this);
|
|
pmWebViewClient = new PmWebViewClient(mUserManager, accountSettingsRepository, this, true);
|
|
quotedMessageWebView.setWebViewClient(pmWebViewClient);
|
|
quotedMessageWebView.requestDisallowInterceptTouchEvent(true);
|
|
|
|
final WebSettings webSettings = quotedMessageWebView.getSettings();
|
|
webSettings.setAllowFileAccess(false);
|
|
webSettings.setDisplayZoomControls(false);
|
|
webSettings.setGeolocationEnabled(false);
|
|
webSettings.setSavePassword(false);
|
|
webSettings.setJavaScriptEnabled(false);
|
|
webSettings.setSupportZoom(true);
|
|
webSettings.setBuiltInZoomControls(true);
|
|
webSettings.setPluginState(WebSettings.PluginState.OFF);
|
|
|
|
composeMessageViewModel.setUpWebViewDarkMode(
|
|
this,
|
|
mUserManager.requireCurrentUserId(),
|
|
quotedMessageWebView,
|
|
composeMessageViewModel.getDraftId()
|
|
);
|
|
|
|
binding.composerQuotedMessageContainer.addView(quotedMessageWebView);
|
|
}
|
|
|
|
private void observeSetup() {
|
|
|
|
composeMessageViewModel.getSetupComplete().observe(this, booleanEvent -> {
|
|
for (Map.Entry<MessageRecipientView, List<MessageRecipient>> entry : pendingRecipients.entrySet()) {
|
|
addRecipientsToView(entry.getValue(), entry.getKey());
|
|
}
|
|
allowSpinnerListener = true;
|
|
});
|
|
|
|
composeMessageViewModel.getCloseComposer().observe(this, booleanEvent -> {
|
|
finishActivity(false);
|
|
});
|
|
|
|
composeMessageViewModel.getDeleteResult().observe(ComposeMessageActivity.this, new CheckLocalMessageObserver());
|
|
composeMessageViewModel.getOpenAttachmentsScreenResult().observe(ComposeMessageActivity.this, new AddAttachmentsObserver());
|
|
composeMessageViewModel.getSavingDraftError().observe(this, errorMessage ->
|
|
Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show());
|
|
composeMessageViewModel.getSavingDraftComplete().observe(this, event -> {
|
|
if (mUpdateDraftPmMeChanged) {
|
|
composeMessageViewModel.setBeforeSaveDraft(false, messageBodyEditText.getText().toString());
|
|
mUpdateDraftPmMeChanged = false;
|
|
}
|
|
disableSendButton(false);
|
|
onMessageLoaded(
|
|
event,
|
|
false,
|
|
TextUtils.isEmpty(mAction) &&
|
|
composeMessageViewModel.getMessageDataResult().getAttachmentList().isEmpty()
|
|
);
|
|
});
|
|
|
|
composeMessageViewModel.getDbIdWatcher().observe(ComposeMessageActivity.this, new SendMessageObserver());
|
|
|
|
composeMessageViewModel.getFetchKeyDetailsResult().observe(
|
|
this,
|
|
this::onFetchEmailKeysEvent
|
|
);
|
|
|
|
composeMessageViewModel.getBuildingMessageCompleted().observe(this, new BuildObserver());
|
|
|
|
composeMessageViewModel.getHasConnectivity().observe(this, this::onConnectivityEvent);
|
|
}
|
|
|
|
private void initialiseActivityOnFirstStart(Intent intent, Bundle extras, String type) {
|
|
composeMessageViewModel.getLoadingDraftResult().observe(ComposeMessageActivity.this, new LoadDraftObserver(extras, intent, type));
|
|
if (extras != null) {
|
|
composeMessageViewModel.prepareMessageData(extras.getBoolean(EXTRA_PGP_MIME, false), extras.getString(EXTRA_MESSAGE_ADDRESS_ID, ""), extras.getString(EXTRA_MESSAGE_ADDRESS_EMAIL_ALIAS));
|
|
composeMessageViewModel.setShowImages(extras.getBoolean(EXTRA_LOAD_IMAGES, false));
|
|
composeMessageViewModel.setShowRemoteContent(extras.getBoolean(EXTRA_LOAD_REMOTE_CONTENT, false));
|
|
boolean replyFromNotification = extras.getBoolean(EXTRA_REPLY_FROM_NOTIFICATION, false);
|
|
String messageId = extras.getString(EXTRA_MESSAGE_ID);
|
|
if (TextUtils.isEmpty(messageId)) {
|
|
messageId = extras.getString(STATE_DRAFT_ID);
|
|
}
|
|
|
|
if (!TextUtils.isEmpty(messageId) && !replyFromNotification) {
|
|
// already saved draft trying to edit here
|
|
composeMessageViewModel.setDraftId(messageId);
|
|
binding.composerProgressLayout.setVisibility(View.VISIBLE);
|
|
if (!TextUtils.isEmpty(composeMessageViewModel.getMessageDataResult().getAddressId())) {
|
|
mSelectedAddressPosition = composeMessageViewModel.getPositionByAddressId();
|
|
}
|
|
fromAddressSpinner.setSelection(mSelectedAddressPosition);
|
|
|
|
composeMessageViewModel.setupEditDraftMessage(messageId, getString(R.string.composer_group_count_of));
|
|
composeMessageViewModel.findDraftMessageById();
|
|
} else {
|
|
// composing new message here
|
|
if (extras.containsKey(EXTRA_TO_RECIPIENTS) || extras.containsKey(EXTRA_TO_RECIPIENT_GROUPS)) {
|
|
List<MessageRecipient> recipientGroups = (List<MessageRecipient>) extras.getSerializable(EXTRA_TO_RECIPIENT_GROUPS);
|
|
if (recipientGroups != null && recipientGroups.size() > 0) {
|
|
addRecipientsToView(recipientGroups, toRecipientView);
|
|
}
|
|
String[] recipientEmails = extras.getStringArray(EXTRA_TO_RECIPIENTS);
|
|
if (recipientEmails != null && recipientEmails.length > 0) {
|
|
addStringRecipientsToView(new ArrayList<>(Arrays.asList(recipientEmails)), toRecipientView);
|
|
}
|
|
messageBodyEditText.requestFocus();
|
|
} else {
|
|
checkPermissionsAndKeyboardToggle();
|
|
}
|
|
if (extras.containsKey(EXTRA_CC_RECIPIENTS)) {
|
|
String[] recipientEmails = extras.getStringArray(EXTRA_CC_RECIPIENTS);
|
|
addStringRecipientsToView(new ArrayList<>(Arrays.asList(recipientEmails)), ccRecipientView);
|
|
mAreAdditionalRowsVisible = true;
|
|
focusRespondInline();
|
|
}
|
|
List<LocalAttachment> attachmentsList = new ArrayList<>();
|
|
if (extras.containsKey(EXTRA_MESSAGE_ATTACHMENTS)) {
|
|
attachmentsList = extras.getParcelableArrayList(EXTRA_MESSAGE_ATTACHMENTS);
|
|
if (attachmentsList != null) {
|
|
for (LocalAttachment localAttachment : attachmentsList) {
|
|
localAttachment.setAttachmentId("");
|
|
localAttachment.setMessageId("");
|
|
}
|
|
}
|
|
composeMessageViewModel.setEmbeddedAttachmentList(extras.getParcelableArrayList(EXTRA_MESSAGE_EMBEDDED_ATTACHMENTS));
|
|
}
|
|
String messageTitle = extras.getString(EXTRA_MESSAGE_TITLE, "");
|
|
subjectEditText.setText(messageTitle);
|
|
|
|
Constants.MessageActionType messageActionType = (Constants.MessageActionType) extras.getSerializable(EXTRA_ACTION_ID);
|
|
composeMessageViewModel.setupComposingNewMessage(messageActionType != null ? messageActionType : Constants.MessageActionType.NONE,
|
|
extras.getString(EXTRA_PARENT_ID, null), getString(R.string.composer_group_count_of));
|
|
|
|
composeMessageViewModel.prepareMessageData(messageTitle, new ArrayList(attachmentsList));
|
|
|
|
String content;
|
|
largeBody = extras.getBoolean(EXTRA_MESSAGE_BODY_LARGE, false);
|
|
if (largeBody) {
|
|
content = mBigContentHolder.getContent();
|
|
mBigContentHolder.setContent(null);
|
|
} else {
|
|
content = extras.getString(EXTRA_MESSAGE_BODY);
|
|
}
|
|
String composerContent = null;
|
|
if (extras.containsKey(STATE_ADDED_CONTENT)) {
|
|
composerContent = extras.getString(STATE_ADDED_CONTENT);
|
|
}
|
|
if (extras.getBoolean(EXTRA_MAIL_TO, false)) {
|
|
composerContent = content;
|
|
content = "";
|
|
}
|
|
|
|
initialiseMessageBody(intent, extras, type, content, composerContent);
|
|
}
|
|
} else {
|
|
composeMessageViewModel.prepareMessageData(false, "", "");
|
|
initialiseMessageBody(intent, null, type, null, null);
|
|
}
|
|
}
|
|
|
|
private void initialiseMessageBody(Intent intent, Bundle extras, String type, String content, String composerContent) {
|
|
if (extras != null && (!TextUtils.isEmpty(content) || (!TextUtils.isEmpty(composerContent) && extras.getBoolean(EXTRA_MAIL_TO)))) {
|
|
// forward, reply, reply all here
|
|
try {
|
|
composeMessageViewModel.setMessageTimestamp(extras.getLong(EXTRA_MESSAGE_TIMESTAMP));
|
|
String senderName = extras.getString(EXTRA_SENDER_NAME);
|
|
String senderAddress = extras.getString(EXTRA_SENDER_ADDRESS);
|
|
|
|
composeMessageViewModel.setSender(senderName != null ? senderName : "", senderAddress != null ? senderAddress : "");
|
|
|
|
setMessageBodyInContainers(
|
|
composeMessageViewModel.setMessageBody(
|
|
composerContent,
|
|
content,
|
|
true,
|
|
composeMessageViewModel.getMessageDataResult().isPGPMime(),
|
|
getString(R.string.original_message_divider),
|
|
getQuoteHeader()
|
|
)
|
|
);
|
|
} catch (Exception exc) {
|
|
Timber.tag("588").e(exc, "Exception on initialise message body");
|
|
}
|
|
} else if (extras != null && extras.containsKey(EXTRA_MESSAGE_ID) && extras.getBoolean(EXTRA_REPLY_FROM_NOTIFICATION, false)) {
|
|
// reply from notification here
|
|
composeMessageViewModel.setMessageTimestamp(extras.getLong(EXTRA_MESSAGE_TIMESTAMP));
|
|
composeMessageViewModel.setSender(extras.getString(EXTRA_SENDER_NAME, ""), extras.getString(EXTRA_SENDER_ADDRESS, ""));
|
|
composeMessageViewModel.startFetchMessageDetailJob(extras.getString(EXTRA_MESSAGE_ID, ""));
|
|
setMessageBody();
|
|
} else if (extras == null || (!extras.containsKey(EXTRA_MESSAGE_ID) && !extras.containsKey(STATE_DRAFT_ID))) {
|
|
// compose new message here
|
|
composeMessageViewModel.setBeforeSaveDraft(false, messageBodyEditText.getText().toString());
|
|
setMessageBody();
|
|
}
|
|
if (Intent.ACTION_SEND.equals(mAction) && type != null) {
|
|
handleSendSingleFile(intent);
|
|
} else if (Intent.ACTION_SEND_MULTIPLE.equals(mAction)) {
|
|
handleSendMultipleFiles(intent);
|
|
} else if (Intent.ACTION_VIEW.equals(mAction)) {
|
|
handleActionView(intent);
|
|
} else if (Intent.ACTION_SENDTO.equals(mAction)) {
|
|
extractMailTo(intent);
|
|
}
|
|
}
|
|
|
|
private void setMessageBody() {
|
|
setMessageBody("");
|
|
}
|
|
|
|
private void setMessageBody(String composerBody) {
|
|
setMessageBodyInContainers(
|
|
composeMessageViewModel.setMessageBody(
|
|
null,
|
|
composerBody,
|
|
true,
|
|
false,
|
|
getString(R.string.original_message_divider),
|
|
getQuoteHeader()
|
|
)
|
|
);
|
|
}
|
|
|
|
private String getQuoteHeader() {
|
|
String timestamp = DateUtil.formatDetailedDateTime(this, composeMessageViewModel.getMessageDataResult().getMessageTimestamp());
|
|
return getString(
|
|
R.string.composer_quote_sender_header,
|
|
timestamp,
|
|
composeMessageViewModel.getMessageDataResult().getSenderName(),
|
|
composeMessageViewModel.getMessageDataResult().getSenderEmailAddress()
|
|
);
|
|
}
|
|
|
|
private void onFetchEmailKeysEvent(List<FetchPublicKeysResult> results) {
|
|
mSendingPressed = false;
|
|
binding.composerProgressLayout.setVisibility(View.GONE);
|
|
boolean isRetry = false;
|
|
for (FetchPublicKeysResult result : results) {
|
|
if (result instanceof FetchPublicKeysResult.Success) {
|
|
FetchPublicKeysResult.Success success = (FetchPublicKeysResult.Success) result;
|
|
isRetry = isRetry || success.isSendRetryRequired();
|
|
String email = success.getEmail();
|
|
String key = success.getKey();
|
|
Constants.RecipientLocationType location = success.getRecipientsType();
|
|
if (location == Constants.RecipientLocationType.TO) {
|
|
toRecipientView.setEmailPublicKey(email, key);
|
|
} else if (location == Constants.RecipientLocationType.CC) {
|
|
ccRecipientView.setEmailPublicKey(email, key);
|
|
} else if (location == Constants.RecipientLocationType.BCC) {
|
|
bccRecipientView.setEmailPublicKey(email, key);
|
|
}
|
|
} else {
|
|
FetchPublicKeysResult.Failure failure = (FetchPublicKeysResult.Failure) result;
|
|
String errorMessage;
|
|
MessageRecipientView recipientView = toRecipientView;
|
|
Constants.RecipientLocationType location = failure.getRecipientsType();
|
|
if (location == Constants.RecipientLocationType.TO) {
|
|
recipientView = toRecipientView;
|
|
} else if (location == Constants.RecipientLocationType.CC) {
|
|
recipientView = ccRecipientView;
|
|
} else if (location == Constants.RecipientLocationType.BCC) {
|
|
recipientView = bccRecipientView;
|
|
}
|
|
|
|
if (failure.getError() instanceof FetchPublicKeysResult.Failure.Error.WithMessage) {
|
|
String baseErrorMessage = ((FetchPublicKeysResult.Failure.Error.WithMessage) failure.getError()).getMessage();
|
|
errorMessage = getString(R.string.composer_removing_address_server_error, failure.getEmail(), baseErrorMessage);
|
|
recipientView.removeObjectForKey(failure.getEmail());
|
|
|
|
} else {
|
|
errorMessage = getString(R.string.composer_removing_address_generic_error, failure.getEmail());
|
|
}
|
|
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show();
|
|
}
|
|
}
|
|
|
|
Timber.v("onFetchEmailKeysEvent size:%d isRetry:%s", results.size(), isRetry);
|
|
if (isRetry) {
|
|
sendMessage(false);
|
|
}
|
|
}
|
|
|
|
private void focusRespondInline() {
|
|
NestedScrollView scrollView = binding.composerScrollView;
|
|
scrollView.postDelayed(() -> scrollView.scrollTo(0, respondInlineButton.getBottom()), 1000);
|
|
}
|
|
|
|
private void setRespondInlineVisibility(boolean visible) {
|
|
if (visible) {
|
|
respondInlineButton.setVisibility(View.VISIBLE);
|
|
composeMessageViewModel.setIsRespondInlineButtonVisible(true);
|
|
binding.composerRespondInlineButton.setVisibility(View.VISIBLE);
|
|
} else {
|
|
respondInlineButton.setVisibility(View.GONE);
|
|
composeMessageViewModel.setIsRespondInlineButtonVisible(false);
|
|
binding.composerRespondInlineButton.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
|
|
private int skipInitial;
|
|
private TextWatcher typingListener = new TextWatcher() {
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onTextChanged(CharSequence text, int start, int before, int count) {
|
|
if (skipInitial < 2) {
|
|
skipInitial++;
|
|
return;
|
|
}
|
|
skipInitial++;
|
|
composeMessageViewModel.autoSaveDraft(text.toString());
|
|
}
|
|
|
|
@Override
|
|
public void afterTextChanged(Editable s) {
|
|
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public void recipientsSelected(@NonNull ArrayList<MessageRecipient> recipients, @Nonnull Constants.RecipientLocationType location) {
|
|
MessageRecipientView recipient = toRecipientView;
|
|
if (location == Constants.RecipientLocationType.CC) {
|
|
recipient = ccRecipientView;
|
|
} else if (location == Constants.RecipientLocationType.BCC) {
|
|
recipient = bccRecipientView;
|
|
}
|
|
addRecipientsToView(recipients, recipient);
|
|
}
|
|
|
|
private void onConnectivityEvent(Constants.ConnectionState connectivity) {
|
|
Timber.v("onConnectivityEvent hasConnectivity:%s DoHOngoing:%s", connectivity.name(), isDohOngoing);
|
|
if (!isDohOngoing) {
|
|
if (connectivity != Constants.ConnectionState.CONNECTED) {
|
|
networkSnackBarUtil.getNoConnectionSnackBar(
|
|
mSnackLayout,
|
|
mUserManager.requireCurrentLegacyUser(),
|
|
this,
|
|
null,
|
|
binding.composerBottomAppBar.getId(),
|
|
connectivity == Constants.ConnectionState.NO_INTERNET
|
|
).show();
|
|
} else {
|
|
networkSnackBarUtil.hideAllSnackBars();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void extractMailTo(Intent intent) {
|
|
Uri mailtoUri = intent.getData();
|
|
if (mailtoUri != null && MailToUtils.MAILTO_SCHEME.equals(mailtoUri.getScheme())) {
|
|
MailToData mailToData = composeMessageViewModel.parseMailTo(intent.getDataString());
|
|
addStringRecipientsToView(mailToData.getAddresses(), toRecipientView);
|
|
} else {
|
|
try {
|
|
ArrayList<String> emails = (ArrayList<String>) intent.getSerializableExtra(Intent.EXTRA_EMAIL);
|
|
addStringRecipientsToView(emails, toRecipientView);
|
|
} catch (Exception e) {
|
|
Timber.d(e, "Failed extracting recipients from the intent");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TODO: this method has originally copied from {@link #extractMailTo(Intent)} and edited, instead of edit the original one, for do not break any logic in other places where it's being called
|
|
* Handle {@link Intent#ACTION_VIEW} for populate the relative fields
|
|
*/
|
|
private void handleActionView(Intent intent) {
|
|
Uri uri = Objects.requireNonNull(intent.getData());
|
|
String stringUri = uri.toString();
|
|
if (stringUri.startsWith(MailToUtils.MAILTO_SCHEME)) {
|
|
MailToData mailToData = composeMessageViewModel.parseMailTo(stringUri);
|
|
// Set recipient
|
|
addStringRecipientsToView(mailToData.getAddresses(), toRecipientView);
|
|
// Set cc
|
|
if (!mailToData.getCc().isEmpty() || !mailToData.getBcc().isEmpty()) {
|
|
setAdditionalRowVisibility(true);
|
|
mAreAdditionalRowsVisible = true;
|
|
}
|
|
addStringRecipientsToView(mailToData.getCc(), ccRecipientView);
|
|
// Set bcc
|
|
addStringRecipientsToView(mailToData.getBcc(), bccRecipientView);
|
|
// Set subject
|
|
subjectEditText.setText(mailToData.getSubject());
|
|
// Set body
|
|
Editable oldBody = messageBodyEditText.getText();
|
|
Editable newBody = Editable.Factory.getInstance().newEditable(mailToData.getBody());
|
|
messageBodyEditText.setText(newBody.append(oldBody));
|
|
|
|
} else {
|
|
try {
|
|
ArrayList<String> emails = (ArrayList<String>) intent.getSerializableExtra(Intent.EXTRA_EMAIL);
|
|
addStringRecipientsToView(emails, toRecipientView);
|
|
} catch (Exception e) {
|
|
Timber.d(e, "Failed extracting recipients from the intent");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void handleSendSingleFile(Intent intent) {
|
|
SpannableStringBuilder contentToShareBuilder = new SpannableStringBuilder();
|
|
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
|
|
|
String sharedSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
|
|
if (!TextUtils.isEmpty(sharedSubject)) {
|
|
subjectEditText.setText(sharedSubject);
|
|
}
|
|
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
|
|
if (!TextUtils.isEmpty(sharedText)) {
|
|
String composerText = messageBodyEditText.getText().toString();
|
|
String builder = sharedText + System.getProperty("line.separator") + composerText;
|
|
messageBodyEditText.setText(builder);
|
|
}
|
|
String contentToShare = contentToShareBuilder.toString();
|
|
if (!TextUtils.isEmpty(contentToShare)) {
|
|
String composerText = messageBodyEditText.getText().toString();
|
|
String contentString = contentToShare + System.getProperty("line.separator") + composerText;
|
|
Spannable contentSpannable = new SpannableString(contentString);
|
|
Linkify.addLinks(contentSpannable, Linkify.ALL);
|
|
messageBodyEditText.setText(contentSpannable);
|
|
setRespondInlineVisibility(false);
|
|
composeMessageViewModel.setBeforeSaveDraft(false, messageBodyEditText.getText().toString());
|
|
}
|
|
try {
|
|
extractMailTo(intent);
|
|
} catch (Exception e) {
|
|
Timber.d(e, "Handle set text: extracting email");
|
|
}
|
|
handleSendFileUri(uri);
|
|
}
|
|
|
|
private void handleSendFileUri(Uri uri) {
|
|
if (uri != null) {
|
|
composerInstanceId = UUID.randomUUID().toString();
|
|
importAttachments(new String[]{uri.toString()});
|
|
}
|
|
}
|
|
|
|
void handleSendMultipleFiles(Intent intent) {
|
|
ArrayList<Uri> fileUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
|
if (fileUris != null) {
|
|
String[] uriStrings = new String[fileUris.size()];
|
|
for (int i = 0; i < fileUris.size(); i++) {
|
|
uriStrings[i] = fileUris.get(i).toString();
|
|
}
|
|
composerInstanceId = UUID.randomUUID().toString();
|
|
importAttachments(uriStrings);
|
|
}
|
|
}
|
|
|
|
void importAttachments(String[] uriStrings) {
|
|
Data data = new Data.Builder()
|
|
.putStringArray(KEY_INPUT_DATA_FILE_URIS_STRING_ARRAY, uriStrings)
|
|
.putString(KEY_INPUT_DATA_COMPOSER_INSTANCE_ID, composerInstanceId)
|
|
.build();
|
|
OneTimeWorkRequest importAttachmentsWork = new OneTimeWorkRequest.Builder(ImportAttachmentsWorker.class)
|
|
.setInputData(data)
|
|
.build();
|
|
WorkManager.getInstance().enqueue(importAttachmentsWork);
|
|
WorkManager.getInstance().getWorkInfoByIdLiveData(importAttachmentsWork.getId()).observe(this, workInfo -> {
|
|
if (workInfo != null) {
|
|
// Get the Event from Worker
|
|
String json = workInfo.getOutputData().getString(composerInstanceId);
|
|
if (json != null) {
|
|
PostImportAttachmentEvent event = SerializationUtils.deserialize(
|
|
json, PostImportAttachmentEvent.class
|
|
);
|
|
onPostImportAttachmentEvent(event);
|
|
}
|
|
Log.d("PMTAG", "ImportAttachmentsWorker workInfo = " + workInfo.getState());
|
|
}
|
|
});
|
|
}
|
|
|
|
@Subscribe
|
|
public void onPostImportAttachmentEvent(PostImportAttachmentEvent event) {
|
|
if (event == null || event.composerInstanceId == null || !event.composerInstanceId.equals(composerInstanceId)) {
|
|
return;
|
|
}
|
|
|
|
List<LocalAttachment> attachmentsList = composeMessageViewModel.getMessageDataResult().getAttachmentList();
|
|
boolean alreadyAdded = CollectionsKt.firstOrNull(attachmentsList, localAttachment ->
|
|
localAttachment.getUri().toString().equals(event.uri)
|
|
) != null;
|
|
|
|
if (!alreadyAdded) {
|
|
attachmentsList.add(new LocalAttachment(Uri.parse(event.uri), event.displayName, event.size, event.mimeType));
|
|
renderViews();
|
|
composeMessageViewModel.buildMessage();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onStart() {
|
|
super.onStart();
|
|
ProtonMailApplication.getApplication().getBus().register(this);
|
|
ProtonMailApplication.getApplication().getBus().register(composeMessageViewModel);
|
|
AlarmReceiver alarmReceiver = new AlarmReceiver();
|
|
alarmReceiver.setAlarm(this, true);
|
|
if (askForPermission) {
|
|
contactsPermissionHelper.checkPermission();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
super.onResume();
|
|
checkDelinquency();
|
|
renderViews();
|
|
composeMessageViewModel.checkConnectivity();
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
UiUtil.hideKeyboard(this);
|
|
super.onPause();
|
|
}
|
|
|
|
@Override
|
|
protected void contactPermissionGranted() {
|
|
loadPMContacts();
|
|
if (!composeMessageViewModel.getAndroidContactsLoaded()) {
|
|
getSupportLoaderManager().initLoader(LOADER_ID_ANDROID_CONTACTS, null, this);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void contactPermissionDenied() {
|
|
loadPMContacts();
|
|
}
|
|
|
|
private void onStoragePermissionGranted() {
|
|
ArrayList<LocalAttachment> embeddedAttachmentsList = composeMessageViewModel.getMessageDataResult().getEmbeddedAttachmentsList();
|
|
if (composeMessageViewModel.getMessageDataResult().getShowImages()) {
|
|
// get messageId from one of the attachments and use it to start DownloadEmbeddedAttachmentsWorker
|
|
for (LocalAttachment localAttachment : embeddedAttachmentsList) {
|
|
if (!TextUtils.isEmpty(localAttachment.getMessageId())) {
|
|
attachmentsWorker.enqueue(
|
|
localAttachment.getMessageId(),
|
|
mUserManager.getCurrentUserId(),
|
|
null
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onStop() {
|
|
super.onStop();
|
|
askForPermission = true;
|
|
ProtonMailApplication.getApplication().getBus().unregister(this);
|
|
ProtonMailApplication.getApplication().getBus().unregister(composeMessageViewModel);
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
super.onSaveInstanceState(outState);
|
|
outState.putBoolean(STATE_ADDITIONAL_ROWS_VISIBLE, mAreAdditionalRowsVisible);
|
|
outState.putString(STATE_DRAFT_ID, composeMessageViewModel.getDraftId());
|
|
if (largeBody) {
|
|
outState.putBoolean(EXTRA_MESSAGE_BODY_LARGE, true);
|
|
mBigContentHolder.setContent(messageBodyEditText.getText().toString());
|
|
} else {
|
|
outState.putString(EXTRA_MESSAGE_BODY, composeMessageViewModel.getMessageDataResult().getInitialMessageContent());
|
|
}
|
|
outState.putString(STATE_ADDED_CONTENT, composeMessageViewModel.getContent(messageBodyEditText.getText().toString()));
|
|
outState.putString(EXTRA_SENDER_NAME, composeMessageViewModel.getMessageDataResult().getSenderName());
|
|
outState.putString(EXTRA_SENDER_ADDRESS, composeMessageViewModel.getMessageDataResult().getSenderEmailAddress());
|
|
outState.putLong(EXTRA_MESSAGE_TIMESTAMP, composeMessageViewModel.getMessageDataResult().getMessageTimestamp());
|
|
}
|
|
|
|
@Override
|
|
public void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
|
|
super.onRestoreInstanceState(savedInstanceState);
|
|
mAreAdditionalRowsVisible = savedInstanceState.getBoolean(STATE_ADDITIONAL_ROWS_VISIBLE);
|
|
String draftId = savedInstanceState.getString(STATE_DRAFT_ID);
|
|
composeMessageViewModel.setDraftId(!TextUtils.isEmpty(draftId) ? draftId : "");
|
|
composeMessageViewModel.setInitialMessageContent(savedInstanceState.getString(EXTRA_MESSAGE_BODY));
|
|
}
|
|
|
|
@Override
|
|
public void onBackPressed() {
|
|
if (composeMessageViewModel.isDraftEmpty(this)) {
|
|
composeMessageViewModel.deleteDraft();
|
|
finishActivity(false);
|
|
} else {
|
|
UiUtil.hideKeyboard(ComposeMessageActivity.this);
|
|
composeMessageViewModel.setBeforeSaveDraft(true, messageBodyEditText.getText().toString(), UserAction.SAVE_DRAFT_EXIT);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
|
getMenuInflater().inflate(R.menu.menu_compose_message, menu);
|
|
setMenuActionsListeners(menu);
|
|
this.menu = menu;
|
|
disableSendButton(true);
|
|
return true;
|
|
}
|
|
|
|
private void setMenuActionsListeners(Menu menu) {
|
|
MenuItem item = menu.findItem(R.id.send_message);
|
|
item.getActionView().findViewById(R.id.send_button).setOnClickListener((View view) -> {
|
|
if (!mSendingPressed)
|
|
onOptionSendHandler();
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case android.R.id.home:
|
|
onBackPressed();
|
|
return true;
|
|
}
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
|
|
private void onOptionSendHandler() {
|
|
boolean containsNonPMAndNonPGPRecipients = toRecipientView.includesNonProtonMailAndNonPGPRecipient() || ccRecipientView.includesNonProtonMailAndNonPGPRecipient() || bccRecipientView.includesNonProtonMailAndNonPGPRecipient();
|
|
boolean containsPgpRecipients = toRecipientView.containsPGPRecipient() || ccRecipientView.containsPGPRecipient() || bccRecipientView.containsPGPRecipient();
|
|
boolean showSection1 = bottomAppBar.hasExpiration() && !bottomAppBar.hasPassword() && containsNonPMAndNonPGPRecipients;
|
|
boolean showSection2 = bottomAppBar.hasExpiration() && containsPgpRecipients;
|
|
if (showSection1 && showSection2) {
|
|
List<String> nonProtonMailRecipients = toRecipientView.getNonProtonMailAndNonPGPRecipients();
|
|
nonProtonMailRecipients.addAll(ccRecipientView.getNonProtonMailAndNonPGPRecipients());
|
|
nonProtonMailRecipients.addAll(bccRecipientView.getNonProtonMailAndNonPGPRecipients());
|
|
List<String> pgpRecipients = toRecipientView.getPGPRecipients();
|
|
pgpRecipients.addAll(ccRecipientView.getPGPRecipients());
|
|
pgpRecipients.addAll(bccRecipientView.getPGPRecipients());
|
|
showExpirationTimeError(nonProtonMailRecipients, pgpRecipients);
|
|
} else if (showSection1) {
|
|
// if expiration time is set, without password and there are recipients which are not PM users, we show the popup
|
|
List<String> nonProtonMailRecipients = toRecipientView.getNonProtonMailAndNonPGPRecipients();
|
|
nonProtonMailRecipients.addAll(ccRecipientView.getNonProtonMailAndNonPGPRecipients());
|
|
nonProtonMailRecipients.addAll(bccRecipientView.getNonProtonMailAndNonPGPRecipients());
|
|
showExpirationTimeError(nonProtonMailRecipients, null);
|
|
} else if (showSection2) {
|
|
// we should add condition if the message sent is pgp
|
|
List<String> pgpRecipients = toRecipientView.getPGPRecipients();
|
|
pgpRecipients.addAll(ccRecipientView.getPGPRecipients());
|
|
pgpRecipients.addAll(bccRecipientView.getPGPRecipients());
|
|
showExpirationTimeError(null, pgpRecipients);
|
|
} else {
|
|
sendMessage(false);
|
|
}
|
|
}
|
|
|
|
private void sendMessage(boolean sendAnyway) {
|
|
if (isMessageValid(sendAnyway)) {
|
|
if (subjectEditText.getText().toString().equals("")) {
|
|
DialogUtils.Companion.showInfoDialogWithTwoButtons(ComposeMessageActivity.this,
|
|
getString(R.string.compose),
|
|
getString(R.string.no_subject),
|
|
getString(R.string.no),
|
|
getString(R.string.yes),
|
|
unit -> {
|
|
UiUtil.hideKeyboard(this);
|
|
composeMessageViewModel.finishBuildingMessage(messageBodyEditText.getText().toString());
|
|
return unit;
|
|
}, true);
|
|
} else {
|
|
UiUtil.hideKeyboard(this);
|
|
composeMessageViewModel.finishBuildingMessage(messageBodyEditText.getText().toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
private void initRecipientsView(final MessageRecipientView recipientsView, ArrayAdapter adapter, final Constants.RecipientLocationType location) {
|
|
recipientsView.setAdapter(adapter);
|
|
recipientsView.allowCollapse(true);
|
|
recipientsView.setSplitChar(RECIPIENT_SEPARATORS);
|
|
recipientsView.setThreshold(1);
|
|
recipientsView.setLocation(location);
|
|
recipientsView.setDeletionStyle(TokenCompleteTextView.TokenDeleteStyle.PartialCompletion);
|
|
recipientsView.setTokenClickStyle(TokenCompleteTextView.TokenClickStyle.None);
|
|
try {
|
|
Field mCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes");
|
|
mCursorDrawableRes.setAccessible(true);
|
|
mCursorDrawableRes.set(recipientsView, R.drawable.cursor_black);
|
|
} catch (Exception e) {
|
|
// NOOP
|
|
}
|
|
recipientsView.setTokenListener(new TokenCompleteTextView.TokenListener<MessageRecipient>() {
|
|
@Override
|
|
public void onTokenAdded(MessageRecipient token) {
|
|
List<String> emailList = new ArrayList<>();
|
|
if (TextUtils.isEmpty(token.getGroup())) {
|
|
emailList.add(token.getEmailAddress());
|
|
} else {
|
|
List<MessageRecipient> groupRecipients = token.getGroupRecipients();
|
|
for (MessageRecipient recipient : groupRecipients) {
|
|
emailList.add(recipient.getEmailAddress());
|
|
}
|
|
}
|
|
|
|
FetchPublicKeysRequest emailKeysRequest = new FetchPublicKeysRequest(emailList, location, false);
|
|
composeMessageViewModel.startFetchPublicKeys(Collections.singletonList(emailKeysRequest));
|
|
GetSendPreferenceJob.Destination destination = GetSendPreferenceJob.Destination.TO;
|
|
if (recipientsView.equals(ccRecipientView)) {
|
|
destination = GetSendPreferenceJob.Destination.CC;
|
|
} else if (recipientsView.equals(bccRecipientView)) {
|
|
destination = GetSendPreferenceJob.Destination.BCC;
|
|
}
|
|
composeMessageViewModel.startSendPreferenceJob(emailList, destination);
|
|
}
|
|
|
|
@Override
|
|
public void onTokenRemoved(MessageRecipient token) {
|
|
|
|
recipientsView.removeKey(token.getEmailAddress());
|
|
recipientsView.removeToken(token.getEmailAddress());
|
|
}
|
|
});
|
|
}
|
|
|
|
private void renderViews() {
|
|
setAdditionalRowVisibility(mAreAdditionalRowsVisible);
|
|
int attachmentsListSize = composeMessageViewModel.getMessageDataResult().getAttachmentList().size();
|
|
bottomAppBar.setAttachmentsCount(attachmentsListSize);
|
|
if (composeMessageViewModel.getMessageDataResult().getSendPreferences() == null) {
|
|
return;
|
|
}
|
|
for (GetSendPreferenceJob.Destination dest : GetSendPreferenceJob.Destination.values()) {
|
|
MessageRecipientView recipientsView = getRecipientView(dest);
|
|
List<MessageRecipient> recipients = recipientsView.getMessageRecipients();
|
|
if (recipients == null) {
|
|
continue;
|
|
}
|
|
for (MessageRecipient recipient : recipients) {
|
|
String email = recipient.getEmailAddress();
|
|
SendPreference preference = composeMessageViewModel.getMessageDataResult().getSendPreferences().get(email);
|
|
if (preference != null) {
|
|
setRecipientIconAndDescription(preference, recipientsView);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void fillMessageFromUserInputs(@NonNull Message message, boolean isDraft) {
|
|
message.setMessageId(composeMessageViewModel.getDraftId());
|
|
String subject = subjectEditText.getText().toString();
|
|
if (TextUtils.isEmpty(subject)) {
|
|
subject = getString(R.string.empty_subject);
|
|
} else {
|
|
subject = subject.replaceAll("\n", " ");
|
|
}
|
|
message.setSubject(subject);
|
|
User user = mUserManager.getCurrentLegacyUser();
|
|
if (!TextUtils.isEmpty(composeMessageViewModel.getMessageDataResult().getAddressId())) {
|
|
message.setAddressID(composeMessageViewModel.getMessageDataResult().getAddressId());
|
|
} else if (user != null) {
|
|
int actionType = composeMessageViewModel.getActionType().ordinal();
|
|
Constants.MessageActionType messageActionType = Constants.MessageActionType.Companion.fromInt(actionType);
|
|
if (messageActionType != Constants.MessageActionType.REPLY && messageActionType != Constants.MessageActionType.REPLY_ALL) {
|
|
try {
|
|
message.setAddressID(user.getSenderAddressIdByEmail((String) fromAddressSpinner.getSelectedItem()));
|
|
message.setSenderName(user.getSenderAddressNameByEmail((String) fromAddressSpinner.getSelectedItem()));
|
|
} catch (Exception exc) {
|
|
Timber.tag("588").e(exc, "Exception on fill message with user inputs");
|
|
}
|
|
} else {
|
|
message.setAddressID(user.getDefaultAddressId());
|
|
message.setSenderName(user.getDisplayName());
|
|
}
|
|
}
|
|
message.setToList(toRecipientView.getMessageRecipients());
|
|
message.setCcList(ccRecipientView.getMessageRecipients());
|
|
message.setBccList(bccRecipientView.getMessageRecipients());
|
|
message.setDecryptedBody(composeMessageViewModel.getMessageDataResult().getContent());
|
|
message.setEmbeddedImagesArray(composeMessageViewModel.getMessageDataResult().getContent());
|
|
message.setMessageEncryption(MessageEncryption.INTERNAL);
|
|
message.setLabelIDs(message.getAllLabelIDs());
|
|
message.setInline(composeMessageViewModel.getMessageDataResult().isRespondInlineChecked());
|
|
if (isDraft) {
|
|
message.setIsRead(true);
|
|
message.addLabels(Arrays.asList(String.valueOf(Constants.MessageLocationType.ALL_DRAFT.getMessageLocationTypeValue()),
|
|
String.valueOf(Constants.MessageLocationType.ALL_MAIL.getMessageLocationTypeValue()),
|
|
String.valueOf(Constants.MessageLocationType.DRAFT.getMessageLocationTypeValue())));
|
|
message.setLocation(Constants.MessageLocationType.DRAFT.getMessageLocationTypeValue());
|
|
message.setTime(ServerTime.currentTimeMillis() / 1000);
|
|
message.setMessageEncryption(MessageEncryption.INTERNAL);
|
|
message.setDownloaded(true);
|
|
}
|
|
}
|
|
|
|
private boolean isInvalidRecipientPresent(MessageRecipientView... messageRecipientViews) {
|
|
|
|
for (MessageRecipientView messageRecipientView : messageRecipientViews) {
|
|
String invalidRecipient = messageRecipientView.findInvalidRecipient();
|
|
if (!TextUtils.isEmpty(invalidRecipient)) {
|
|
String message = getString(R.string.invalid_email_address, invalidRecipient);
|
|
TextExtensions.showToast(this, message, Toast.LENGTH_LONG, Gravity.CENTER);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private boolean mSendingPressed = false;
|
|
|
|
private boolean isMessageValid(boolean sendAnyway) {
|
|
|
|
toRecipientView.clearFocus();
|
|
ccRecipientView.clearFocus();
|
|
bccRecipientView.clearFocus();
|
|
if (isInvalidRecipientPresent(toRecipientView, ccRecipientView, bccRecipientView)) {
|
|
return false;
|
|
}
|
|
int totalRecipients = toRecipientView.getRecipientCount() + ccRecipientView.getRecipientCount() + bccRecipientView
|
|
.getRecipientCount();
|
|
if (totalRecipients == 0) {
|
|
TextExtensions.showToast(this, R.string.no_recipients_specified, Toast.LENGTH_LONG, Gravity.CENTER);
|
|
return false;
|
|
}
|
|
long attachmentFileSize = composeMessageViewModel.calculateAttachmentFileSize();
|
|
if (attachmentFileSize > Constants.MAX_ATTACHMENT_FILE_SIZE_IN_BYTES) {
|
|
String message = getString(R.string.attachment_limit, Formatter.formatFileSize(this, Constants.MAX_ATTACHMENT_FILE_SIZE_IN_BYTES), Formatter.formatFileSize(this, attachmentFileSize));
|
|
TextExtensions.showToast(this, message, Toast.LENGTH_LONG, Gravity.CENTER);
|
|
return false;
|
|
}
|
|
if (bottomAppBar.hasPassword()) {
|
|
List<String> toMissingKeys = toRecipientView.addressesWithMissingKeys();
|
|
List<String> ccMissingKeys = ccRecipientView.addressesWithMissingKeys();
|
|
List<String> bccMissingKeys = bccRecipientView.addressesWithMissingKeys();
|
|
boolean isValid = true;
|
|
List<FetchPublicKeysRequest> keysRequests = new ArrayList<>();
|
|
if (!toMissingKeys.isEmpty()) {
|
|
keysRequests.add(
|
|
new FetchPublicKeysRequest(toMissingKeys, Constants.RecipientLocationType.TO, true)
|
|
);
|
|
isValid = false;
|
|
}
|
|
if (!ccMissingKeys.isEmpty()) {
|
|
keysRequests.add(
|
|
new FetchPublicKeysRequest(ccMissingKeys, Constants.RecipientLocationType.CC, true)
|
|
);
|
|
isValid = false;
|
|
}
|
|
if (!bccMissingKeys.isEmpty()) {
|
|
keysRequests.add(
|
|
new FetchPublicKeysRequest(bccMissingKeys, Constants.RecipientLocationType.BCC, true)
|
|
);
|
|
isValid = false;
|
|
}
|
|
if (!isValid) {
|
|
if (mNetworkUtil.isConnected()) {
|
|
composeMessageViewModel.startFetchPublicKeys(keysRequests);
|
|
binding.composerProgressLayout.setVisibility(View.VISIBLE);
|
|
mSendingPressed = true;
|
|
} else {
|
|
// TODO: 3/10/17 update with message can not send
|
|
TextExtensions.showToast(this, "Please send password encrypted messages when you have connection", Toast.LENGTH_SHORT);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
boolean includesNonProtonMailRecipient = includesNonProtonMailRecipient();
|
|
if (!sendAnyway && includesNonProtonMailRecipient && bottomAppBar.hasExpiration() && !bottomAppBar.hasPassword()) {
|
|
TextExtensions.showToast(this, R.string.no_password_specified, Toast.LENGTH_LONG, Gravity.CENTER);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean includesNonProtonMailRecipient() {
|
|
|
|
return toRecipientView.includesNonProtonMailRecipient()
|
|
|| ccRecipientView.includesNonProtonMailRecipient()
|
|
|| bccRecipientView.includesNonProtonMailRecipient();
|
|
}
|
|
|
|
private void setAdditionalRowVisibility(boolean show) {
|
|
final int icon = show ? R.drawable.ic_chevron_up : R.drawable.ic_chevron_down;
|
|
binding.composerExpandRecipientsButton.setImageResource(icon);
|
|
final int visibility = show ? View.VISIBLE : View.GONE;
|
|
binding.composerExpandedRecipientsGroup.setVisibility(visibility);
|
|
}
|
|
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
if (requestCode == REQUEST_CODE_ADD_ATTACHMENTS && resultCode == RESULT_OK) {
|
|
Timber.d("ComposeMessageAct.onActivityResult Received add attachment response with result OK");
|
|
askForPermission = false;
|
|
ArrayList<LocalAttachment> resultAttachmentList = data.getParcelableArrayListExtra(AddAttachmentsActivity.EXTRA_ATTACHMENT_LIST);
|
|
ArrayList<LocalAttachment> listToSet = resultAttachmentList != null ? resultAttachmentList : new ArrayList<>();
|
|
composeMessageViewModel.setAttachmentList(listToSet);
|
|
afterAttachmentsAdded();
|
|
} else {
|
|
Timber.d("ComposeMessageAct.onActivityResult Received result not handled. \n" +
|
|
"Request code = %s\n" +
|
|
"Result code = %s", requestCode, resultCode);
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
}
|
|
}
|
|
|
|
private void afterAttachmentsAdded() {
|
|
composeMessageViewModel.setBeforeSaveDraft(false, messageBodyEditText.getText().toString());
|
|
renderViews();
|
|
}
|
|
|
|
@Subscribe
|
|
public void onSendPreferencesEvent(SendPreferencesEvent event) {
|
|
|
|
Map<String, SendPreference> sendPreferenceMap = event.getSendPreferenceMap();
|
|
if (sendPreferenceMap != null) {
|
|
final MessageRecipientView recipientsView = getRecipientView(event.getDestination());
|
|
for (final Map.Entry<String, SendPreference> entry : sendPreferenceMap.entrySet()) {
|
|
SendPreference sendPreference = entry.getValue();
|
|
if (sendPreference == null) {
|
|
if (event.getStatus() == Status.FAILED) {
|
|
TextExtensions.showToast(this, String.format(getString(R.string.recipient_error_and_removed), entry.getKey()));
|
|
} else {
|
|
TextExtensions.showToast(this, String.format(getString(R.string.recipient_not_found_and_removed), entry.getKey()));
|
|
}
|
|
recipientsView.removeToken(entry.getKey());
|
|
recipientsView.removeObjectForKey(entry.getKey());
|
|
return;
|
|
}
|
|
if (!sendPreference.isPrimaryPinned() && sendPreference.hasPinnedKeys()) {
|
|
// send with untrusted key popup
|
|
DialogUtils.Companion.showInfoDialog(
|
|
ComposeMessageActivity.this,
|
|
getString(R.string.send_with_untrusted_key_title),
|
|
String.format(getString(R.string.send_with_untrusted_key_message), entry.getKey()),
|
|
unit -> unit);
|
|
}
|
|
boolean isVerified = sendPreference.isVerified();
|
|
if (!isVerified) {
|
|
// send
|
|
DialogUtils.Companion.showInfoDialogWithTwoButtons(
|
|
ComposeMessageActivity.this,
|
|
getString(R.string.resign_contact_title),
|
|
String.format(getString(R.string.resign_contact_message), entry.getKey()),
|
|
getString(R.string.cancel),
|
|
getString(R.string.yes),
|
|
unit -> {
|
|
recipientsView.removeToken(entry.getKey());
|
|
recipientsView.removeObjectForKey(entry.getKey());
|
|
return unit;
|
|
},
|
|
unit -> {
|
|
GetSendPreferenceJob.Destination destination = GetSendPreferenceJob.Destination.TO;
|
|
if (recipientsView.equals(ccRecipientView)) {
|
|
destination = GetSendPreferenceJob.Destination.CC;
|
|
} else if (recipientsView.equals(bccRecipientView)) {
|
|
destination = GetSendPreferenceJob.Destination.BCC;
|
|
}
|
|
composeMessageViewModel.startResignContactJobJob(entry.getKey(), entry.getValue(), destination);
|
|
return unit;
|
|
},
|
|
false);
|
|
}
|
|
if (isVerified) {
|
|
setRecipientIconAndDescription(sendPreference, recipientsView);
|
|
}
|
|
}
|
|
recipientsView.setSendPreferenceMap(composeMessageViewModel.getMessageDataResult().getSendPreferences(), bottomAppBar.hasPassword());
|
|
}
|
|
}
|
|
|
|
private MessageRecipientView getRecipientView(GetSendPreferenceJob.Destination destination) {
|
|
|
|
switch (destination) {
|
|
case TO:
|
|
return toRecipientView;
|
|
case CC:
|
|
return ccRecipientView;
|
|
case BCC:
|
|
return bccRecipientView;
|
|
}
|
|
return toRecipientView;
|
|
}
|
|
|
|
@Subscribe
|
|
public void onResignContactEvent(ResignContactEvent event) {
|
|
|
|
MessageRecipientView recipientsView = getRecipientView(event.getDestination());
|
|
SendPreference sendPreference = event.getSendPreference();
|
|
if (event.getStatus() == ContactEvent.SUCCESS) {
|
|
setRecipientIconAndDescription(sendPreference, recipientsView);
|
|
} else {
|
|
recipientsView.removeToken(sendPreference.getEmailAddress());
|
|
}
|
|
}
|
|
|
|
private void setRecipientIconAndDescription(SendPreference sendPreference, MessageRecipientView recipientView) {
|
|
|
|
String email = sendPreference.getEmailAddress();
|
|
SendPreferencesToMessageEncryptionUiModelMapper sendPreferencesMapper = new SendPreferencesToMessageEncryptionUiModelMapper();
|
|
MessageEncryptionUiModel encryptionUiModel = sendPreferencesMapper.toMessageEncryptionUiModel(sendPreference, bottomAppBar.hasPassword());
|
|
|
|
composeMessageViewModel.addSendPreferences(sendPreference);
|
|
recipientView.setIconAndDescription(
|
|
email,
|
|
encryptionUiModel.getLockIcon(),
|
|
encryptionUiModel.getLockIconColor(),
|
|
encryptionUiModel.getTooltip(),
|
|
sendPreference.isPGP()
|
|
);
|
|
}
|
|
|
|
private void finishActivity(boolean isSaveDraftAndExit) {
|
|
if (isSaveDraftAndExit) {
|
|
setResult(RESULT_OK, new Intent().putExtra(EXTRA_MESSAGE_ID, composeMessageViewModel.getDraftId()));
|
|
} else {
|
|
setResult(RESULT_OK);
|
|
}
|
|
if (isTaskRoot()) {
|
|
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
|
if (activityManager != null) {
|
|
List<ActivityManager.AppTask> tasks = activityManager.getAppTasks();
|
|
if (tasks != null && tasks.size() > 0) {
|
|
tasks.get(0).finishAndRemoveTask();
|
|
}
|
|
}
|
|
} else
|
|
finish();
|
|
}
|
|
|
|
@Subscribe
|
|
public void onFetchDraftDetailEvent(FetchDraftDetailEvent event) {
|
|
binding.composerProgressLayout.setVisibility(View.GONE);
|
|
if (event.success) {
|
|
composeMessageViewModel.initSignatures();
|
|
composeMessageViewModel.processSignature();
|
|
onMessageLoaded(event.getMessage(), true, true);
|
|
} else {
|
|
if (!isFinishing()) {
|
|
DialogUtils.Companion.showInfoDialog(ComposeMessageActivity.this, getString(R.string.app_name), getString(R.string.messages_load_failure),
|
|
unit -> {
|
|
finishActivity(false);
|
|
return unit;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Executed when the message is loaded and everything is prepared for rendering it. It can come from the server or from DB
|
|
*
|
|
* @param loadedMessage the message itself
|
|
* @param renderBody whether to render the body
|
|
* @param updateAttachments if the attachments should be updated
|
|
*/
|
|
private void onMessageLoaded(@Nullable final Message loadedMessage, boolean renderBody, boolean updateAttachments) {
|
|
if (loadedMessage == null || !loadedMessage.isDownloaded()) {
|
|
return;
|
|
} else {
|
|
binding.composerProgressLayout.setVisibility(View.GONE);
|
|
}
|
|
AddressCrypto crypto = Crypto.forAddress(mUserManager, mUserManager.requireCurrentUserId(), new AddressId(loadedMessage.getAddressID()));
|
|
if (updateAttachments) {
|
|
composeMessageViewModel.createLocalAttachments(loadedMessage);
|
|
}
|
|
if (!renderBody) {
|
|
return;
|
|
}
|
|
if (loadedMessage.getToList().size() != 0) {
|
|
toRecipientView.clear();
|
|
addRecipientsToView(loadedMessage.getToList(), toRecipientView);
|
|
}
|
|
if (loadedMessage.getCcList().size() != 0) {
|
|
ccRecipientView.clear();
|
|
addRecipientsToView(loadedMessage.getCcList(), ccRecipientView);
|
|
mAreAdditionalRowsVisible = true;
|
|
}
|
|
if (loadedMessage.getBccList().size() != 0) {
|
|
bccRecipientView.clear();
|
|
addRecipientsToView(loadedMessage.getBccList(), bccRecipientView);
|
|
mAreAdditionalRowsVisible = true;
|
|
}
|
|
subjectEditText.setText(loadedMessage.getSubject());
|
|
String messageBody = loadedMessage.getMessageBody();
|
|
try {
|
|
TextDecryptionResult tct = crypto.decrypt(new CipherText(messageBody));
|
|
messageBody = tct.getDecryptedData();
|
|
} catch (Exception e) {
|
|
Timber.w(e, "Decryption error");
|
|
}
|
|
composeMessageViewModel.setInitialMessageContent(messageBody);
|
|
if (loadedMessage.isInline()) {
|
|
String mimeType = loadedMessage.getMimeType();
|
|
setInlineContent(messageBody, false, mimeType != null && mimeType.equals(Constants.MIME_TYPE_PLAIN_TEXT));
|
|
} else {
|
|
binding.composerQuotedMessageContainer.setVisibility(View.VISIBLE);
|
|
quotedMessageWebView.setVisibility(View.VISIBLE);
|
|
composeMessageViewModel.setIsMessageBodyVisible(true);
|
|
quotedMessageWebView.setFocusable(false);
|
|
quotedMessageWebView.requestFocus();
|
|
String delim = "<div class=\"verticalLine\">";
|
|
String webDelim = "<blockquote class=\"protonmail_quote\"";
|
|
int delimIndex = messageBody.indexOf(delim);
|
|
if (delimIndex == -1) {
|
|
delimIndex = messageBody.indexOf(webDelim);
|
|
}
|
|
String mimeType = loadedMessage.getMimeType();
|
|
if (delimIndex > -1) {
|
|
String composeBody = messageBody.substring(0, delimIndex);
|
|
setInlineContent(composeBody, true, mimeType != null && mimeType.equals(Constants.MIME_TYPE_PLAIN_TEXT));
|
|
messageBody = messageBody.substring(delimIndex);
|
|
composeMessageViewModel.setInitialMessageContent(messageBody);
|
|
composeMessageViewModel.setContent(messageBody);
|
|
setBodyContent(false, mimeType != null && mimeType.equals(Constants.MIME_TYPE_PLAIN_TEXT));
|
|
quotedMessageWebView.setVisibility(View.VISIBLE);
|
|
composeMessageViewModel.setIsMessageBodyVisible(true);
|
|
quotedMessageWebView.setFocusable(false);
|
|
quotedMessageWebView.requestFocus();
|
|
setRespondInlineVisibility(true);
|
|
} else {
|
|
setInlineContent(messageBody, false, mimeType != null && mimeType.equals(Constants.MIME_TYPE_PLAIN_TEXT));
|
|
}
|
|
}
|
|
composeMessageViewModel.onMessageLoaded(loadedMessage);
|
|
renderViews();
|
|
new SaveMassageTask(messageDetailsRepository, loadedMessage).execute();
|
|
new Handler(Looper.getMainLooper()).postDelayed(() -> disableSendButton(false), 500);
|
|
}
|
|
|
|
private void setInlineContent(String messageBody, boolean clean, boolean isPlainText) {
|
|
|
|
final String originalMessageDividerString = getString(R.string.original_message_divider);
|
|
if (clean && messageBody.contains(originalMessageDividerString)) {
|
|
Spanned secondPart = htmlToSpanned.invoke(messageBody.substring(messageBody.indexOf(originalMessageDividerString)));
|
|
binding.composerQuoteHeaderTextView.setText(secondPart);
|
|
composeMessageViewModel.setQuotedHeader(secondPart);
|
|
messageBody = messageBody.substring(0, messageBody.indexOf(originalMessageDividerString));
|
|
}
|
|
setRespondInlineVisibility(false);
|
|
quotedMessageWebView.setVisibility(View.GONE);
|
|
composeMessageViewModel.setIsMessageBodyVisible(false);
|
|
messageBodyEditText.setVisibility(View.VISIBLE);
|
|
composeMessageViewModel.setContent(messageBody);
|
|
setBodyContent(true, isPlainText);
|
|
}
|
|
|
|
@Override
|
|
public void onMessagePasswordChanged() {
|
|
|
|
renderViews();
|
|
}
|
|
|
|
@Override
|
|
public void onMessageExpirationChanged() {
|
|
|
|
renderViews();
|
|
}
|
|
|
|
private Map<MessageRecipientView, List<MessageRecipient>> pendingRecipients = new HashMap<>();
|
|
|
|
private void addStringRecipientsToView(List<String> recipients, MessageRecipientView messageRecipientView) {
|
|
for (String recipient : recipients) {
|
|
if (CommonExtensionsKt.isValidEmail(recipient)) {
|
|
messageRecipientView.addObject(new MessageRecipient("", recipient));
|
|
} else {
|
|
String message = getString(R.string.invalid_email_address_removed, recipient);
|
|
TextExtensions.showToast(this, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addRecipientsToView(List<MessageRecipient> recipients, MessageRecipientView messageRecipientView) {
|
|
if (!composeMessageViewModel.getSetupCompleteValue()) {
|
|
pendingRecipients.put(messageRecipientView, recipients);
|
|
return;
|
|
} else {
|
|
messageRecipientView.clear();
|
|
}
|
|
Map<String, List<MessageRecipient>> groupedRecipients = new HashMap<>();
|
|
for (MessageRecipient recipient : recipients) {
|
|
// loop all recipients
|
|
if (!CommonExtensionsKt.isValidEmail(recipient.getEmailAddress())) {
|
|
String message = getString(R.string.invalid_email_address_removed, recipient);
|
|
TextExtensions.showToast(this, message);
|
|
continue;
|
|
}
|
|
String group = recipient.getGroup();
|
|
if (!TextUtils.isEmpty(group)) {
|
|
List<MessageRecipient> groupRecipients = groupedRecipients.get(group);
|
|
if (groupRecipients == null) {
|
|
groupRecipients = new ArrayList();
|
|
}
|
|
groupRecipients.add(recipient);
|
|
groupedRecipients.put(group, groupRecipients);
|
|
} else {
|
|
messageRecipientView.addObject(new MessageRecipient("", recipient.getEmailAddress()));
|
|
}
|
|
}
|
|
for (Map.Entry<String, List<MessageRecipient>> entry : groupedRecipients.entrySet()) {
|
|
List<MessageRecipient> groupRecipients = entry.getValue();
|
|
String groupName = entry.getKey();
|
|
ContactLabelUiModel group = composeMessageViewModel.getContactGroupByName(groupName);
|
|
if (group != null) {
|
|
String name = String.format(getString(R.string.composer_group_count_of), groupName, group.getContactEmailsCount(), group.getContactEmailsCount());
|
|
if (groupRecipients.size() != group.getContactEmailsCount()) {
|
|
if (groupRecipients.size() < group.getContactEmailsCount()) {
|
|
name = String.format(getString(R.string.composer_group_count_of), groupName, groupRecipients.size(), group.getContactEmailsCount());
|
|
} else {
|
|
List<MessageRecipient> groupRecipientList = composeMessageViewModel.getContactGroupRecipients(group);
|
|
Iterator groupRecipientsIterator = groupRecipients.iterator();
|
|
while (groupRecipientsIterator.hasNext()) {
|
|
MessageRecipient currentMR = (MessageRecipient) groupRecipientsIterator.next();
|
|
boolean found = false;
|
|
for (MessageRecipient groupMR : groupRecipientList) {
|
|
if (currentMR.getEmailAddress().equals(groupMR.getEmailAddress())) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
groupRecipientsIterator.remove();
|
|
messageRecipientView.addObject(new MessageRecipient("", currentMR.getEmailAddress()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MessageRecipient recipient = new MessageRecipient(name, "");
|
|
recipient.setGroup(groupName);
|
|
recipient.setGroupIcon(R.string.contact_group_groups_icon);
|
|
recipient.setGroupColor(Color.parseColor(UiUtil.normalizeColor(group.getColor())));
|
|
recipient.setGroupRecipients(groupRecipients);
|
|
messageRecipientView.addObject(recipient);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setBodyContent(boolean respondInline, boolean isPlainText) {
|
|
|
|
String content = composeMessageViewModel.getMessageDataResult().getContent();
|
|
if (!respondInline) {
|
|
String css = AppUtil.readTxt(this, R.raw.css_reset_with_custom_props);
|
|
String darkCss = "";
|
|
if (composeMessageViewModel.isAppInDarkMode(this)) {
|
|
darkCss = AppUtil.readTxt(this, R.raw.css_reset_dark_mode_only);
|
|
}
|
|
Transformer viewportTransformer = new ViewportTransformer(renderDimensionsProvider.getRenderWidth(this), css, darkCss);
|
|
Transformer contentTransformer = new DefaultTransformer()
|
|
.pipe(viewportTransformer)
|
|
.pipe(new AbstractTransformer() {
|
|
@Override
|
|
public Document transform(Document doc) {
|
|
return doc;
|
|
}
|
|
});
|
|
Document doc = Jsoup.parse(content);
|
|
doc.outputSettings().indentAmount(0).prettyPrint(false);
|
|
doc = contentTransformer.transform(doc);
|
|
content = doc.toString();
|
|
pmWebViewClient.blockRemoteResources(!composeMessageViewModel.getMessageDataResult().getShowRemoteContent());
|
|
quotedMessageWebView.loadDataWithBaseURL("", content, "text/html", HTTP.UTF_8, "");
|
|
} else {
|
|
final CharSequence bodyText;
|
|
if (isPlainText) {
|
|
bodyText = content;
|
|
} else {
|
|
bodyText = htmlToSpanned.invoke(content);
|
|
}
|
|
messageBodyEditText.setText(bodyText);
|
|
}
|
|
}
|
|
|
|
private void setMessageBodyInContainers(ComposeMessageViewModel.MessageBodySetup messageBodySetup) {
|
|
messageBodyEditText.setText(messageBodySetup.getComposeBody());
|
|
binding.composerQuotedMessageContainer.setVisibility(messageBodySetup.getWebViewVisibility() ? View.VISIBLE : View.GONE);
|
|
binding.composerQuoteHeaderTextView.setText(composeMessageViewModel.getMessageDataResult().getQuotedHeader());
|
|
setRespondInlineVisibility(messageBodySetup.getRespondInlineVisibility());
|
|
setBodyContent(messageBodySetup.getRespondInline(), messageBodySetup.isPlainText());
|
|
}
|
|
|
|
@Subscribe
|
|
public void onDownloadEmbeddedImagesEvent(DownloadEmbeddedImagesEvent event) {
|
|
if (event.getStatus().equals(Status.SUCCESS)) {
|
|
Timber.v("onDownloadEmbeddedImagesEvent %s", event.getStatus());
|
|
String content = composeMessageViewModel.getMessageDataResult().getContent();
|
|
String css = AppUtil.readTxt(this, R.raw.css_reset_with_custom_props);
|
|
String darkCss = "";
|
|
if (composeMessageViewModel.isAppInDarkMode(this)) {
|
|
darkCss = AppUtil.readTxt(this, R.raw.css_reset_dark_mode_only);
|
|
}
|
|
Transformer contentTransformer = new ViewportTransformer(renderDimensionsProvider.getRenderWidth(this), css, darkCss);
|
|
Document doc = Jsoup.parse(content);
|
|
doc.outputSettings().indentAmount(0).prettyPrint(false);
|
|
doc = contentTransformer.transform(doc);
|
|
content = doc.toString();
|
|
pmWebViewClient.blockRemoteResources(!composeMessageViewModel.getMessageDataResult().getShowRemoteContent());
|
|
EmbeddedImagesThread mEmbeddedImagesTask = new EmbeddedImagesThread(new WeakReference<>(ComposeMessageActivity.this.quotedMessageWebView), event, content);
|
|
mEmbeddedImagesTask.execute();
|
|
}
|
|
}
|
|
|
|
private void loadPMContacts() {
|
|
if (mUserManager.getCurrentLegacyUser().getCombinedContacts()) {
|
|
Set<UserId> userIds = AccountManagerKt.allLoggedInBlocking(accountManager);
|
|
for(UserId userId : userIds) {
|
|
composeMessageViewModel.fetchContactGroups(userId);
|
|
}
|
|
} else {
|
|
composeMessageViewModel.fetchContactGroups(mUserManager.requireCurrentUserId());
|
|
}
|
|
|
|
composeMessageViewModel.getContactGroupsResult().observe(this, messageRecipients -> {
|
|
recipientAdapter.setData(messageRecipients);
|
|
});
|
|
composeMessageViewModel.getPmMessageRecipientsResult().observe(this, messageRecipients -> {
|
|
recipientAdapter.setData(messageRecipients);
|
|
});
|
|
composeMessageViewModel.getSetupComplete().observe(this, event -> {
|
|
Boolean setUpComplete = event.getContentIfNotHandled();
|
|
if (setUpComplete != null && setUpComplete) {
|
|
composeMessageViewModel.loadPMContactsIfNeeded();
|
|
}
|
|
});
|
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
|
|
getSupportLoaderManager().initLoader(LOADER_ID_ANDROID_CONTACTS, null, this);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String getSearchTerm() {
|
|
return "";
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
|
if (loader.getId() == LOADER_ID_ANDROID_CONTACTS && data != null) {
|
|
while (data.moveToNext()) {
|
|
fromAndroidCursor(data);
|
|
}
|
|
composeMessageViewModel.getAndroidMessageRecipientsResult().observe(this, messageRecipients -> {
|
|
recipientAdapter.setData(messageRecipients); // no groups
|
|
});
|
|
composeMessageViewModel.onAndroidContactsLoaded();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
|
}
|
|
|
|
public void fromAndroidCursor(Cursor cursor) {
|
|
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DISPLAY_NAME_PRIMARY));
|
|
if (TextUtils.isEmpty(name)) {
|
|
int idx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DISPLAY_NAME);
|
|
if (idx >= 0) { // this is workaround for sometimes on some devices we get illegal state exception
|
|
if (cursor.getType(idx) == Cursor.FIELD_TYPE_STRING) {
|
|
name = cursor.getString(idx);
|
|
}
|
|
}
|
|
}
|
|
if (name == null) {
|
|
name = "";
|
|
}
|
|
String email = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS));
|
|
if (!TextUtils.isEmpty(email)) {
|
|
composeMessageViewModel.createMessageRecipient(name, email);
|
|
}
|
|
}
|
|
|
|
private void showPmChangedDialog(String address) {
|
|
|
|
DialogUtils.Companion.warningDialog(ComposeMessageActivity.this, getString(R.string.dont_remind_again), getString(R.string.okay), String.format(getString(R.string.pm_me_changed), address),
|
|
unit -> {
|
|
defaultSharedPreferences.edit().putBoolean(Constants.Prefs.PREF_PM_ADDRESS_CHANGED, true).apply();
|
|
return unit;
|
|
});
|
|
}
|
|
|
|
private void showExpirationTimeError(List<String> recipientsMissingPassword, List<String> recipientsDisablePgp) {
|
|
UiUtil.buildExpirationTimeErrorDialog(this, recipientsMissingPassword, recipientsDisablePgp, v -> sendMessage(true));
|
|
}
|
|
|
|
private class KeyboardManager implements ViewTreeObserver.OnGlobalLayoutListener {
|
|
@Override
|
|
public void onGlobalLayout() {
|
|
|
|
ConstraintLayout rootLayout = binding.rootLayout;
|
|
int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
|
|
if (heightDiff > 150) {
|
|
binding.dummyKeyboard.setVisibility(View.VISIBLE);
|
|
} else {
|
|
binding.dummyKeyboard.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class ComposeBodyChangeListener implements View.OnKeyListener {
|
|
@Override
|
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private class RespondInlineButtonClickListener implements View.OnClickListener {
|
|
@Override
|
|
public void onClick(View v) {
|
|
|
|
composeMessageViewModel.setRespondInline(true);
|
|
setRespondInlineVisibility(false);
|
|
quotedMessageWebView.setVisibility(View.GONE);
|
|
composeMessageViewModel.setIsMessageBodyVisible(false);
|
|
quotedMessageWebView.loadData("", "text/html; charset=utf-8", HTTP.UTF_8);
|
|
binding.composerQuoteHeaderTextView.setVisibility(View.GONE);
|
|
String composeContentBuilder = messageBodyEditText.getText().toString() +
|
|
System.getProperty("line.separator") +
|
|
binding.composerQuoteHeaderTextView.getText().toString() +
|
|
htmlToSpanned.invoke(composeMessageViewModel.getMessageDataResult().getInitialMessageContent());
|
|
messageBodyEditText.setText(composeContentBuilder);
|
|
}
|
|
}
|
|
|
|
private class MessageTitleEditorActionListener implements TextView.OnEditorActionListener {
|
|
@Override
|
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
|
|
subjectEditText.clearFocus();
|
|
messageBodyEditText.requestFocus();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private boolean allowSpinnerListener = false;
|
|
|
|
private class AddressSpinnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
|
|
private void showCanNotSendDialog(String senderAddress) {
|
|
if (!isFinishing()) {
|
|
DialogUtils.Companion.showInfoDialog(
|
|
ComposeMessageActivity.this,
|
|
getString(R.string.info),
|
|
String.format(getString(R.string.error_can_not_send_from_this_address), senderAddress),
|
|
unit -> unit);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onGlobalLayout() {
|
|
|
|
// Here we are just handling the mAddressesSpinner
|
|
|
|
final ViewTreeObserver viewTreeObserver = fromAddressSpinner.getViewTreeObserver();
|
|
viewTreeObserver.removeOnGlobalLayoutListener(this);
|
|
skipInitial = 0;
|
|
messageBodyEditText.addTextChangedListener(typingListener);
|
|
subjectEditText.addTextChangedListener(typingListener);
|
|
fromAddressSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
|
@Override
|
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
if (!allowSpinnerListener) {
|
|
return;
|
|
}
|
|
String email = (String) fromAddressSpinner.getItemAtPosition(position);
|
|
boolean localAttachmentsListEmpty = composeMessageViewModel.getMessageDataResult().getAttachmentList().isEmpty();
|
|
if (!composeMessageViewModel.isPaidUser() && MessageUtils.INSTANCE.isPmMeAddress(email)) {
|
|
fromAddressSpinner.setSelection(mSelectedAddressPosition);
|
|
Snackbar snack = Snackbar.make(binding.rootLayout, String.format(getString(R.string.error_can_not_send_from_this_address), email), Snackbar.LENGTH_LONG);
|
|
snack.setAnchorView(binding.composerBottomAppBar);
|
|
View snackView = snack.getView();
|
|
TextView tv = snackView.findViewById(com.google.android.material.R.id.snackbar_text);
|
|
tv.setTextColor(getColor(R.color.text_inverted));
|
|
snack.show();
|
|
} else {
|
|
String previousSenderAddressId = composeMessageViewModel.getMessageDataResult().getAddressId();
|
|
if (TextUtils.isEmpty(previousSenderAddressId)) { // first switch from default address
|
|
previousSenderAddressId = mUserManager.requireCurrentLegacyUser().getDefaultAddressId();
|
|
}
|
|
|
|
if (TextUtils.isEmpty(composeMessageViewModel.getOldSenderAddressId()) && !localAttachmentsListEmpty) {
|
|
composeMessageViewModel.setOldSenderAddressId(previousSenderAddressId); // only set OldSenderAddressId if it was empty in ViewModel
|
|
}
|
|
|
|
composeMessageViewModel.setSenderAddressIdByEmail(email);
|
|
Address address = composeMessageViewModel.getAddressById();
|
|
if (address.getSend() == 0) {
|
|
fromAddressSpinner.setSelection(mSelectedAddressPosition);
|
|
showCanNotSendDialog(address.getEmail());
|
|
return;
|
|
}
|
|
|
|
String currentMail = messageBodyEditText.getText().toString();
|
|
String newSignature = composeMessageViewModel.getNewSignature();
|
|
boolean isNewSignatureEmpty = TextUtils.isEmpty(newSignature) || TextUtils.isEmpty(htmlToSpanned.invoke(newSignature).toString().trim());
|
|
String signature = composeMessageViewModel.getMessageDataResult().getSignature();
|
|
Spanned signatureSpanned = htmlToSpanned.invoke(signature);
|
|
|
|
boolean isSignatureEmpty = TextUtils.isEmpty(signature) || TextUtils.isEmpty(signatureSpanned.toString().trim());
|
|
if (!isNewSignatureEmpty && !isSignatureEmpty) {
|
|
newSignature = composeMessageViewModel.calculateSignature(newSignature);
|
|
messageBodyEditText.setText(currentMail.replace(signatureSpanned, htmlToSpanned.invoke(newSignature)));
|
|
composeMessageViewModel.processSignature(newSignature);
|
|
} else if (isNewSignatureEmpty && !isSignatureEmpty) {
|
|
if (currentMail.contains(signature)) {
|
|
messageBodyEditText.setText(currentMail.replace("\n\n\n" + signature, ""));
|
|
} else {
|
|
if (currentMail.contains(htmlToSpanned.invoke(signature.trim()))) {
|
|
messageBodyEditText.setText(currentMail.replace("\n\n\n" + htmlToSpanned.invoke(signature.trim()), ""));
|
|
} else {
|
|
messageBodyEditText.setText(currentMail.replace(signatureSpanned, ""));
|
|
}
|
|
}
|
|
composeMessageViewModel.setSignature("");
|
|
} else if (!isNewSignatureEmpty && isSignatureEmpty) {
|
|
newSignature = composeMessageViewModel.calculateSignature(newSignature).trim();
|
|
StringBuilder sb = new StringBuilder(currentMail);
|
|
int lastIndexSpace = currentMail.indexOf("\n\n\n");
|
|
if (lastIndexSpace != -1) {
|
|
sb.insert(lastIndexSpace, "\n\n\n" + newSignature);
|
|
} else {
|
|
sb.append("\n\n\n" + newSignature);
|
|
}
|
|
messageBodyEditText.setText(htmlToSpanned.invoke(sb.toString().replace("\n", newline)));
|
|
composeMessageViewModel.processSignature(newSignature);
|
|
}
|
|
// Trigger Save Draft after changing sender to ensure attachments are encrypted with the right key
|
|
composeMessageViewModel.setBeforeSaveDraft(false, messageBodyEditText.getText().toString());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onNothingSelected(AdapterView<?> parent) {
|
|
// noop
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private class SendMessageObserver implements Observer<Long> {
|
|
|
|
@Override
|
|
public void onChanged(@Nullable Long dbId) {
|
|
@StringRes int sendingToast = R.string.sending_message;
|
|
if (!mNetworkUtil.isConnected()) {
|
|
sendingToast = R.string.sending_message_offline;
|
|
}
|
|
if(dbId == null){
|
|
Timber.d("Error while saving message. DbId is null.");
|
|
TextExtensions.showToast(ComposeMessageActivity.this, R.string.error_saving_try_again);
|
|
} else {
|
|
TextExtensions.showToast(ComposeMessageActivity.this, sendingToast);
|
|
new Handler(Looper.getMainLooper()).postDelayed(() -> finishActivity(false), 500);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class AddAttachmentsObserver implements Observer<List<LocalAttachment>> {
|
|
|
|
@Override
|
|
public void onChanged(@Nullable List<LocalAttachment> attachments) {
|
|
String draftId = composeMessageViewModel.getDraftId();
|
|
Intent intent = AppUtil.decorInAppIntent(new Intent(ComposeMessageActivity.this, AddAttachmentsActivity.class));
|
|
intent.putExtra(AddAttachmentsActivity.EXTRA_DRAFT_CREATED, !TextUtils.isEmpty(draftId) && !MessageUtils.INSTANCE.isLocalMessageId(draftId));
|
|
intent.putParcelableArrayListExtra(AddAttachmentsActivity.EXTRA_ATTACHMENT_LIST, new ArrayList<>(attachments));
|
|
intent.putExtra(AddAttachmentsActivity.EXTRA_DRAFT_ID, draftId);
|
|
startActivityForResult(intent, REQUEST_CODE_ADD_ATTACHMENTS);
|
|
renderViews();
|
|
}
|
|
}
|
|
|
|
private class LoadDraftObserver implements Observer<Message> {
|
|
private final Bundle extras;
|
|
private final Intent intent;
|
|
private final String type;
|
|
|
|
LoadDraftObserver(Bundle extras, Intent intent, String type) {
|
|
this.extras = extras;
|
|
this.intent = intent;
|
|
this.type = type;
|
|
}
|
|
|
|
@Override
|
|
public void onChanged(@Nullable Message message) {
|
|
onMessageLoaded(message, true, true);
|
|
String content;
|
|
if (extras.getBoolean(EXTRA_MESSAGE_BODY_LARGE, false)) {
|
|
content = mBigContentHolder.getContent();
|
|
mBigContentHolder.setContent(null);
|
|
} else {
|
|
content = extras.getString(EXTRA_MESSAGE_BODY);
|
|
}
|
|
String composerContent = null;
|
|
if (extras.containsKey(STATE_ADDED_CONTENT)) {
|
|
composerContent = extras.getString(STATE_ADDED_CONTENT);
|
|
}
|
|
initialiseMessageBody(intent, extras, type, content, composerContent);
|
|
}
|
|
}
|
|
|
|
private class CheckLocalMessageObserver implements Observer<Event<PostResult>> {
|
|
|
|
@Override
|
|
public void onChanged(@Nullable Event<PostResult> postResultEvent) {
|
|
|
|
PostResult result = new PostResult("", Status.UNAUTHORIZED);
|
|
if (postResultEvent != null) {
|
|
result = postResultEvent.getContentIfNotHandled();
|
|
}
|
|
if (result != null && result.getStatus() == Status.SUCCESS) {
|
|
composeMessageViewModel.setBeforeSaveDraft(false, messageBodyEditText.getText().toString());
|
|
renderViews();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class BuildObserver implements Observer<Event<Message>> {
|
|
|
|
@Override
|
|
public void onChanged(@Nullable Event<Message> messageEvent) {
|
|
|
|
Message localMessage = messageEvent.getContentIfNotHandled();
|
|
if (localMessage != null) {
|
|
User user = mUserManager.requireCurrentLegacyUser();
|
|
String aliasAddress = composeMessageViewModel.getMessageDataResult().getAddressEmailAlias();
|
|
MessageSender messageSender;
|
|
if (aliasAddress != null && aliasAddress.equals(fromAddressSpinner.getSelectedItem())) { // it's being sent by alias
|
|
messageSender = new MessageSender(user.getDisplayNameForAddress(composeMessageViewModel.getMessageDataResult().getAddressId()), composeMessageViewModel.getMessageDataResult().getAddressEmailAlias());
|
|
} else {
|
|
Address nonAliasAddress;
|
|
try {
|
|
if (localMessage.getAddressID() != null) {
|
|
nonAliasAddress = user.getAddressById(localMessage.getAddressID());
|
|
} else { // fallback to default address if newly composed message has no addressId
|
|
nonAliasAddress = user.getAddressById(user.getDefaultAddressId());
|
|
}
|
|
messageSender = new MessageSender(nonAliasAddress.getDisplayName(), nonAliasAddress.getEmail());
|
|
} catch (NullPointerException e) {
|
|
Timber.d(e, "Inside " + this.getClass().getName() + " nonAliasAddress was null");
|
|
messageSender = new MessageSender("", "");
|
|
}
|
|
}
|
|
localMessage.setSender(messageSender);
|
|
|
|
} else {
|
|
return;
|
|
}
|
|
UserAction userAction = composeMessageViewModel.getActionType();
|
|
if ((userAction == UserAction.SAVE_DRAFT || userAction == UserAction.SAVE_DRAFT_EXIT) && !mSendingInProgress) {
|
|
// draft
|
|
fillMessageFromUserInputs(localMessage, true);
|
|
localMessage.setExpirationTime(0);
|
|
composeMessageViewModel.saveDraft(localMessage);
|
|
new Handler(Looper.getMainLooper()).postDelayed(() -> disableSendButton(false), 500);
|
|
if (userAction == UserAction.SAVE_DRAFT_EXIT) {
|
|
finishActivity(true);
|
|
}
|
|
} else if (composeMessageViewModel.getActionType() == UserAction.FINISH_EDIT) {
|
|
mSendingInProgress = true;
|
|
//region prepare sending message
|
|
if (!composeMessageViewModel.getMessageDataResult().isPasswordValid()) {
|
|
TextExtensions.showToast(ComposeMessageActivity.this, R.string.eo_password_not_completed, Toast.LENGTH_LONG, Gravity.CENTER);
|
|
return;
|
|
}
|
|
fillMessageFromUserInputs(localMessage, false);
|
|
composeMessageViewModel.sendMessage(localMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void checkPermissionsAndKeyboardToggle() {
|
|
if (ActivityCompat.checkSelfPermission(ComposeMessageActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED && !askForPermission) {
|
|
UiUtil.hideKeyboard(this);
|
|
} else if (ActivityCompat.checkSelfPermission(ComposeMessageActivity.this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED
|
|
&& !askForPermission) {
|
|
UiUtil.hideKeyboard(this);
|
|
} else {
|
|
toRecipientView.requestFocus();
|
|
UiUtil.toggleKeyboard(this, toRecipientView);
|
|
}
|
|
}
|
|
|
|
private void disableSendButton(boolean disable) {
|
|
// Find the menu item you want to style
|
|
if (menu != null) {
|
|
MenuItem item = menu.findItem(R.id.send_message);
|
|
ImageButton imageButton = item.getActionView().findViewById(R.id.send_button);
|
|
if (disable) {
|
|
item.setEnabled(false);
|
|
imageButton.setBackgroundTintList(ColorStateList.valueOf(getColor(R.color.shade_40)));
|
|
} else {
|
|
item.setEnabled(true);
|
|
imageButton.setBackgroundTintList(null);
|
|
}
|
|
}
|
|
}
|
|
}
|