compose: incorporate feedback

This commit is contained in:
Vincent Breitmoser 2015-12-20 01:36:28 +01:00
parent a3fe6389eb
commit f12da27098
15 changed files with 493 additions and 531 deletions

View file

@ -1283,7 +1283,8 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
new String[] {"Android-PullToRefresh", "https://github.com/chrisbanes/Android-PullToRefresh"},
new String[] {"ckChangeLog", "https://github.com/cketti/ckChangeLog"},
new String[] {"HoloColorPicker", "https://github.com/LarsWerkman/HoloColorPicker"},
new String[] {"Glide", "https://github.com/bumptech/glide"}
new String[] {"Glide", "https://github.com/bumptech/glide"},
new String[] {"TokenAutoComplete", "https://github.com/splitwise/TokenAutoComplete/"},
};
private void onAbout() {

View file

@ -14,16 +14,16 @@ import android.view.View;
import com.fsck.k9.R;
import com.fsck.k9.activity.RecipientPresenter.CryptoMode;
import com.fsck.k9.view.CryptoStatusSelector;
import com.fsck.k9.view.CryptoStatusSelector.CryptoStatusSelectedListener;
import com.fsck.k9.view.CryptoModeSelector;
import com.fsck.k9.view.CryptoModeSelector.CryptoStatusSelectedListener;
import com.fsck.k9.view.LinearViewAnimator;
public class CryptoSettingsDialog extends DialogFragment implements CryptoStatusSelectedListener {
public static final String ARG_CURRENT_MODE = "current_override";
private static final String ARG_CURRENT_MODE = "current_override";
private CryptoStatusSelector cryptoStatusSelector;
private CryptoModeSelector cryptoModeSelector;
private LinearViewAnimator cryptoStatusText;
private CryptoMode currentMode;
@ -42,10 +42,10 @@ public class CryptoSettingsDialog extends DialogFragment implements CryptoStatus
public Dialog onCreateDialog(Bundle savedInstanceState) {
@SuppressLint("InflateParams")
View view = LayoutInflater.from(getActivity()).inflate(R.layout.crypto_settings_dialog, null);
cryptoStatusSelector = (CryptoStatusSelector) view.findViewById(R.id.crypto_status_selector);
cryptoModeSelector = (CryptoModeSelector) view.findViewById(R.id.crypto_status_selector);
cryptoStatusText = (LinearViewAnimator) view.findViewById(R.id.crypto_status_text);
cryptoStatusSelector.setCryptoStatusListener(this);
cryptoModeSelector.setCryptoStatusListener(this);
Bundle arguments = savedInstanceState != null ? savedInstanceState : getArguments();
currentMode = CryptoMode.valueOf(arguments.getString(ARG_CURRENT_MODE));
@ -53,7 +53,7 @@ public class CryptoSettingsDialog extends DialogFragment implements CryptoStatus
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view);
builder.setPositiveButton("Proceed", new OnClickListener() {
builder.setPositiveButton(R.string.crypto_settings_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
@ -66,15 +66,15 @@ public class CryptoSettingsDialog extends DialogFragment implements CryptoStatus
void updateView(boolean animate) {
switch (currentMode) {
case DISABLE:
cryptoStatusSelector.setCryptoStatus(0);
cryptoModeSelector.setCryptoStatus(0);
cryptoStatusText.setDisplayedChild(0, animate);
break;
case SIGN_ONLY:
cryptoStatusSelector.setCryptoStatus(1);
cryptoModeSelector.setCryptoStatus(1);
cryptoStatusText.setDisplayedChild(1, animate);
break;
case OPPORTUNISTIC:
cryptoStatusSelector.setCryptoStatus(2);
cryptoModeSelector.setCryptoStatus(2);
cryptoStatusText.setDisplayedChild(2, animate);
break;
}
@ -103,8 +103,16 @@ public class CryptoSettingsDialog extends DialogFragment implements CryptoStatus
// is this supposed to happen?
return;
}
boolean activityIsCryptoModeChangedListener = activity instanceof OnCryptoModeChangedListener;
if (!activityIsCryptoModeChangedListener) {
throw new AssertionError("This dialog must be called by an OnCryptoModeChangedListener!");
}
((MessageCompose) activity).onCryptoModeChanged(currentMode);
((OnCryptoModeChangedListener) activity).onCryptoModeChanged(currentMode);
}
interface OnCryptoModeChangedListener {
void onCryptoModeChanged(CryptoMode cryptoMode);
}
}

View file

@ -72,6 +72,7 @@ import com.fsck.k9.Identity;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.activity.CryptoSettingsDialog.OnCryptoModeChangedListener;
import com.fsck.k9.activity.RecipientPresenter.CryptoMode;
import com.fsck.k9.activity.loader.AttachmentContentLoader;
import com.fsck.k9.activity.loader.AttachmentInfoLoader;
@ -116,7 +117,7 @@ import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
public class MessageCompose extends K9Activity implements OnClickListener,
CancelListener, OnFocusChangeListener {
CancelListener, OnFocusChangeListener, OnCryptoModeChangedListener {
private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1;
private static final int DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED = 2;
@ -174,6 +175,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
private static final int ACTIVITY_REQUEST_PICK_ATTACHMENT = 1;
private static final int REQUEST_CODE_SIGN_ENCRYPT = 12;
private static final int REQUEST_MASK_RECIPIENT_PRESENTER = (1<<8);
/**
* Regular expression to remove the first localized "Re:" prefix in subjects.
@ -246,8 +248,9 @@ public class MessageCompose extends K9Activity implements OnClickListener,
}
}
public void onCryptoModeChanged(CryptoMode type) {
recipientPresenter.onCryptoModeChanged(type);
@Override
public void onCryptoModeChanged(CryptoMode cryptoMode) {
recipientPresenter.onCryptoModeChanged(cryptoMode);
}
enum Action {
@ -550,13 +553,6 @@ public class MessageCompose extends K9Activity implements OnClickListener,
mChooseIdentityButton = (TextView) findViewById(R.id.identity);
mChooseIdentityButton.setOnClickListener(this);
/*
if (mAccount.getIdentities().size() == 1 &&
Preferences.getPreferences(this).getAvailableAccounts().size() == 1) {
mChooseIdentityButton.setVisibility(View.GONE);
}
*/
RecipientView recipientView = new RecipientView(this);
recipientPresenter = new RecipientPresenter(this, recipientView, mAccount);
@ -698,7 +694,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
if (mAction != Action.EDIT_DRAFT) {
String alwaysBccString = mAccount.getAlwaysBcc();
if (!TextUtils.isEmpty(alwaysBccString)) {
recipientPresenter.addBccAddresses(new Address(alwaysBccString, ""));
recipientPresenter.addBccAddresses(Address.parse(alwaysBccString));
}
}
}
@ -713,7 +709,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
mMessageContentView.requestFocus();
} else {
// Explicitly set focus to "To:" input field (see issue 2998)
recipientView.toFieldRequestFocus();
recipientView.requestFocusOnToFied();
}
if (mAction == Action.FORWARD) {
@ -766,7 +762,6 @@ public class MessageCompose extends K9Activity implements OnClickListener,
} else {
mEncryptLayout.setVisibility(View.GONE);
}
mEncryptLayout.setVisibility(View.GONE);
// Set font size of input controls
int fontSize = mFontSizes.getMessageComposeInput();
@ -1539,22 +1534,11 @@ public class MessageCompose extends K9Activity implements OnClickListener,
}
}
private static final int REQUEST_MASK_RECIPIENT_PRESENTER = (1<<8);
public void showContactPicker(int requestCode) {
requestCode |= REQUEST_MASK_RECIPIENT_PRESENTER;
startActivityForResult(mContacts.contactPickerIntent(), requestCode);
}
private Boolean hasContactPicker;
public boolean hasContactPicker() {
if (hasContactPicker == null) {
hasContactPicker = !(getPackageManager().queryIntentActivities(mContacts.contactPickerIntent(), 0).isEmpty());
}
return hasContactPicker;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// if a CryptoSystem activity is returning, then mPreventDraftSaving was set to true

View file

@ -24,9 +24,8 @@ import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus;
public class RecipientAdapter extends BaseAdapter implements Filterable {
List<Recipient> recipients;
boolean showCryptoStatus = true;
Context context;
private final Context context;
private List<Recipient> recipients;
public RecipientAdapter(Context context) {
super();
@ -83,12 +82,6 @@ public class RecipientAdapter extends BaseAdapter implements Filterable {
setContactPhotoOrPlaceholder(context, holder.photo, recipient);
if (!showCryptoStatus) {
holder.cryptoStatus.setVisibility(View.GONE);
return;
}
holder.cryptoStatus.setVisibility(View.VISIBLE);
Integer cryptoStatusRes = null, cryptoStatusColor = null;
RecipientCryptoStatus cryptoStatus = recipient.getCryptoStatus();
switch (cryptoStatus) {
@ -122,6 +115,7 @@ public class RecipientAdapter extends BaseAdapter implements Filterable {
public static void setContactPhotoOrPlaceholder(Context context, ImageView imageView, Recipient recipient) {
imageView.setImageDrawable(null);
// TODO don't use two different mechanisms for loading!
if (recipient.photoThumbnailUri != null) {
Glide.with(context).load(recipient.photoThumbnailUri).into(imageView);
} else {
@ -150,10 +144,11 @@ public class RecipientAdapter extends BaseAdapter implements Filterable {
};
}
static class RecipientTokenHolder {
TextView name, email;
ImageView photo;
ImageView cryptoStatus;
private static class RecipientTokenHolder {
public final TextView name;
public final TextView email;
public final ImageView photo;
public final ImageView cryptoStatus;
public RecipientTokenHolder(View view) {
name = (TextView) view.findViewById(R.id.text1);

View file

@ -5,14 +5,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.annotation.TargetApi;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build.VERSION_CODES;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.provider.ContactsContract;
import com.fsck.k9.mail.Address;
@ -20,7 +16,6 @@ import com.fsck.k9.view.RecipientSelectView.Recipient;
import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus;
@TargetApi(VERSION_CODES.JELLY_BEAN) // TODO get rid of this, affects cancellation behavior!
public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
/** Indexes of the fields in the projection. This must match the order in
@ -57,7 +52,6 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
private List<Recipient> cachedRecipients;
private ForceLoadContentObserver observerContact, observerKey;
private CancellationSignal mCancellationSignal;
public RecipientLoader(Context context, String cryptoProvider, String query) {
super(context);
@ -85,43 +79,28 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
@Override
public List<Recipient> loadInBackground() {
ArrayList<Recipient> recipients = new ArrayList<Recipient>();
HashMap<String,Recipient> recipientMap = new HashMap<String, Recipient>();
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
mCancellationSignal = new CancellationSignal();
if (addresses != null) {
fillContactDataFromAddresses(addresses, recipients, recipientMap);
} else if (contactUri != null) {
fillContactDataFromContactUri(contactUri, recipients, recipientMap);
} else if (query != null) {
fillContactDataFromQuery(query, recipients, recipientMap);
} else {
throw new IllegalStateException("loader must be initialized with query or list of addresses!");
}
try {
ArrayList<Recipient> recipients = new ArrayList<Recipient>();
HashMap<String,Recipient> recipientMap = new HashMap<String, Recipient>();
if (addresses != null) {
fillContactDataFromAddresses(addresses, recipients, recipientMap);
} else if (contactUri != null) {
fillContactDataFromContactUri(contactUri, recipients, recipientMap);
} else if (query != null) {
fillContactDataFromQuery(query, recipients, recipientMap);
} else {
throw new IllegalStateException("loader must be initialized with query or list of addresses!");
}
if (recipients.isEmpty()) {
return recipients;
}
if (cryptoProvider != null) {
fillCryptoStatusData(recipientMap);
}
if (recipients.isEmpty()) {
return recipients;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
if (cryptoProvider != null) {
fillCryptoStatusData(recipientMap);
}
return recipients;
}
private void fillContactDataFromAddresses(Address[] addresses, ArrayList<Recipient> recipients,
@ -160,8 +139,7 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
Uri queryUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Email.CONTENT_FILTER_URI,
Uri.encode(query));
Cursor cursor = getContext().getContentResolver().query(
queryUri, PROJECTION, null, null, SORT_ORDER, mCancellationSignal);
Cursor cursor = getContext().getContentResolver().query(queryUri, PROJECTION, null, null, SORT_ORDER);
if (cursor == null) {
return;
@ -178,9 +156,7 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
private void fillContactDataFromCursor(Cursor cursor, ArrayList<Recipient> recipients,
HashMap<String, Recipient> recipientMap) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
while (cursor.moveToNext()) {
String name = cursor.getString(INDEX_NAME);
String email = cursor.getString(INDEX_EMAIL);
@ -189,7 +165,6 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
// already exists? just skip then
if (recipientMap.containsKey(email)) {
// TODO merge? do something else? what do we do?
cursor.moveToNext();
continue;
}
@ -200,8 +175,6 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
recipientMap.put(email, recipient);
recipients.add(recipient);
cursor.moveToNext();
}
cursor.close();
@ -213,7 +186,7 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
Uri queryUri = Uri.parse("content://" + cryptoProvider + ".provider.exported/email_status");
Cursor cursor = getContext().getContentResolver().query(
queryUri, PROJECTION_CRYPTO_STATUS, null, recipientAddresses, null, mCancellationSignal);
queryUri, PROJECTION_CRYPTO_STATUS, null, recipientAddresses, null);
// fill all values with "unavailable", even if the query fails
for (Recipient recipient : recipientMap.values()) {
@ -224,8 +197,7 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
return;
}
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
while (cursor.moveToNext()) {
String email = cursor.getString(0);
int status = cursor.getInt(1);
@ -242,8 +214,6 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
}
}
}
cursor.moveToNext();
}
cursor.close();
@ -286,15 +256,4 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> {
}
}
@Override
public void cancelLoadInBackground() {
super.cancelLoadInBackground();
synchronized (this) {
if (mCancellationSignal != null) {
mCancellationSignal.cancel();
}
}
}
}

View file

@ -17,6 +17,7 @@ import com.fsck.k9.Identity;
import com.fsck.k9.R;
import com.fsck.k9.activity.MessageCompose.CaseInsensitiveParamWrapper;
import com.fsck.k9.activity.RecipientView.CryptoStatusType;
import com.fsck.k9.helper.Contacts;
import com.fsck.k9.view.RecipientSelectView.Recipient;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address;
@ -228,7 +229,7 @@ public class RecipientPresenter {
}
public void onPrepareOptionsMenu(Menu menu) {
boolean noContactPickerAvailable = !recipientView.hasContactPicker();
boolean noContactPickerAvailable = !hasContactPicker();
if (noContactPickerAvailable) {
menu.findItem(R.id.add_from_contacts).setVisible(false);
}
@ -371,6 +372,7 @@ public class RecipientPresenter {
Recipient[] recipientArray = result.toArray(new Recipient[result.size()]);
recipientView.addRecipients(recipientType, recipientArray);
stopLoading();
abandon();
}
}.startLoading();
}
@ -387,6 +389,7 @@ public class RecipientPresenter {
Recipient recipient = result.get(0);
recipientView.addRecipients(recipientType, recipient);
stopLoading();
abandon();
}
}.startLoading();
}
@ -455,8 +458,8 @@ public class RecipientPresenter {
recipientView.showCryptoDialog(currentCryptoMode);
}
public void onCryptoModeChanged(CryptoMode type) {
currentCryptoMode = type;
public void onCryptoModeChanged(CryptoMode cryptoMode) {
currentCryptoMode = cryptoMode;
updateCryptoDisplayStatus();
}
@ -464,4 +467,21 @@ public class RecipientPresenter {
OPPORTUNISTIC, DISABLE, SIGN_ONLY
}
private Boolean hasContactPicker;
/**
* Does the device actually have a Contacts application suitable for
* picking a contact. As hard as it is to believe, some vendors ship
* without it.
*
* @return True, if the device supports picking contacts. False, otherwise.
*/
public boolean hasContactPicker() {
if (hasContactPicker == null) {
Contacts contacts = Contacts.getInstance(context);
hasContactPicker = !(context.getPackageManager().queryIntentActivities(
contacts.contactPickerIntent(), 0).isEmpty());
}
return hasContactPicker;
}
}

View file

@ -119,7 +119,7 @@ public class RecipientView implements OnFocusChangeListener, OnClickListener {
bccView.setCryptoProvider(openPgpProvider);
}
public void toFieldRequestFocus() {
public void requestFocusOnToFied() {
toView.requestFocus();
}
@ -238,17 +238,6 @@ public class RecipientView implements OnFocusChangeListener, OnClickListener {
}
}
/**
* Does the device actually have a Contacts application suitable for
* picking a contact. As hard as it is to believe, some vendors ship
* without it.
*
* @return True, if the device supports picking contacts. False, otherwise.
*/
public boolean hasContactPicker() {
return activity.hasContactPicker();
}
public void showContactPicker(int requestCode) {
// this is an extra indirection to keep the view here clear
activity.showContactPicker(requestCode);

View file

@ -338,26 +338,11 @@ public class MessageBuilder {
return this;
}
public MessageBuilder setTo(Address[] to) {
this.to = to;
return this;
}
public MessageBuilder setCc(Address[] cc) {
this.cc = cc;
return this;
}
public MessageBuilder setCc(List<Address> cc) {
this.cc = cc.toArray(new Address[cc.size()]);
return this;
}
public MessageBuilder setBcc(Address[] bcc) {
this.bcc = bcc;
return this;
}
public MessageBuilder setBcc(List<Address> bcc) {
this.bcc = bcc.toArray(new Address[bcc.size()]);
return this;

View file

@ -14,10 +14,16 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import com.fsck.k9.R;
public class CryptoStatusSelector extends FrameLayout implements OnSeekBarChangeListener {
public class CryptoModeSelector extends FrameLayout implements OnSeekBarChangeListener {
public static final int CROSSFADE_THRESH_3_LOW = 125;
public static final int CROSSFADE_THRESH_2_LOW = 50;
public static final int CROSSFADE_THRESH_2_HIGH = 150;
public static final float CROSSFADE_DIVISOR_2 = 50.0f;
public static final float CROSSFADE_DIVISOR_3 = 75.0f;
private SeekBar seekbar;
private ImageView icon1, icon2, icon3;
private ImageView modeIcon2;
private ImageView modeIcon3;
private ObjectAnimator currentSeekbarAnim;
@ -26,12 +32,12 @@ public class CryptoStatusSelector extends FrameLayout implements OnSeekBarChange
private CryptoStatusSelectedListener cryptoStatusListener;
private static final ArgbEvaluator ARGB_EVALUATOR = new ArgbEvaluator();
public CryptoStatusSelector(Context context, AttributeSet attrs) {
public CryptoModeSelector(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CryptoStatusSelector(Context context, AttributeSet attrs, int defStyleAttr) {
public CryptoModeSelector(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@ -39,14 +45,15 @@ public class CryptoStatusSelector extends FrameLayout implements OnSeekBarChange
public void init() {
inflate(getContext(), R.layout.crypto_settings_slider, this);
seekbar = (SeekBar) findViewById(R.id.seek_bar);
icon1 = (ImageView) findViewById(R.id.icon_1);
icon2 = (ImageView) findViewById(R.id.icon_2);
icon3 = (ImageView) findViewById(R.id.icon_3);
modeIcon2 = (ImageView) findViewById(R.id.icon_2);
modeIcon3 = (ImageView) findViewById(R.id.icon_3);
seekbar.setOnSeekBarChangeListener(this);
onProgressChanged(seekbar, seekbar.getProgress(), false);
icon1.setColorFilter(getResources().getColor(R.color.openpgp_grey), PorterDuff.Mode.SRC_ATOP);
// this one is set static for now, since it is static grey and not crossfaded
ImageView modeIcon1 = (ImageView) findViewById(R.id.icon_1);
modeIcon1.setColorFilter(getResources().getColor(R.color.openpgp_grey), PorterDuff.Mode.SRC_ATOP);
}
@Override
@ -54,8 +61,8 @@ public class CryptoStatusSelector extends FrameLayout implements OnSeekBarChange
int grey = getResources().getColor(R.color.openpgp_grey);
float crossfadeValue2, crossfadeValue3;
if (progress > 50 && progress < 150) {
crossfadeValue2 = (progress-50) / 50.0f;
if (progress > CROSSFADE_THRESH_2_LOW && progress < CROSSFADE_THRESH_2_HIGH) {
crossfadeValue2 = (progress -CROSSFADE_THRESH_2_LOW) / CROSSFADE_DIVISOR_2;
if (crossfadeValue2 > 1.0f) {
crossfadeValue2 = 2.0f -crossfadeValue2;
}
@ -63,8 +70,8 @@ public class CryptoStatusSelector extends FrameLayout implements OnSeekBarChange
crossfadeValue2 = 0.0f;
}
if (progress > 125) {
crossfadeValue3 = (progress-125) / 75.0f;
if (progress > CROSSFADE_THRESH_3_LOW) {
crossfadeValue3 = (progress -CROSSFADE_THRESH_3_LOW) / CROSSFADE_DIVISOR_3;
} else {
crossfadeValue3 = 0.0f;
}
@ -72,10 +79,10 @@ public class CryptoStatusSelector extends FrameLayout implements OnSeekBarChange
int crossfadedColor;
crossfadedColor = crossfadeColor(grey, getResources().getColor(R.color.openpgp_red), crossfadeValue2);
icon2.setColorFilter(crossfadedColor, PorterDuff.Mode.SRC_ATOP);
modeIcon2.setColorFilter(crossfadedColor, PorterDuff.Mode.SRC_ATOP);
crossfadedColor = crossfadeColor(grey, getResources().getColor(R.color.openpgp_green), crossfadeValue3);
icon3.setColorFilter(crossfadedColor, PorterDuff.Mode.SRC_ATOP);
modeIcon3.setColorFilter(crossfadedColor, PorterDuff.Mode.SRC_ATOP);
}
@Override

View file

@ -16,11 +16,11 @@ import com.fsck.k9.R;
*/
public class LinearViewAnimator extends ViewAnimator {
Animation mUpInAnimation;
Animation mUpOutAnimation;
private Animation upInAnimation;
private Animation upOutAnimation;
Animation mDownInAnimation;
Animation mDownOutAnimation;
private Animation downInAnimation;
private Animation downOutAnimation;
public LinearViewAnimator(Context context) {
super(context);
@ -31,6 +31,7 @@ public class LinearViewAnimator extends ViewAnimator {
init(context, attrs);
}
@SuppressWarnings("UnusedParameters")
public LinearViewAnimator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs);
init(context, attrs);
@ -67,7 +68,7 @@ public class LinearViewAnimator extends ViewAnimator {
}
public void setUpOutAnimation(Animation animation) {
mUpOutAnimation = animation;
upOutAnimation = animation;
}
public void setUpInAnimation(Context context, int resourceID) {
@ -75,7 +76,7 @@ public class LinearViewAnimator extends ViewAnimator {
}
public void setUpInAnimation(Animation animation) {
mUpInAnimation = animation;
upInAnimation = animation;
}
public void setDownOutAnimation(Context context, int resourceID) {
@ -83,7 +84,7 @@ public class LinearViewAnimator extends ViewAnimator {
}
public void setDownOutAnimation(Animation animation) {
mDownOutAnimation = animation;
downOutAnimation = animation;
}
public void setDownInAnimation(Context context, int resourceID) {
@ -91,7 +92,7 @@ public class LinearViewAnimator extends ViewAnimator {
}
public void setDownInAnimation(Animation animation) {
mDownInAnimation = animation;
downInAnimation = animation;
}
@Override
@ -108,11 +109,11 @@ public class LinearViewAnimator extends ViewAnimator {
setInAnimation(null);
setOutAnimation(null);
} else if (displayedChild < whichChild) {
setInAnimation(mDownInAnimation);
setOutAnimation(mDownOutAnimation);
setInAnimation(downInAnimation);
setOutAnimation(downOutAnimation);
} else {
setInAnimation(mUpInAnimation);
setOutAnimation(mUpOutAnimation);
setInAnimation(upInAnimation);
setOutAnimation(upOutAnimation);
}
super.setDisplayedChild(whichChild);
}

View file

@ -93,14 +93,23 @@ public class RecipientSelectView extends TokenCompleteTextView<Recipient> implem
View cryptoStatusGreen = view.findViewById(R.id.contact_crypto_status_green);
boolean hasCryptoProvider = cryptoProvider != null;
// display unavailable status only if a crypto provider is even available
cryptoStatusRed.setVisibility(
hasCryptoProvider && recipient.cryptoStatus == RecipientCryptoStatus.UNAVAILABLE
? View.VISIBLE : View.GONE);
cryptoStatusOrange.setVisibility(
recipient.cryptoStatus == RecipientCryptoStatus.AVAILABLE_UNTRUSTED ? View.VISIBLE : View.GONE);
cryptoStatusGreen.setVisibility(
recipient.cryptoStatus == RecipientCryptoStatus.AVAILABLE_TRUSTED ? View.VISIBLE : View.GONE);
if (!hasCryptoProvider) {
cryptoStatusRed.setVisibility(View.GONE);
cryptoStatusOrange.setVisibility(View.GONE);
cryptoStatusGreen.setVisibility(View.GONE);
} else if (recipient.cryptoStatus == RecipientCryptoStatus.UNAVAILABLE) {
cryptoStatusRed.setVisibility(View.VISIBLE);
cryptoStatusOrange.setVisibility(View.GONE);
cryptoStatusGreen.setVisibility(View.GONE);
} else if (recipient.cryptoStatus == RecipientCryptoStatus.AVAILABLE_UNTRUSTED) {
cryptoStatusRed.setVisibility(View.GONE);
cryptoStatusOrange.setVisibility(View.VISIBLE);
cryptoStatusGreen.setVisibility(View.GONE);
} else if (recipient.cryptoStatus == RecipientCryptoStatus.AVAILABLE_TRUSTED) {
cryptoStatusRed.setVisibility(View.GONE);
cryptoStatusOrange.setVisibility(View.GONE);
cryptoStatusGreen.setVisibility(View.VISIBLE);
}
return view;
}

View file

@ -43,7 +43,7 @@
</com.fsck.k9.view.LinearViewAnimator>
<com.fsck.k9.view.CryptoStatusSelector
<com.fsck.k9.view.CryptoModeSelector
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/crypto_status_selector" />

View file

@ -17,368 +17,48 @@
android:layout_height="wrap_content"
android:orientation="vertical" >
<include layout="@layout/message_compose_recipients" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:orientation="vertical">
android:layout_width="match_parent"
android:layout_marginLeft="12dip"
android:layout_marginRight="12dip"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="50dp">
<RelativeLayout
android:layout_width="match_parent"
<EditText
android:id="@+id/subject"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dip"
android:layout_marginRight="12dip"
android:minHeight="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:id="@+id/from_label"
android:minWidth="50dp"
android:text="From"
style="@style/ComposeTextLabel" />
<TextView
android:id="@+id/identity"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/from_label"
android:layout_toEndOf="@id/from_label"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:singleLine="true"
android:paddingTop="10dp"
android:paddingBottom="10dp"
tools:text="Address"
style="@style/ComposeEditText"
/>
<ViewAnimator
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:id="@+id/crypto_status"
android:visibility="gone"
android:inAnimation="@anim/fade_in"
android:outAnimation="@anim/fade_out"
tools:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_open"
android:tint="@color/openpgp_red"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_error"
android:tint="@color/openpgp_orange"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_closed"
android:tint="@color/openpgp_green"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_open"
android:tint="@color/openpgp_red"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_disabled"
android:tint="@color/openpgp_grey"
/>
</ViewAnimator>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<RelativeLayout
android:id="@+id/to_wrapper"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:minHeight="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:id="@+id/to_label"
android:labelFor="@+id/to"
android:minWidth="50dp"
android:text="To"
style="@style/ComposeTextLabel" />
<com.fsck.k9.view.RecipientSelectView
android:id="@+id/to"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/to_label"
android:layout_toEndOf="@id/to_label"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:inputType="textEmailAddress|textMultiLine"
android:imeOptions="actionNext"
android:textAppearance="?android:attr/textAppearanceMedium"
android:background="@android:color/transparent"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:dropDownWidth="wrap_content"
android:dropDownAnchor="@id/to_wrapper"
tools:text="Recipient"
style="@style/ComposeEditText"
/>
<ViewAnimator
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:id="@+id/recipient_expander_container"
android:inAnimation="@anim/fade_in"
android:outAnimation="@anim/fade_out"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_action_expand_light"
android:id="@+id/recipient_expander"
/>
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</ViewAnimator>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/cc_wrapper"
android:visibility="gone"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginLeft="12dip"
android:layout_marginRight="12dip"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="50dp"
android:labelFor="@+id/cc"
android:text="Cc"
style="@style/ComposeTextLabel" />
<com.fsck.k9.view.RecipientSelectView
android:id="@+id/cc"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="textEmailAddress|textMultiLine"
android:imeOptions="actionNext"
android:background="@android:color/transparent"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:dropDownWidth="wrap_content"
android:dropDownAnchor="@id/cc_wrapper"
style="@style/ComposeEditText"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:visibility="gone"
android:id="@+id/cc_divider"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/bcc_wrapper"
android:visibility="gone"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginLeft="12dip"
android:layout_marginRight="12dip"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="50dp"
android:labelFor="@+id/bcc"
android:text="Bcc"
style="@style/ComposeTextLabel" />
<com.fsck.k9.view.RecipientSelectView
android:id="@+id/bcc"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="textEmailAddress|textMultiLine"
android:imeOptions="actionNext"
android:background="@android:color/transparent"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:dropDownWidth="wrap_content"
android:dropDownAnchor="@id/bcc_wrapper"
style="@style/ComposeEditText"
android:hint="@string/message_compose_subject_hint"
android:inputType="textEmailSubject|textAutoCorrect|textCapSentences|textImeMultiLine"
android:imeOptions="actionNext"
android:singleLine="true"
android:background="@android:color/transparent"
android:paddingTop="10dp"
android:paddingBottom="10dp"
style="@style/ComposeEditTextLarge"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:visibility="gone"
android:id="@+id/bcc_divider"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/layout_encrypt"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:paddingLeft="6dip"
android:paddingRight="6dip">
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<CheckBox
android:text="@string/btn_crypto_sign"
android:id="@+id/cb_crypto_signature"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:orientation="vertical"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingRight="2dip">
<TextView
android:id="@+id/userId"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/userIdRest"
android:textSize="10sp"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
<CheckBox
android:text="@string/btn_encrypt"
android:id="@+id/cb_encrypt"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginLeft="12dip"
android:layout_marginRight="12dip"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="50dp">
<EditText
android:id="@+id/subject"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/message_compose_subject_hint"
android:inputType="textEmailSubject|textAutoCorrect|textCapSentences|textImeMultiLine"
android:imeOptions="actionNext"
android:singleLine="true"
android:background="@android:color/transparent"
android:paddingTop="10dp"
android:paddingBottom="10dp"
style="@style/ComposeEditTextLarge"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<!--
Empty container for storing attachments. We'll stick
instances of message_compose_attachment.xml in here.
-->
<LinearLayout
android:id="@+id/attachments"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<!--
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@drawable/divider_horizontal_email" />
-->
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<!--
Empty container for storing attachments. We'll stick
instances of message_compose_attachment.xml in here.
-->
<LinearLayout
android:id="@+id/attachments"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<!-- We have to use "wrap_content" (not "0dip") for "layout_height", otherwise the
EditText won't properly grow in height while the user is typing the message -->
<view

View file

@ -0,0 +1,319 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:orientation="vertical"
tools:showIn="@layout/message_compose">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dip"
android:layout_marginRight="12dip"
android:minHeight="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:id="@+id/from_label"
android:minWidth="50dp"
android:text="@string/recipient_from"
style="@style/ComposeTextLabel" />
<TextView
android:id="@+id/identity"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/from_label"
android:layout_toEndOf="@id/from_label"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:singleLine="true"
android:paddingTop="10dp"
android:paddingBottom="10dp"
tools:text="Address"
style="@style/ComposeEditText"
/>
<ViewAnimator
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:id="@+id/crypto_status"
android:visibility="gone"
android:inAnimation="@anim/fade_in"
android:outAnimation="@anim/fade_out"
tools:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_open"
android:tint="@color/openpgp_red"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_error"
android:tint="@color/openpgp_orange"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_closed"
android:tint="@color/openpgp_green"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_open"
android:tint="@color/openpgp_red"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/status_lock_disabled"
android:tint="@color/openpgp_grey"
/>
</ViewAnimator>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<RelativeLayout
android:id="@+id/to_wrapper"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:minHeight="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:id="@+id/to_label"
android:labelFor="@+id/to"
android:minWidth="50dp"
android:text="@string/recipient_to"
style="@style/ComposeTextLabel" />
<com.fsck.k9.view.RecipientSelectView
android:id="@+id/to"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/to_label"
android:layout_toEndOf="@id/to_label"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:inputType="textEmailAddress|textMultiLine"
android:imeOptions="actionNext"
android:textAppearance="?android:attr/textAppearanceMedium"
android:background="@android:color/transparent"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:dropDownWidth="wrap_content"
android:dropDownAnchor="@id/to_wrapper"
tools:text="Recipient"
style="@style/ComposeEditText"
/>
<ViewAnimator
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:id="@+id/recipient_expander_container"
android:inAnimation="@anim/fade_in"
android:outAnimation="@anim/fade_out"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_action_expand_light"
android:id="@+id/recipient_expander"
/>
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</ViewAnimator>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/cc_wrapper"
android:visibility="gone"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginLeft="12dip"
android:layout_marginRight="12dip"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="50dp"
android:labelFor="@+id/cc"
android:text="@string/recipient_cc"
style="@style/ComposeTextLabel" />
<com.fsck.k9.view.RecipientSelectView
android:id="@+id/cc"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="textEmailAddress|textMultiLine"
android:imeOptions="actionNext"
android:background="@android:color/transparent"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:dropDownWidth="wrap_content"
android:dropDownAnchor="@id/cc_wrapper"
style="@style/ComposeEditText"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:visibility="gone"
android:id="@+id/cc_divider"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/bcc_wrapper"
android:visibility="gone"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginLeft="12dip"
android:layout_marginRight="12dip"
android:gravity="center_vertical"
android:orientation="horizontal"
android:minHeight="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="50dp"
android:labelFor="@+id/bcc"
android:text="@string/recipient_bcc"
style="@style/ComposeTextLabel" />
<com.fsck.k9.view.RecipientSelectView
android:id="@+id/bcc"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:inputType="textEmailAddress|textMultiLine"
android:imeOptions="actionNext"
android:background="@android:color/transparent"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:dropDownWidth="wrap_content"
android:dropDownAnchor="@id/bcc_wrapper"
style="@style/ComposeEditText"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:visibility="gone"
android:id="@+id/bcc_divider"
android:background="?android:attr/listDivider" />
<LinearLayout
android:id="@+id/layout_encrypt"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="6dip"
android:paddingRight="6dip"
>
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<CheckBox
android:text="@string/btn_crypto_sign"
android:id="@+id/cb_crypto_signature"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:orientation="vertical"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingRight="2dip">
<TextView
android:id="@+id/userId"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/userIdRest"
android:textSize="10sp"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
<CheckBox
android:text="@string/btn_encrypt"
android:id="@+id/cb_encrypt"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>

View file

@ -162,7 +162,6 @@ Please submit bug reports, contribute new features and ask questions at
<string name="message_view_theme_action_light">Switch to light theme</string>
<string name="mark_as_unread_action">Mark unread</string>
<string name="add_cc_bcc_action">Add Cc/Bcc</string>
<string name="read_receipt">Read receipt</string>
<string name="read_receipt_enabled">Will request read receipt</string>
<string name="read_receipt_disabled">Will not request read receipt</string>
@ -1159,4 +1158,10 @@ Please submit bug reports, contribute new features and ask questions at
<!-- Note: This references message_view_download_remainder -->
<string name="crypto_download_complete_message_to_decrypt">Click \'Download complete message\' to allow decryption.</string>
<string name="add_from_contacts">Add from Contacts</string>
<string name="crypto_settings_ok">Proceed</string>
<string name="recipient_cc">Cc</string>
<string name="recipient_bcc">Bcc</string>
<string name="recipient_to">To</string>
<string name="recipient_from">From</string>
</resources>