compose: centralize pgp crypto status into immutable ComposeCryptoStatus object

This commit is contained in:
Vincent Breitmoser 2015-12-26 22:44:16 +01:00
parent bbc52b3265
commit 441e35f1cd
11 changed files with 259 additions and 131 deletions

View file

@ -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;
}
}

View file

@ -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() {

View file

@ -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

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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,

View file

@ -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;

View file

@ -16,7 +16,7 @@ public class SimpleMessageBuilder extends MessageBuilder {
}
@Override
public void buildMessageInternal() {
protected void buildMessageInternal() {
try {
MimeMessage message = build();
queueMessageBuildSuccess(message);

View file

@ -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;
}
}
}

View file

@ -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>

View file

@ -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";