From 35c28c5ad2a164201778543647b3bb1dda9459c2 Mon Sep 17 00:00:00 2001
From: shamim-emon
Date: Wed, 11 Dec 2024 09:48:54 +0600
Subject: [PATCH] Initial focus in message compose screen
---
.../main/java/com/fsck/k9/UiKoinModules.kt | 2 +
.../com/fsck/k9/activity/MessageCompose.java | 169 +++++-------------
.../k9/activity/compose/RecipientMvpView.kt | 3 +
.../k9/activity/compose/RecipientPresenter.kt | 4 +
.../fsck/k9/ui/compose/IntentDataMapper.kt | 76 ++++++++
.../java/com/fsck/k9/ui/compose/KoinModule.kt | 7 +
6 files changed, 141 insertions(+), 120 deletions(-)
create mode 100644 legacy/ui/legacy/src/main/java/com/fsck/k9/ui/compose/IntentDataMapper.kt
create mode 100644 legacy/ui/legacy/src/main/java/com/fsck/k9/ui/compose/KoinModule.kt
diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/UiKoinModules.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/UiKoinModules.kt
index e0737e7e18d..a7ea4c4fa20 100644
--- a/legacy/ui/legacy/src/main/java/com/fsck/k9/UiKoinModules.kt
+++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/UiKoinModules.kt
@@ -10,6 +10,7 @@ import com.fsck.k9.ui.account.accountUiModule
import com.fsck.k9.ui.base.uiBaseModule
import com.fsck.k9.ui.changelog.changelogUiModule
import com.fsck.k9.ui.choosefolder.chooseFolderUiModule
+import com.fsck.k9.ui.compose.composeModule
import com.fsck.k9.ui.endtoend.endToEndUiModule
import com.fsck.k9.ui.folders.foldersUiModule
import com.fsck.k9.ui.identity.identityUiModule
@@ -36,6 +37,7 @@ val uiModules = listOf(
chooseFolderUiModule,
contactsModule,
accountModule,
+ composeModule,
viewModule,
changelogUiModule,
messageSourceModule,
diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java
index 068b75f247a..befb6de0168 100644
--- a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java
+++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/MessageCompose.java
@@ -4,7 +4,6 @@
import java.io.File;
import java.util.Date;
import java.util.HashMap;
-import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
@@ -19,7 +18,6 @@
import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.net.Uri;
-import android.nfc.NfcAdapter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -110,6 +108,8 @@
import com.fsck.k9.ui.R;
import com.fsck.k9.ui.base.K9Activity;
import app.k9mail.legacy.ui.theme.ThemeManager;
+import com.fsck.k9.ui.compose.IntentData;
+import com.fsck.k9.ui.compose.IntentDataMapper;
import com.fsck.k9.ui.compose.QuotedMessageMvpView;
import com.fsck.k9.ui.compose.QuotedMessagePresenter;
import com.fsck.k9.ui.compose.WrapUriTextWatcher;
@@ -118,16 +118,15 @@
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textview.MaterialTextView;
import org.openintents.openpgp.OpenPgpApiManager;
-import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpIntentStarter;
import timber.log.Timber;
@SuppressWarnings("deprecation") // TODO get rid of activity dialogs and indeterminate progress bars
public class MessageCompose extends K9Activity implements OnClickListener,
- CancelListener, AttachmentDownloadCancelListener, OnFocusChangeListener,
- OnOpenPgpInlineChangeListener, OnOpenPgpSignOnlyChangeListener, MessageBuilder.Callback,
- AttachmentPresenter.AttachmentsChangedListener, OnOpenPgpDisableListener {
+ CancelListener, AttachmentDownloadCancelListener, OnFocusChangeListener,
+ OnOpenPgpInlineChangeListener, OnOpenPgpSignOnlyChangeListener, MessageBuilder.Callback,
+ AttachmentPresenter.AttachmentsChangedListener, OnOpenPgpDisableListener {
private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1;
private static final int DIALOG_CONFIRM_DISCARD_ON_BACK = 2;
@@ -140,7 +139,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
public static final String ACTION_FORWARD = "com.fsck.k9.intent.action.FORWARD";
public static final String ACTION_FORWARD_AS_ATTACHMENT = "com.fsck.k9.intent.action.FORWARD_AS_ATTACHMENT";
public static final String ACTION_EDIT_DRAFT = "com.fsck.k9.intent.action.EDIT_DRAFT";
- private static final String ACTION_AUTOCRYPT_PEER = "org.autocrypt.PEER_ACTION";
+ public static final String ACTION_AUTOCRYPT_PEER = "org.autocrypt.PEER_ACTION";
public static final String EXTRA_ACCOUNT = "account";
public static final String EXTRA_MESSAGE_REFERENCE = "message_reference";
@@ -185,6 +184,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
private final MessagingController messagingController = DI.get(MessagingController.class);
private final Preferences preferences = DI.get(Preferences.class);
+ private final IntentDataMapper indentDataMapper = DI.get(IntentDataMapper.class);
+
private final Contacts contacts = DI.get(Contacts.class);
private QuotedMessagePresenter quotedMessagePresenter;
@@ -222,6 +223,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
private Action action;
+ private RecipientMvpView recipientMvpView;
private boolean requestReadReceipt = false;
private MaterialTextView chooseIdentityView;
@@ -304,7 +306,7 @@ public void onCreate(Bundle savedInstanceState) {
ReplyToView replyToView = new ReplyToView(this);
replyToPresenter = new ReplyToPresenter(replyToView);
- RecipientMvpView recipientMvpView = new RecipientMvpView(this);
+ recipientMvpView = new RecipientMvpView(this);
ComposePgpInlineDecider composePgpInlineDecider = new ComposePgpInlineDecider();
ComposePgpEnableByDefaultDecider composePgpEnableByDefaultDecider = new ComposePgpEnableByDefaultDecider();
@@ -374,14 +376,44 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
relatedMessageProcessed = savedInstanceState.getBoolean(STATE_KEY_SOURCE_MESSAGE_PROCED, false);
}
+ IntentData intentData = indentDataMapper.initFromIntent(intent);
+
+ if (intentData.getMailToUri() != null) {
+ Uri uri = intentData.getMailToUri();
+ if (MailTo.isMailTo(uri)) {
+ MailTo mailTo = MailTo.parse(uri);
+ initializeFromMailto(mailTo);
+ }
+ }
+
+ if (intentData.getExtraText() != null && messageContentView.getText().length() == 0) {
+ messageContentView.setText(CrLfConverter.toLf(intentData.getExtraText()));
+ }
+
+ if (intentData.getExtraStream() != null) {
+ attachmentPresenter.addExternalAttachment(intentData.getExtraStream(), intentData.getIntentType());
+ }
+
+ if (intentData.getSubject() != null && subjectView.getText().length() == 0) {
+ subjectView.setText(intentData.getSubject());
+ }
+
+ if (intentData.getShouldInitFromSendOrViewIntent()) {
+ recipientPresenter.initFromSendOrViewIntent(intent);
+ }
+
+ if (intentData.getTrustId() != null) {
+ recipientPresenter.initFromTrustIdAction(intentData.getTrustId());
+ }
- if (initFromIntent(intent)) {
+ if (intentData.getStartedByExternalIntent()) {
action = Action.COMPOSE;
changesMadeSinceLastSave = true;
} else {
String action = intent.getAction();
if (ACTION_COMPOSE.equals(action)) {
this.action = Action.COMPOSE;
+ recipientMvpView.requestFocusOnToField();
} else if (ACTION_REPLY.equals(action)) {
this.action = Action.REPLY;
} else if (ACTION_REPLY_ALL.equals(action)) {
@@ -450,15 +482,6 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
relatedFlag = Flag.FORWARDED;
}
- if (action == Action.REPLY || action == Action.REPLY_ALL ||
- action == Action.EDIT_DRAFT) {
- //change focus to message body.
- messageContentView.requestFocus();
- } else {
- // Explicitly set focus to "To:" input field (see issue 2998)
- recipientMvpView.requestFocusOnToField();
- }
-
updateMessageFormat();
// Set font size of input controls
@@ -480,109 +503,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
setProgressBarIndeterminateVisibility(true);
currentMessageBuilder.reattachCallback(this);
}
- }
-
- /**
- * Handle external intents that trigger the message compose activity.
- *
- *
- * Supported external intents:
- *
- * - {@link Intent#ACTION_VIEW}
- * - {@link Intent#ACTION_SENDTO}
- * - {@link Intent#ACTION_SEND}
- * - {@link Intent#ACTION_SEND_MULTIPLE}
- *
- *
- *
- * @param intent
- * The (external) intent that started the activity.
- *
- * @return {@code true}, if this activity was started by an external intent. {@code false},
- * otherwise.
- */
- private boolean initFromIntent(final Intent intent) {
- boolean startedByExternalIntent = false;
- final String action = intent.getAction();
- if (Intent.ACTION_VIEW.equals(action) || Intent.ACTION_SENDTO.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
- /*
- * Someone has clicked a mailto: link, or scanned an NFC tag. The address is in the URI.
- */
- if (intent.getData() != null) {
- Uri uri = intent.getData();
- if (MailTo.isMailTo(uri)) {
- MailTo mailTo = MailTo.parse(uri);
- initializeFromMailto(mailTo);
- }
- }
-
- /*
- * Note: According to the documentation ACTION_VIEW and ACTION_SENDTO don't accept
- * EXTRA_* parameters.
- * And previously we didn't process these EXTRAs. But it looks like nobody bothers to
- * read the official documentation and just copies wrong sample code that happens to
- * work with the AOSP Email application. And because even big players get this wrong,
- * we're now finally giving in and read the EXTRAs for those actions (below).
- */
- }
-
- if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action) ||
- Intent.ACTION_SENDTO.equals(action) || Intent.ACTION_VIEW.equals(action)) {
- startedByExternalIntent = true;
-
- /*
- * Note: Here we allow a slight deviation from the documented behavior.
- * EXTRA_TEXT is used as message body (if available) regardless of the MIME
- * type of the intent. In addition one or multiple attachments can be added
- * using EXTRA_STREAM.
- */
- CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
- // Only use EXTRA_TEXT if the body hasn't already been set by the mailto URI
- if (text != null && messageContentView.getText().length() == 0) {
- messageContentView.setText(CrLfConverter.toLf(text));
- }
-
- String type = intent.getType();
- if (Intent.ACTION_SEND.equals(action)) {
- Uri stream = IntentCompat.getParcelableExtra(intent,Intent.EXTRA_STREAM, Uri.class);
- if (stream != null) {
- attachmentPresenter.addExternalAttachment(stream, type);
- }
- } else {
- List list = IntentCompat.getParcelableArrayListExtra(
- intent,
- Intent.EXTRA_STREAM,
- Parcelable.class
- );
- if (list != null) {
- for (Parcelable parcelable : list) {
- Uri stream = (Uri) parcelable;
- if (stream != null) {
- attachmentPresenter.addExternalAttachment(stream, type);
- }
- }
- }
- }
-
- String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
- // Only use EXTRA_SUBJECT if the subject hasn't already been set by the mailto URI
- if (subject != null && subjectView.getText().length() == 0) {
- subjectView.setText(subject);
- }
-
- recipientPresenter.initFromSendOrViewIntent(intent);
- }
-
- if (ACTION_AUTOCRYPT_PEER.equals(action)) {
- String trustId = intent.getStringExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID);
- if (trustId != null) {
- recipientPresenter.initFromTrustIdAction(trustId);
- startedByExternalIntent = true;
- }
- }
-
- return startedByExternalIntent;
}
@Override
@@ -1677,6 +1598,14 @@ public void onMessageDataLoadFailed() {
public void onMessageViewInfoLoadFinished(MessageViewInfo messageViewInfo) {
internalMessageHandler.sendEmptyMessage(MSG_PROGRESS_OFF);
loadLocalMessageForDisplay(messageViewInfo, action);
+
+ if(!recipientPresenter.isToAddressAdded()) {
+ recipientMvpView.requestFocusOnToField();
+ } else if (subjectView.getText().length() == 0) {
+ subjectView.requestFocus();
+ } else {
+ messageContentView.requestFocus();
+ }
}
@Override
diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientMvpView.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientMvpView.kt
index c18427f8879..a86e847a1d3 100644
--- a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientMvpView.kt
+++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientMvpView.kt
@@ -71,6 +71,9 @@ class RecipientMvpView(private val activity: MessageCompose) : View.OnFocusChang
val bccRecipients: List
get() = bccView.objects
+ val isToAddressAdded: Boolean
+ get() = presenter.isToAddressAdded()
+
val isCcTextEmpty: Boolean
get() = ccView.text.isEmpty()
diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientPresenter.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientPresenter.kt
index 372912ee475..347f79656fe 100644
--- a/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientPresenter.kt
+++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/activity/compose/RecipientPresenter.kt
@@ -64,6 +64,7 @@ class RecipientPresenter(
private val replyToParser: ReplyToParser,
private val draftStateHeaderParser: AutocryptDraftStateHeaderParser,
) {
+ private var isToAddressAdded: Boolean = false
private lateinit var account: Account
private var alwaysBccAddresses: Array? = null
private var hasContactPicker: Boolean? = null
@@ -254,8 +255,11 @@ class RecipientPresenter(
private fun addToAddresses(vararg toAddresses: Address) {
addRecipientsFromAddresses(RecipientType.TO, *toAddresses)
+ isToAddressAdded = true
}
+ fun isToAddressAdded() = isToAddressAdded
+
private fun addCcAddresses(vararg ccAddresses: Address) {
if (ccAddresses.isNotEmpty()) {
addRecipientsFromAddresses(RecipientType.CC, *ccAddresses)
diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/compose/IntentDataMapper.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/compose/IntentDataMapper.kt
new file mode 100644
index 00000000000..c166010ec96
--- /dev/null
+++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/compose/IntentDataMapper.kt
@@ -0,0 +1,76 @@
+package com.fsck.k9.ui.compose
+
+import android.content.Intent
+import android.net.Uri
+import android.nfc.NfcAdapter
+import android.os.Parcelable
+import androidx.core.content.IntentCompat
+import com.fsck.k9.activity.MessageCompose
+import org.openintents.openpgp.util.OpenPgpApi
+
+class IntentDataMapper {
+
+ fun initFromIntent(intent: Intent): IntentData {
+ val action: String? = intent.action
+ var intentData = IntentData()
+
+ if (Intent.ACTION_VIEW == action || Intent.ACTION_SENDTO == action || NfcAdapter.ACTION_NDEF_DISCOVERED == action) {
+ intent.data?.let { data ->
+ intentData = intentData.copy(mailToUri = data)
+ }
+ }
+
+ if ((Intent.ACTION_SEND == action || Intent.ACTION_SEND_MULTIPLE == action ||
+ Intent.ACTION_SENDTO == action || Intent.ACTION_VIEW == action)
+ ) {
+ intentData = intentData.copy(
+ startedByExternalIntent = true,
+ extraText = intent.getCharSequenceExtra(Intent.EXTRA_TEXT),
+ intentType = intent.type,
+ )
+
+ if ((Intent.ACTION_SEND == action)) {
+ val extraStream = IntentCompat.getParcelableExtra(
+ intent,
+ Intent.EXTRA_STREAM,
+ Uri::class.java,
+ )
+ intentData = intentData.copy(extraStream = extraStream)
+ } else {
+ val list: List? = IntentCompat.getParcelableArrayListExtra(
+ intent,
+ Intent.EXTRA_STREAM,
+ Parcelable::class.java,
+ )
+ list?.let {
+ for (parcelable in it) {
+ intentData = intentData.copy(extraStream = parcelable as Uri)
+ }
+ }
+ }
+ intentData = intentData.copy(
+ subject = intent.getStringExtra(Intent.EXTRA_SUBJECT),
+ shouldInitFromSendOrViewIntent = true
+ )
+ }
+
+ if ((MessageCompose.ACTION_AUTOCRYPT_PEER == action)) {
+ intentData = intentData.copy(
+ trustId = intent.getStringExtra(OpenPgpApi.EXTRA_AUTOCRYPT_PEER_ID),
+ startedByExternalIntent = true,
+ )
+ }
+ return intentData
+ }
+}
+
+data class IntentData(
+ val startedByExternalIntent: Boolean = false,
+ val shouldInitFromSendOrViewIntent: Boolean = false,
+ val mailToUri: Uri? = null,
+ val extraText: CharSequence? = null,
+ val intentType: String? = null,
+ val extraStream: Uri? = null,
+ val subject: String? = null,
+ val trustId: String? = null,
+)
diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/compose/KoinModule.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/compose/KoinModule.kt
new file mode 100644
index 00000000000..1d5c0e38f48
--- /dev/null
+++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/compose/KoinModule.kt
@@ -0,0 +1,7 @@
+package com.fsck.k9.ui.compose
+
+import org.koin.dsl.module
+
+val composeModule = module {
+ factory { IntentDataMapper() }
+}