compose: centralize pgp crypto status into immutable ComposeCryptoStatus object
This commit is contained in:
parent
bbc52b3265
commit
441e35f1cd
11 changed files with 259 additions and 131 deletions
|
@ -0,0 +1,129 @@
|
|||
package com.fsck.k9.activity;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fsck.k9.activity.RecipientMvpView.CryptoStatusDisplayType;
|
||||
import com.fsck.k9.activity.RecipientPresenter.CryptoMode;
|
||||
import com.fsck.k9.view.RecipientSelectView.Recipient;
|
||||
import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus;
|
||||
|
||||
/** This is an immutable object, which contains all relevant metadata entered
|
||||
* during e-mail composition to apply cryptographic operations before sending
|
||||
* or saving as draft.
|
||||
*/
|
||||
public class ComposeCryptoStatus {
|
||||
|
||||
|
||||
private final CryptoMode cryptoMode;
|
||||
private final ArrayList<String> keyReferences;
|
||||
private final boolean allKeysAvailable;
|
||||
private final boolean allKeysVerified;
|
||||
private final Long signingKeyId;
|
||||
private final Long selfEncryptKeyId;
|
||||
|
||||
|
||||
private ComposeCryptoStatus(CryptoMode cryptoMode, boolean allKeysAvailable, boolean allKeysVerified,
|
||||
ArrayList<String> keyReferences, Long signingKeyId, Long selfEncryptKeyId) {
|
||||
this.cryptoMode = cryptoMode;
|
||||
this.keyReferences = keyReferences;
|
||||
this.allKeysAvailable = allKeysAvailable;
|
||||
this.allKeysVerified = allKeysVerified;
|
||||
this.signingKeyId = signingKeyId;
|
||||
this.selfEncryptKeyId = selfEncryptKeyId;
|
||||
}
|
||||
|
||||
public static ComposeCryptoStatus createFromRecipients(CryptoMode cryptoMode, List<Recipient> recipients,
|
||||
Long accountCryptoKey) {
|
||||
ArrayList<String> keyReferences = new ArrayList<String>();
|
||||
|
||||
boolean allKeysAvailable = true;
|
||||
boolean allKeysVerified = true;
|
||||
for (Recipient recipient : recipients) {
|
||||
RecipientCryptoStatus cryptoStatus = recipient.getCryptoStatus();
|
||||
if (cryptoStatus.isAvailable()) {
|
||||
keyReferences.add(recipient.getKeyReference());
|
||||
if (cryptoStatus == RecipientCryptoStatus.AVAILABLE_UNTRUSTED) {
|
||||
allKeysVerified = false;
|
||||
}
|
||||
} else {
|
||||
allKeysAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
// noinspection UnnecessaryLocalVariable
|
||||
Long signingKeyId = accountCryptoKey;
|
||||
// noinspection UnnecessaryLocalVariable // TODO introduce separate key setting here?
|
||||
Long selfEncryptKeyId = accountCryptoKey;
|
||||
|
||||
return new ComposeCryptoStatus(cryptoMode, allKeysAvailable, allKeysVerified, keyReferences,
|
||||
signingKeyId, selfEncryptKeyId);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public long[] getEncryptKeyIds(boolean isDraft) {
|
||||
if (selfEncryptKeyId == null) {
|
||||
return null;
|
||||
}
|
||||
return new long[] { selfEncryptKeyId };
|
||||
}
|
||||
|
||||
public String[] getEncryptKeyReferences(boolean isDraft) {
|
||||
if (isDraft) {
|
||||
return null;
|
||||
}
|
||||
if (keyReferences.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return keyReferences.toArray(new String[keyReferences.size()]);
|
||||
}
|
||||
|
||||
public Long getSigningKeyId() {
|
||||
return signingKeyId;
|
||||
}
|
||||
|
||||
public CryptoStatusDisplayType getCryptoStatusDisplayType() {
|
||||
switch (cryptoMode) {
|
||||
case PRIVATE:
|
||||
if (allKeysAvailable && allKeysVerified) {
|
||||
return CryptoStatusDisplayType.PRIVATE_TRUSTED;
|
||||
} else if (allKeysAvailable) {
|
||||
return CryptoStatusDisplayType.PRIVATE_UNTRUSTED;
|
||||
}
|
||||
return CryptoStatusDisplayType.PRIVATE_NOKEY;
|
||||
case OPPORTUNISTIC:
|
||||
if (allKeysAvailable && allKeysVerified) {
|
||||
return CryptoStatusDisplayType.OPPORTUNISTIC_TRUSTED;
|
||||
} else if (allKeysAvailable) {
|
||||
return CryptoStatusDisplayType.OPPORTUNISTIC_UNTRUSTED;
|
||||
}
|
||||
return CryptoStatusDisplayType.OPPORTUNISTIC_NOKEY;
|
||||
case SIGN_ONLY:
|
||||
return CryptoStatusDisplayType.SIGN_ONLY;
|
||||
default:
|
||||
case DISABLE:
|
||||
return CryptoStatusDisplayType.DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldUsePgpMessageBuilder() {
|
||||
return cryptoMode != CryptoMode.DISABLE;
|
||||
}
|
||||
|
||||
public boolean isEncryptionEnabled() {
|
||||
return cryptoMode == CryptoMode.PRIVATE || cryptoMode == CryptoMode.OPPORTUNISTIC;
|
||||
}
|
||||
|
||||
public boolean isSigningEnabled() {
|
||||
return cryptoMode != CryptoMode.DISABLE && signingKeyId != null;
|
||||
}
|
||||
|
||||
public boolean isMissingSignKey() {
|
||||
return signingKeyId == null;
|
||||
}
|
||||
|
||||
public boolean isPrivateAndIncomplete() {
|
||||
return cryptoMode == CryptoMode.PRIVATE && !allKeysAvailable;
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
|
@ -90,7 +91,6 @@ import com.fsck.k9.mail.Part;
|
|||
import com.fsck.k9.mail.internet.MessageExtractor;
|
||||
import com.fsck.k9.mail.internet.MimeMessage;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.internet.TextBody;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.message.IdentityField;
|
||||
import com.fsck.k9.message.IdentityHeaderParser;
|
||||
|
@ -992,20 +992,19 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||
}
|
||||
}
|
||||
|
||||
private TextBody buildText(boolean isDraft) {
|
||||
return createMessageBuilder(isDraft).buildText();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MessageBuilder createMessageBuilder(boolean isDraft) {
|
||||
MessageBuilder builder;
|
||||
CryptoMode cryptoMode = recipientPresenter.getCurrentCryptoMode();
|
||||
|
||||
if (!recipientPresenter.canSendOrError(isDraft)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ComposeCryptoStatus cryptoStatus = recipientPresenter.getCurrentCryptoStatus();
|
||||
// TODO encrypt drafts for storage
|
||||
if(!isDraft && cryptoMode != CryptoMode.DISABLE) {
|
||||
if(!isDraft && cryptoStatus.shouldUsePgpMessageBuilder()) {
|
||||
PgpMessageBuilder pgpBuilder = new PgpMessageBuilder(getApplicationContext(), getOpenPgpApi());
|
||||
pgpBuilder.setCryptoMode(cryptoMode);
|
||||
pgpBuilder.setSigningKeyId(mAccount.getCryptoKey());
|
||||
// TODO introduce separate key setting here?
|
||||
pgpBuilder.setSelfEncryptKeyId(mAccount.getCryptoKey());
|
||||
pgpBuilder.setCryptoStatus(cryptoStatus);
|
||||
builder = pgpBuilder;
|
||||
} else {
|
||||
builder = new SimpleMessageBuilder(getApplicationContext());
|
||||
|
@ -1077,12 +1076,16 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||
|
||||
private void performSave() {
|
||||
currentMessageBuilder = createMessageBuilder(true);
|
||||
currentMessageBuilder.buildAsync(this);
|
||||
if (currentMessageBuilder != null) {
|
||||
currentMessageBuilder.buildAsync(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void performSend() {
|
||||
currentMessageBuilder = createMessageBuilder(false);
|
||||
currentMessageBuilder.buildAsync(this);
|
||||
if (currentMessageBuilder != null) {
|
||||
currentMessageBuilder.buildAsync(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void onDiscard() {
|
||||
|
@ -3004,8 +3007,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
|
|||
if (!isCryptoProviderEnabled()) {
|
||||
return false;
|
||||
}
|
||||
CryptoMode cryptoMode = recipientPresenter.getCurrentCryptoMode();
|
||||
return cryptoMode == CryptoMode.OPPORTUNISTIC || cryptoMode == CryptoMode.PRIVATE;
|
||||
ComposeCryptoStatus cryptoStatus = recipientPresenter.getCurrentCryptoStatus();
|
||||
return cryptoStatus.isEncryptionEnabled();
|
||||
}
|
||||
|
||||
private boolean shouldSign() {
|
||||
|
|
|
@ -3,22 +3,16 @@ package com.fsck.k9.activity;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.activity.RecipientPresenter.CryptoMode;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.mail.Body;
|
||||
import com.fsck.k9.mail.BodyPart;
|
||||
import com.fsck.k9.mail.Message.RecipientType;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.internet.BinaryTempFileBody;
|
||||
import com.fsck.k9.mail.internet.MimeBodyPart;
|
||||
|
@ -43,9 +37,7 @@ public class PgpMessageBuilder extends MessageBuilder {
|
|||
private final OpenPgpApi openPgpApi;
|
||||
|
||||
private MimeMessage currentProcessedMimeMessage;
|
||||
private long signingKeyId = Account.NO_OPENPGP_KEY;
|
||||
private long selfEncryptKeyId = Account.NO_OPENPGP_KEY;
|
||||
private CryptoMode cryptoMode;
|
||||
private ComposeCryptoStatus cryptoStatus;
|
||||
|
||||
public PgpMessageBuilder(Context context, OpenPgpApi openPgpApi) {
|
||||
super(context);
|
||||
|
@ -54,12 +46,12 @@ public class PgpMessageBuilder extends MessageBuilder {
|
|||
|
||||
/** This class keeps track of its internal state explicitly. */
|
||||
private enum State {
|
||||
IDLE, START,
|
||||
IDLE, START, FAILURE,
|
||||
OPENPGP_SIGN, OPENPGP_SIGN_UI, OPENPGP_SIGN_OK,
|
||||
OPENPGP_ENCRYPT, OPENPGP_ENCRYPT_UI, OPENPGP_ENCRYPT_OK;
|
||||
|
||||
public boolean isBreakState() {
|
||||
return this == OPENPGP_SIGN_UI || this == OPENPGP_ENCRYPT_UI;
|
||||
return this == OPENPGP_SIGN_UI || this == OPENPGP_ENCRYPT_UI || this == FAILURE;
|
||||
}
|
||||
|
||||
public boolean isReentrantState() {
|
||||
|
@ -75,7 +67,7 @@ public class PgpMessageBuilder extends MessageBuilder {
|
|||
State currentState = State.IDLE;
|
||||
|
||||
@Override
|
||||
public void buildMessageInternal() {
|
||||
protected void buildMessageInternal() {
|
||||
if (currentState != State.IDLE) {
|
||||
throw new IllegalStateException("internal error, a PgpMessageBuilder can only be built once!");
|
||||
}
|
||||
|
@ -135,50 +127,35 @@ public class PgpMessageBuilder extends MessageBuilder {
|
|||
return;
|
||||
}
|
||||
|
||||
if (cryptoMode == CryptoMode.SIGN_ONLY) {
|
||||
if (!cryptoStatus.isEncryptionEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean shouldEncryptToSelf = selfEncryptKeyId != Account.NO_OPENPGP_KEY;
|
||||
boolean shouldEncryptToRecipients = !isDraft();
|
||||
boolean isDraft = isDraft();
|
||||
long[] encryptKeyIds = cryptoStatus.getEncryptKeyIds(isDraft);
|
||||
String[] encryptKeyReferences = cryptoStatus.getEncryptKeyReferences(isDraft);
|
||||
|
||||
if (!shouldEncryptToSelf && !shouldEncryptToRecipients) {
|
||||
if (encryptKeyIds == null && encryptKeyReferences == null) {
|
||||
// TODO safeguard here once this is better handled by the caller?
|
||||
// throw new MessagingException("Tried to encrypt draft, but no encryption key specified!");
|
||||
// throw new MessagingException("Encryption is enabled, but no encryption key specified!");
|
||||
return;
|
||||
}
|
||||
|
||||
Intent encryptIntent = new Intent(OpenPgpApi.ACTION_ENCRYPT);
|
||||
encryptIntent.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
|
||||
|
||||
if (shouldEncryptToSelf) {
|
||||
encryptIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, new long[] { selfEncryptKeyId });
|
||||
if (encryptKeyIds != null) {
|
||||
encryptIntent.putExtra(OpenPgpApi.EXTRA_KEY_IDS, encryptKeyIds);
|
||||
}
|
||||
|
||||
if (shouldEncryptToRecipients) {
|
||||
String[] addressStrings = getRecipientAddressStrings(currentProcessedMimeMessage);
|
||||
encryptIntent.putExtra(OpenPgpApi.EXTRA_USER_IDS, addressStrings);
|
||||
if (encryptKeyReferences != null) {
|
||||
encryptIntent.putExtra(OpenPgpApi.EXTRA_KEY_REFERENCES, encryptKeyReferences);
|
||||
}
|
||||
|
||||
currentState = State.OPENPGP_ENCRYPT;
|
||||
mimeIntentLaunch(encryptIntent);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String[] getRecipientAddressStrings(MimeMessage messsage) throws MessagingException {
|
||||
ArrayList<String> recipientAddresses = new ArrayList<String>();
|
||||
for (Address address : messsage.getRecipients(RecipientType.TO)) {
|
||||
recipientAddresses.add(address.getAddress());
|
||||
}
|
||||
for (Address address : messsage.getRecipients(RecipientType.CC)) {
|
||||
recipientAddresses.add(address.getAddress());
|
||||
}
|
||||
for (Address address : messsage.getRecipients(RecipientType.BCC)) {
|
||||
recipientAddresses.add(address.getAddress());
|
||||
}
|
||||
return recipientAddresses.toArray(new String[recipientAddresses.size()]);
|
||||
}
|
||||
|
||||
private void startOrContinueSigningIfRequested(Intent userInteractionResult) throws MessagingException {
|
||||
boolean reenterOperation = currentState == State.OPENPGP_SIGN;
|
||||
if (reenterOperation) {
|
||||
|
@ -186,15 +163,15 @@ public class PgpMessageBuilder extends MessageBuilder {
|
|||
return;
|
||||
}
|
||||
|
||||
boolean isNoSigningKeyAvailable = signingKeyId == Account.NO_OPENPGP_KEY;
|
||||
boolean signingDisabled = !cryptoStatus.isSigningEnabled();
|
||||
boolean alreadySigned = currentState.isSignOk();
|
||||
boolean isDraft = isDraft();
|
||||
if (isNoSigningKeyAvailable || alreadySigned || isDraft) {
|
||||
if (signingDisabled || alreadySigned || isDraft) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent signIntent = new Intent(OpenPgpApi.ACTION_DETACHED_SIGN);
|
||||
signIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, signingKeyId);
|
||||
signIntent.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, cryptoStatus.getSigningKeyId());
|
||||
|
||||
currentState = State.OPENPGP_SIGN;
|
||||
mimeIntentLaunch(signIntent);
|
||||
|
@ -319,16 +296,8 @@ public class PgpMessageBuilder extends MessageBuilder {
|
|||
currentState = State.OPENPGP_ENCRYPT_OK;
|
||||
}
|
||||
|
||||
public void setSigningKeyId(long signingKeyId) {
|
||||
this.signingKeyId = signingKeyId;
|
||||
}
|
||||
|
||||
public void setSelfEncryptKeyId(long selfEncryptKeyId) {
|
||||
this.selfEncryptKeyId = selfEncryptKeyId;
|
||||
}
|
||||
|
||||
public void setCryptoMode(CryptoMode cryptoMode) {
|
||||
this.cryptoMode = cryptoMode;
|
||||
public void setCryptoStatus(ComposeCryptoStatus cryptoStatus) {
|
||||
this.cryptoStatus = cryptoStatus;
|
||||
}
|
||||
|
||||
/* TODO re-add PGP/INLINE
|
||||
|
|
|
@ -49,10 +49,12 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
|
|||
ContactsContract.Contacts.SORT_KEY_PRIMARY;
|
||||
|
||||
private static final int INDEX_EMAIL_ADDRESS = 0;
|
||||
private static final int INDEX_EMAIL_STATUS = 1;
|
||||
private static final int INDEX_EMAIL_KEY_REFERENCE = 1;
|
||||
private static final int INDEX_EMAIL_STATUS = 2;
|
||||
|
||||
private static final String[] PROJECTION_CRYPTO_STATUS = {
|
||||
"email_address",
|
||||
"email_key_reference",
|
||||
"email_status"
|
||||
};
|
||||
|
||||
|
@ -265,6 +267,7 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
|
|||
|
||||
while (cursor.moveToNext()) {
|
||||
String email = cursor.getString(INDEX_EMAIL_ADDRESS);
|
||||
String keyRef = cursor.getString(INDEX_EMAIL_KEY_REFERENCE);
|
||||
int status = cursor.getInt(INDEX_EMAIL_STATUS);
|
||||
|
||||
for (Address address : Address.parseUnencoded(email)) {
|
||||
|
@ -273,11 +276,15 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
|
|||
Recipient recipient = recipientMap.get(emailAddress);
|
||||
switch (status) {
|
||||
case CRYPTO_PROVIDER_STATUS_UNTRUSTED: {
|
||||
recipient.setCryptoStatus(RecipientCryptoStatus.AVAILABLE_UNTRUSTED);
|
||||
if (recipient.getCryptoStatus() == RecipientCryptoStatus.UNAVAILABLE) {
|
||||
recipient.setCryptoStatus(RecipientCryptoStatus.AVAILABLE_UNTRUSTED, keyRef);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CRYPTO_PROVIDER_STATUS_TRUSTED: {
|
||||
recipient.setCryptoStatus(RecipientCryptoStatus.AVAILABLE_TRUSTED);
|
||||
if (recipient.getCryptoStatus() != RecipientCryptoStatus.AVAILABLE_TRUSTED) {
|
||||
recipient.setCryptoStatus(RecipientCryptoStatus.AVAILABLE_TRUSTED, keyRef);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -294,7 +301,7 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
|
|||
|
||||
private void initializeCryptoStatusForAllRecipients(Map<String, Recipient> recipientMap) {
|
||||
for (Recipient recipient : recipientMap.values()) {
|
||||
recipient.setCryptoStatus(RecipientCryptoStatus.UNAVAILABLE);
|
||||
recipient.setCryptoStatus(RecipientCryptoStatus.UNAVAILABLE, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -247,18 +247,26 @@ public class RecipientMvpView implements OnFocusChangeListener, OnClickListener
|
|||
}
|
||||
|
||||
public void showToUncompletedError() {
|
||||
toView.setError(toView.getContext().getString(R.string.message_compose_error_incomplete_recipient));
|
||||
toView.setError(toView.getContext().getString(R.string.compose_error_incomplete_recipient));
|
||||
}
|
||||
|
||||
public void showCcUncompletedError() {
|
||||
ccView.setError(ccView.getContext().getString(R.string.message_compose_error_incomplete_recipient));
|
||||
ccView.setError(ccView.getContext().getString(R.string.compose_error_incomplete_recipient));
|
||||
}
|
||||
|
||||
public void showBccUncompletedError() {
|
||||
bccView.setError(bccView.getContext().getString(R.string.message_compose_error_incomplete_recipient));
|
||||
bccView.setError(bccView.getContext().getString(R.string.compose_error_incomplete_recipient));
|
||||
}
|
||||
|
||||
public void showCryptoStatus(final CryptoStatusType childToDisplay) {
|
||||
public void showMissingSignKeyError() {
|
||||
Toast.makeText(activity, R.string.compose_error_no_signing_key, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
public void showPrivateAndIncompleteError() {
|
||||
Toast.makeText(activity, R.string.compose_error_private_missing_keys, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
public void showCryptoStatus(final CryptoStatusDisplayType childToDisplay) {
|
||||
if (cryptoStatusView.getVisibility() == View.VISIBLE) {
|
||||
switchCryptoStatus(childToDisplay);
|
||||
return;
|
||||
|
@ -274,7 +282,7 @@ public class RecipientMvpView implements OnFocusChangeListener, OnClickListener
|
|||
}).start();
|
||||
}
|
||||
|
||||
private void switchCryptoStatus(CryptoStatusType cryptoStatus) {
|
||||
private void switchCryptoStatus(CryptoStatusDisplayType cryptoStatus) {
|
||||
int childToDisplay = cryptoStatus.childToDisplay;
|
||||
if (cryptoStatusView.getDisplayedChild() != childToDisplay) {
|
||||
cryptoStatusView.setDisplayedChild(childToDisplay);
|
||||
|
@ -331,18 +339,20 @@ public class RecipientMvpView implements OnFocusChangeListener, OnClickListener
|
|||
dialog.show(activity.getFragmentManager(), "crypto_settings");
|
||||
}
|
||||
|
||||
|
||||
public enum CryptoStatusType {
|
||||
public enum CryptoStatusDisplayType {
|
||||
DISABLED(VIEW_INDEX_CRYPTO_STATUS_DISABLED),
|
||||
SIGN_ONLY(VIEW_INDEX_CRYPTO_STATUS_SIGN_ONLY),
|
||||
OPPORTUNISTIC_NOKEY(VIEW_INDEX_CRYPTO_STATUS_NO_KEY),
|
||||
OPPORTUNISTIC_UNTRUSTED(VIEW_INDEX_CRYPTO_STATUS_UNTRUSTED),
|
||||
OPPORTUNISTIC_TRUSTED(VIEW_INDEX_CRYPTO_STATUS_TRUSTED);
|
||||
OPPORTUNISTIC_TRUSTED(VIEW_INDEX_CRYPTO_STATUS_TRUSTED),
|
||||
PRIVATE_NOKEY(VIEW_INDEX_CRYPTO_STATUS_NO_KEY),
|
||||
PRIVATE_UNTRUSTED(VIEW_INDEX_CRYPTO_STATUS_UNTRUSTED),
|
||||
PRIVATE_TRUSTED(VIEW_INDEX_CRYPTO_STATUS_TRUSTED);
|
||||
|
||||
|
||||
final int childToDisplay;
|
||||
|
||||
CryptoStatusType(int childToDisplay) {
|
||||
CryptoStatusDisplayType(int childToDisplay) {
|
||||
this.childToDisplay = childToDisplay;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import android.view.Menu;
|
|||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.Identity;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.RecipientMvpView.CryptoStatusType;
|
||||
import com.fsck.k9.helper.Contacts;
|
||||
import com.fsck.k9.helper.MailTo;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
|
@ -27,12 +26,11 @@ import com.fsck.k9.mail.Message.RecipientType;
|
|||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.view.RecipientSelectView.Recipient;
|
||||
import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus;
|
||||
|
||||
|
||||
public class RecipientPresenter {
|
||||
private static final String STATE_KEY_CC_SHOWN = "com.fsck.k9.activity.MessageCompose.ccShown";
|
||||
private static final String STATE_KEY_BCC_SHOWN = "com.fsck.k9.activity.MessageCompose.bccShown";
|
||||
private static final String STATE_KEY_CC_SHOWN = "state:ccShown";
|
||||
private static final String STATE_KEY_BCC_SHOWN = "state:bccShown";
|
||||
|
||||
private static final int CONTACT_PICKER_TO = 1;
|
||||
private static final int CONTACT_PICKER_CC = 2;
|
||||
|
@ -44,8 +42,9 @@ public class RecipientPresenter {
|
|||
private Account account;
|
||||
private String cryptoProvider;
|
||||
private RecipientType lastFocusedType = RecipientType.TO;
|
||||
private CryptoMode currentCryptoMode = CryptoMode.OPPORTUNISTIC;
|
||||
private Boolean hasContactPicker;
|
||||
private CryptoMode currentCryptoMode = CryptoMode.OPPORTUNISTIC;
|
||||
private ComposeCryptoStatus currentCryptoStatus;
|
||||
|
||||
|
||||
public RecipientPresenter(Context context, RecipientMvpView recipientMvpView, Account account) {
|
||||
|
@ -54,6 +53,7 @@ public class RecipientPresenter {
|
|||
|
||||
recipientMvpView.setPresenter(this);
|
||||
onSwitchAccount(account);
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
public List<Address> getToAddresses() {
|
||||
|
@ -299,88 +299,75 @@ public class RecipientPresenter {
|
|||
recipientMvpView.setRecipientExpanderVisibility(notBothAreVisible);
|
||||
}
|
||||
|
||||
public void updateCryptoDisplayStatus() {
|
||||
public void updateCryptoStatus() {
|
||||
List<Recipient> recipients = getAllRecipients();
|
||||
|
||||
long accountCryptoKey = account.getCryptoKey();
|
||||
|
||||
currentCryptoStatus = ComposeCryptoStatus.createFromRecipients(currentCryptoMode, getAllRecipients(),
|
||||
accountCryptoKey == Account.NO_OPENPGP_KEY ? null : accountCryptoKey);
|
||||
|
||||
boolean hasNoCryptoProviderOrRecipients = cryptoProvider == null || recipients.isEmpty();
|
||||
if (hasNoCryptoProviderOrRecipients) {
|
||||
recipientMvpView.hideCryptoStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentCryptoMode == CryptoMode.SIGN_ONLY) {
|
||||
recipientMvpView.showCryptoStatus(CryptoStatusType.SIGN_ONLY);
|
||||
return;
|
||||
}
|
||||
recipientMvpView.showCryptoStatus(currentCryptoStatus.getCryptoStatusDisplayType());
|
||||
}
|
||||
|
||||
if (currentCryptoMode == CryptoMode.DISABLE) {
|
||||
recipientMvpView.showCryptoStatus(CryptoStatusType.DISABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean allKeysAvailable = true;
|
||||
boolean allKeysVerified = true;
|
||||
for (Recipient recipient : recipients) {
|
||||
RecipientCryptoStatus cryptoStatus = recipient.getCryptoStatus();
|
||||
if (!cryptoStatus.isAvailable()) {
|
||||
allKeysAvailable = false;
|
||||
} else if (cryptoStatus == RecipientCryptoStatus.AVAILABLE_UNTRUSTED) {
|
||||
allKeysVerified = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (allKeysAvailable && allKeysVerified) {
|
||||
recipientMvpView.showCryptoStatus(CryptoStatusType.OPPORTUNISTIC_TRUSTED);
|
||||
} else if (allKeysAvailable) {
|
||||
recipientMvpView.showCryptoStatus(CryptoStatusType.OPPORTUNISTIC_UNTRUSTED);
|
||||
} else {
|
||||
recipientMvpView.showCryptoStatus(CryptoStatusType.OPPORTUNISTIC_NOKEY);
|
||||
}
|
||||
public ComposeCryptoStatus getCurrentCryptoStatus() {
|
||||
return currentCryptoStatus;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onToTokenAdded(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onToTokenRemoved(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onToTokenChanged(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onCcTokenAdded(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onCcTokenRemoved(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onCcTokenChanged(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onBccTokenAdded(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onBccTokenRemoved(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onBccTokenChanged(Recipient recipient) {
|
||||
updateCryptoDisplayStatus();
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
public void onCryptoModeChanged(CryptoMode cryptoMode) {
|
||||
currentCryptoMode = cryptoMode;
|
||||
updateCryptoStatus();
|
||||
}
|
||||
|
||||
private void addRecipientsFromAddresses(final RecipientType recipientType, final Address... addresses) {
|
||||
|
@ -479,11 +466,6 @@ public class RecipientPresenter {
|
|||
recipientMvpView.showCryptoDialog(currentCryptoMode);
|
||||
}
|
||||
|
||||
public void onCryptoModeChanged(CryptoMode cryptoMode) {
|
||||
currentCryptoMode = cryptoMode;
|
||||
updateCryptoDisplayStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the device actually have a Contacts application suitable for
|
||||
* picking a contact. As hard as it is to believe, some vendors ship
|
||||
|
@ -503,10 +485,23 @@ public class RecipientPresenter {
|
|||
return hasContactPicker;
|
||||
}
|
||||
|
||||
public CryptoMode getCurrentCryptoMode() {
|
||||
return currentCryptoMode;
|
||||
}
|
||||
public boolean canSendOrError(boolean isDraft) {
|
||||
if (isDraft) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ComposeCryptoStatus cryptoStatus = getCurrentCryptoStatus();
|
||||
if (cryptoStatus.isMissingSignKey()) {
|
||||
recipientMvpView.showMissingSignKeyError();
|
||||
return false;
|
||||
}
|
||||
if (cryptoStatus.isPrivateAndIncomplete()) {
|
||||
recipientMvpView.showPrivateAndIncompleteError();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum CryptoMode {
|
||||
PRIVATE,
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.app.PendingIntent;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.Account.QuoteStyle;
|
||||
|
|
|
@ -16,7 +16,7 @@ public class SimpleMessageBuilder extends MessageBuilder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void buildMessageInternal() {
|
||||
protected void buildMessageInternal() {
|
||||
try {
|
||||
MimeMessage message = build();
|
||||
queueMessageBuildSuccess(message);
|
||||
|
|
|
@ -477,6 +477,8 @@ public class RecipientSelectView extends TokenCompleteTextView<Recipient> implem
|
|||
|
||||
@NonNull
|
||||
private RecipientCryptoStatus cryptoStatus;
|
||||
@Nullable // null iff cryptoStatus == UNDEFINED || cryptoStatus == UNAVAILABLE
|
||||
private String keyReference;
|
||||
|
||||
|
||||
public Recipient(@NonNull Address address) {
|
||||
|
@ -530,8 +532,9 @@ public class RecipientSelectView extends TokenCompleteTextView<Recipient> implem
|
|||
return cryptoStatus;
|
||||
}
|
||||
|
||||
public void setCryptoStatus(@NonNull RecipientCryptoStatus cryptoStatus) {
|
||||
public void setCryptoStatus(@NonNull RecipientCryptoStatus cryptoStatus, @Nullable String keyReference) {
|
||||
this.cryptoStatus = cryptoStatus;
|
||||
this.keyReference = keyReference;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -570,5 +573,10 @@ public class RecipientSelectView extends TokenCompleteTextView<Recipient> implem
|
|||
photoThumbnailUri = Uri.parse(uriString);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getKeyReference() {
|
||||
return keyReference;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -263,7 +263,7 @@ Please submit bug reports, contribute new features and ask questions at
|
|||
<string name="message_compose_reply_header_fmt"><xliff:g id="sender">%s</xliff:g> wrote:</string>
|
||||
<string name="message_compose_reply_header_fmt_with_date">On <xliff:g id="sent_date">%1$s</xliff:g>, <xliff:g id="sender">%2$s</xliff:g> wrote:</string>
|
||||
<string name="message_compose_error_no_recipients">You must add at least one recipient.</string>
|
||||
<string name="message_compose_error_incomplete_recipient">Recipient field contains incomplete input!</string>
|
||||
<string name="compose_error_incomplete_recipient">Recipient field contains incomplete input!</string>
|
||||
<string name="error_contact_address_not_found">No email address could be found for this contact.</string>
|
||||
<string name="message_compose_attachments_skipped_toast">Some attachments cannot be forwarded because they have not been downloaded.</string>
|
||||
<string name="message_compose_show_quoted_text_action">Quote message</string>
|
||||
|
@ -1165,5 +1165,10 @@ Please submit bug reports, contribute new features and ask questions at
|
|||
<string name="address_type_other">Other</string>
|
||||
<string name="address_type_mobile">Mobile</string>
|
||||
<string name="warn_search_disabled">Search in message bodies is disabled in this development version!</string>
|
||||
<string name="compose_confirm_draft_unencrypted">Draft will be saved unencrypted! Repeat to confirm.</string>
|
||||
<string name="compose_error_no_draft_folder">No Drafts folder configured for this account!</string>
|
||||
<string name="compose_not_saving_unencrypted">Draft NOT saved without encryption!</string>
|
||||
<string name="compose_error_no_signing_key">Signing requested, but no key selected!</string>
|
||||
<string name="compose_error_private_missing_keys">Private mode enabled, but cannot encrypt to all recipients!</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -223,6 +223,7 @@ public class OpenPgpApi {
|
|||
public static final String EXTRA_USER_IDS = "user_ids";
|
||||
public static final String EXTRA_KEY_IDS = "key_ids";
|
||||
public static final String EXTRA_SIGN_KEY_ID = "sign_key_id";
|
||||
public static final String EXTRA_KEY_REFERENCES = "key_refs";
|
||||
// optional extras:
|
||||
public static final String EXTRA_PASSPHRASE = "passphrase";
|
||||
public static final String EXTRA_ORIGINAL_FILENAME = "original_filename";
|
||||
|
|
Loading…
Reference in a new issue