Loading k9mail/src/main/java/com/fsck/k9/activity/AlternateRecipientAdapter.java +36 −23 Original line number Diff line number Diff line Loading @@ -23,12 +23,16 @@ import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus; public class AlternateRecipientAdapter extends BaseAdapter { private static final int NUMBER_OF_FIXED_LIST_ITEMS = 2; private static final int POSITION_HEADER_VIEW = 0; private static final int POSITION_CURRENT_ADDRESS = 1; private final Context context; private final AlternateRecipientListener listener; private List<Recipient> recipients; private Recipient currentRecipient; private AlternateRecipientListener listener; public AlternateRecipientAdapter(Context context, AlternateRecipientListener listener) { super(); Loading @@ -48,20 +52,20 @@ public class AlternateRecipientAdapter extends BaseAdapter { @Override public int getCount() { // number of alternate addresses, +1 for the header, +1 for the current address if (recipients == null) { return 2; return NUMBER_OF_FIXED_LIST_ITEMS; } return recipients.size() +2; return recipients.size() + NUMBER_OF_FIXED_LIST_ITEMS; } @Override public Recipient getItem(int position) { // position 0 is the recipient we display alternates for, substitute it if (position == 0 || position == 1) { if (position == POSITION_HEADER_VIEW || position == POSITION_CURRENT_ADDRESS) { return currentRecipient; } return recipients == null ? null : recipients.get(position -2); return recipients == null ? null : getRecipientFromPosition(position); } @Override Loading @@ -69,13 +73,19 @@ public class AlternateRecipientAdapter extends BaseAdapter { return position; } private Recipient getRecipientFromPosition(int position) { return recipients.get(position - NUMBER_OF_FIXED_LIST_ITEMS); } @Override public View getView(int position, View view, ViewGroup parent) { if (view == null) { view = newView(parent); } Recipient recipient = getItem(position); if (position == 0) { if (position == POSITION_HEADER_VIEW) { bindHeaderView(view, recipient); } else { bindItemView(view, recipient); Loading @@ -86,15 +96,16 @@ public class AlternateRecipientAdapter extends BaseAdapter { public View newView(ViewGroup parent) { View view = LayoutInflater.from(context).inflate(R.layout.recipient_alternate_item, parent, false); RecipientTokenHolder holder = new RecipientTokenHolder(view); view.setTag(holder); return view; } @Override public boolean isEnabled(int position) { // the header isn't clickable, all other elements are return position > 0; return position != POSITION_HEADER_VIEW; } public void bindHeaderView(View view, Recipient recipient) { Loading Loading @@ -141,19 +152,22 @@ public class AlternateRecipientAdapter extends BaseAdapter { Integer cryptoStatusRes = null, cryptoStatusColor = null; RecipientCryptoStatus cryptoStatus = recipient.getCryptoStatus(); switch (cryptoStatus) { case AVAILABLE_TRUSTED: case AVAILABLE_TRUSTED: { cryptoStatusRes = R.drawable.status_lock_closed; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_green); break; case AVAILABLE_UNTRUSTED: } case AVAILABLE_UNTRUSTED: { cryptoStatusRes = R.drawable.status_lock_error; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_orange); break; case UNAVAILABLE: } case UNAVAILABLE: { cryptoStatusRes = R.drawable.status_lock_open; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_red); break; } } if (cryptoStatusRes != null) { // we could do this easier with setImageTintList, but that's API level 21 Loading @@ -169,16 +183,16 @@ public class AlternateRecipientAdapter extends BaseAdapter { } static class RecipientTokenHolder { View layoutHeader, layoutItem; TextView headerName; QuickContactBadge headerPhoto; View headerRemove; private static class RecipientTokenHolder { public final View layoutHeader, layoutItem; public final TextView headerName; public final QuickContactBadge headerPhoto; public final View headerRemove; public final TextView itemAddress; public final TextView itemAddressLabel; public final ImageView itemCryptoStatus; TextView itemAddress; TextView itemAddressLabel; ImageView itemCryptoStatus; public RecipientTokenHolder(View view) { layoutHeader = view.findViewById(R.id.alternate_container_header); Loading @@ -203,5 +217,4 @@ public class AlternateRecipientAdapter extends BaseAdapter { void onRecipientRemove(Recipient currentRecipient); void onRecipientChange(Recipient currentRecipient, Recipient alternateRecipient); } } k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java +1 −1 Original line number Diff line number Diff line Loading @@ -709,7 +709,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, mMessageContentView.requestFocus(); } else { // Explicitly set focus to "To:" input field (see issue 2998) recipientMvpView.requestFocusOnToFied(); recipientMvpView.requestFocusOnToField(); } if (mAction == Action.FORWARD) { Loading k9mail/src/main/java/com/fsck/k9/activity/RecipientAdapter.java +15 −6 Original line number Diff line number Diff line Loading @@ -23,10 +23,10 @@ import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus; public class RecipientAdapter extends BaseAdapter implements Filterable { private final Context context; private List<Recipient> recipients; public RecipientAdapter(Context context) { super(); this.context = context; Loading Loading @@ -54,10 +54,10 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { @Override public View getView(int position, View view, ViewGroup parent) { if (view == null) { view = newView(parent); } Recipient recipient = getItem(position); bindView(view, recipient); Loading @@ -66,8 +66,10 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { public View newView(ViewGroup parent) { View view = LayoutInflater.from(context).inflate(R.layout.recipient_dropdown_item, parent, false); RecipientTokenHolder holder = new RecipientTokenHolder(view); view.setTag(holder); return view; } Loading @@ -84,19 +86,22 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { Integer cryptoStatusRes = null, cryptoStatusColor = null; RecipientCryptoStatus cryptoStatus = recipient.getCryptoStatus(); switch (cryptoStatus) { case AVAILABLE_TRUSTED: case AVAILABLE_TRUSTED: { cryptoStatusRes = R.drawable.status_lock_closed; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_green); break; case AVAILABLE_UNTRUSTED: } case AVAILABLE_UNTRUSTED: { cryptoStatusRes = R.drawable.status_lock_error; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_orange); break; case UNAVAILABLE: } case UNAVAILABLE: { cryptoStatusRes = R.drawable.status_lock_open; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_red); break; } } if (cryptoStatusRes != null) { // we could do this easier with setImageTintList, but that's API level 21 Loading @@ -113,6 +118,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); Loading @@ -129,9 +135,11 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { if (recipients == null) { return null; } FilterResults result = new FilterResults(); result.values = recipients; result.count = recipients.size(); return result; } Loading @@ -142,12 +150,14 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { }; } 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); email = (TextView) view.findViewById(R.id.text2); Loading @@ -155,5 +165,4 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { cryptoStatus = (ImageView) view.findViewById(R.id.contact_crypto_status); } } } k9mail/src/main/java/com/fsck/k9/activity/RecipientLoader.java +84 −56 Original line number Diff line number Diff line Loading @@ -4,8 +4,10 @@ package com.fsck.k9.activity; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.content.AsyncTaskLoader; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; Loading @@ -18,18 +20,18 @@ import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus; public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { /** Indexes of the fields in the projection. This must match the order in * {@link #PROJECTION}. */ protected static final int INDEX_NAME = 1; protected static final int INDEX_LOOKUP_KEY = 2; protected static final int INDEX_EMAIL = 3; protected static final int INDEX_EMAIL_TYPE = 4; protected static final int INDEX_EMAIL_CUSTOM_LABEL = 5; protected static final int INDEX_CONTACT_ID = 6; protected static final int INDEX_PHOTO_URI = 7; protected static final String[] PROJECTION = { /* * Indexes of the fields in the projection. This must match the order in {@link #PROJECTION}. */ private static final int INDEX_NAME = 1; private static final int INDEX_LOOKUP_KEY = 2; private static final int INDEX_EMAIL = 3; private static final int INDEX_EMAIL_TYPE = 4; private static final int INDEX_EMAIL_CUSTOM_LABEL = 5; private static final int INDEX_CONTACT_ID = 6; private static final int INDEX_PHOTO_URI = 7; private static final String[] PROJECTION = { ContactsContract.CommonDataKinds.Email._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.LOOKUP_KEY, Loading @@ -40,26 +42,32 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { ContactsContract.Contacts.PHOTO_THUMBNAIL_URI }; protected static final String[] PROJECTION_CRYPTO_STATUS = { private static final String SORT_ORDER = "" + ContactsContract.CommonDataKinds.Email.TIMES_CONTACTED + " DESC, " + ContactsContract.Contacts.DISPLAY_NAME + ", " + ContactsContract.CommonDataKinds.Email._ID; private static final int INDEX_EMAIL_ADDRESS = 0; private static final int INDEX_EMAIL_STATUS = 1; private static final String[] PROJECTION_CRYPTO_STATUS = { "email_address", "email_status" }; protected static final String SORT_ORDER = ContactsContract.CommonDataKinds.Email.TIMES_CONTACTED + " DESC, " + ContactsContract.Contacts.DISPLAY_NAME + ", " + ContactsContract.CommonDataKinds.Email._ID; private static final int CRYPTO_PROVIDER_STATUS_UNTRUSTED = 1; private static final int CRYPTO_PROVIDER_STATUS_TRUSTED = 2; private final String query; private final Address[] addresses; private final Uri contactUri; private final String cryptoProvider; private List<Recipient> cachedRecipients; private ForceLoadContentObserver observerContact, observerKey; public RecipientLoader(Context context, String cryptoProvider, String query) { super(context); this.query = query; Loading @@ -86,8 +94,8 @@ 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>(); List<Recipient> recipients = new ArrayList<Recipient>(); Map<String, Recipient> recipientMap = new HashMap<String, Recipient>(); if (addresses != null) { fillContactDataFromAddresses(addresses, recipients, recipientMap); Loading @@ -110,8 +118,9 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { return recipients; } private void fillContactDataFromAddresses(Address[] addresses, ArrayList<Recipient> recipients, HashMap<String, Recipient> recipientMap) { private void fillContactDataFromAddresses(Address[] addresses, List<Recipient> recipients, Map<String, Recipient> recipientMap) { for (Address address : addresses) { // TODO actually query contacts - not sure if this is possible in a single query tho :( Recipient recipient = new Recipient(address); Loading @@ -120,10 +129,10 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { } } private void fillContactDataFromContactUri( Uri contactUri, ArrayList<Recipient> recipients, HashMap<String, Recipient> recipientMap) { // Get the contact id from the Uri String contactIdStr = contactUri.getLastPathSegment(); private void fillContactDataFromContactUri(Uri contactUri, List<Recipient> recipients, Map<String, Recipient> recipientMap) { String contactIdStr = getContactIdFromUri(contactUri); Cursor cursor = getContext().getContentResolver().query( ContactsContract.CommonDataKinds.Email.CONTENT_URI, Loading @@ -137,11 +146,18 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { fillContactDataFromCursor(cursor, recipients, recipientMap); } private void fillContactDataFromQuery( String query, ArrayList<Recipient> recipients, HashMap<String, Recipient> recipientMap) { private String getContactIdFromUri(Uri contactUri) { return contactUri.getLastPathSegment(); } private void fillContactDataFromQuery(String query, List<Recipient> recipients, Map<String, Recipient> recipientMap) { ContentResolver contentResolver = getContext().getContentResolver(); Uri queryUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Email.CONTENT_FILTER_URI, Uri.encode(query)); Cursor cursor = getContext().getContentResolver().query(queryUri, PROJECTION, null, null, SORT_ORDER); Cursor cursor = contentResolver.query(queryUri, PROJECTION, null, null, SORT_ORDER); if (cursor == null) { return; Loading @@ -151,12 +167,13 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { if (observerContact != null) { observerContact = new ForceLoadContentObserver(); getContext().getContentResolver().registerContentObserver(queryUri, false, observerContact); contentResolver.registerContentObserver(queryUri, false, observerContact); } } private void fillContactDataFromCursor(Cursor cursor, ArrayList<Recipient> recipients, HashMap<String, Recipient> recipientMap) { private void fillContactDataFromCursor(Cursor cursor, List<Recipient> recipients, Map<String, Recipient> recipientMap) { while (cursor.moveToNext()) { String name = cursor.getString(INDEX_NAME); String email = cursor.getString(INDEX_EMAIL); Loading @@ -172,70 +189,75 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { int addressType = cursor.getInt(INDEX_EMAIL_TYPE); String addressLabel = null; switch (addressType) { case ContactsContract.CommonDataKinds.Email.TYPE_HOME: case ContactsContract.CommonDataKinds.Email.TYPE_HOME: { addressLabel = getContext().getString(R.string.address_type_home); break; case ContactsContract.CommonDataKinds.Email.TYPE_WORK: } case ContactsContract.CommonDataKinds.Email.TYPE_WORK: { addressLabel = getContext().getString(R.string.address_type_work); break; case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: } case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: { addressLabel = getContext().getString(R.string.address_type_other); break; case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE: } case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE: { // mobile isn't listed as an option contacts app, but it has a constant so we better support it addressLabel = getContext().getString(R.string.address_type_mobile); break; case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM: } case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM: { addressLabel = cursor.getString(INDEX_EMAIL_CUSTOM_LABEL); break; } } Uri photoUri = cursor.isNull(INDEX_PHOTO_URI) ? null : Uri.parse(cursor.getString(INDEX_PHOTO_URI)); Uri photoUri = cursor.isNull(INDEX_PHOTO_URI) ? null : Uri.parse(cursor.getString(INDEX_PHOTO_URI)); Recipient recipient = new Recipient(name, email, addressLabel, contactId, lookupKey); recipient.photoThumbnailUri = photoUri; recipientMap.put(email, recipient); recipients.add(recipient); } cursor.close(); } private void fillCryptoStatusData(HashMap<String, Recipient> recipientMap) { ArrayList<String> recipientArrayList = new ArrayList<String>(recipientMap.keySet()); String[] recipientAddresses = recipientArrayList.toArray(new String[ recipientArrayList.size() ]); private void fillCryptoStatusData(Map<String, Recipient> recipientMap) { List<String> recipientList = new ArrayList<String>(recipientMap.keySet()); String[] recipientAddresses = recipientList.toArray(new String[recipientList.size()]); Uri queryUri = Uri.parse("content://" + cryptoProvider + ".provider.exported/email_status"); Cursor cursor = getContext().getContentResolver().query( queryUri, PROJECTION_CRYPTO_STATUS, null, recipientAddresses, null); Cursor cursor = getContext().getContentResolver().query(queryUri, PROJECTION_CRYPTO_STATUS, null, recipientAddresses, null); // fill all values with "unavailable", even if the query fails for (Recipient recipient : recipientMap.values()) { recipient.setCryptoStatus(RecipientCryptoStatus.UNAVAILABLE); } initializeCryptoStatusForAllRecipients(recipientMap); if (cursor == null) { return; } while (cursor.moveToNext()) { String email = cursor.getString(0); int status = cursor.getInt(1); String email = cursor.getString(INDEX_EMAIL_ADDRESS); int status = cursor.getInt(INDEX_EMAIL_STATUS); for (Address address : Address.parseUnencoded(email)) { if (recipientMap.containsKey(address.getAddress())) { Recipient recipient = recipientMap.get(address.getAddress()); String emailAddress = address.getAddress(); if (recipientMap.containsKey(emailAddress)) { Recipient recipient = recipientMap.get(emailAddress); switch (status) { case 1: case CRYPTO_PROVIDER_STATUS_UNTRUSTED: { recipient.setCryptoStatus(RecipientCryptoStatus.AVAILABLE_UNTRUSTED); break; case 2: } case CRYPTO_PROVIDER_STATUS_TRUSTED: { recipient.setCryptoStatus(RecipientCryptoStatus.AVAILABLE_TRUSTED); break; } } } } } cursor.close(); if (observerKey != null) { Loading @@ -244,6 +266,12 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { } } private void initializeCryptoStatusForAllRecipients(Map<String, Recipient> recipientMap) { for (Recipient recipient : recipientMap.values()) { recipient.setCryptoStatus(RecipientCryptoStatus.UNAVAILABLE); } } @Override public void deliverResult(List<Recipient> data) { cachedRecipients = data; Loading @@ -268,6 +296,7 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { @Override protected void onAbandon() { super.onAbandon(); if (observerKey != null) { getContext().getContentResolver().unregisterContentObserver(observerKey); } Loading @@ -275,5 +304,4 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { getContext().getContentResolver().unregisterContentObserver(observerContact); } } } k9mail/src/main/java/com/fsck/k9/activity/RecipientMvpView.java +56 −32 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
k9mail/src/main/java/com/fsck/k9/activity/AlternateRecipientAdapter.java +36 −23 Original line number Diff line number Diff line Loading @@ -23,12 +23,16 @@ import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus; public class AlternateRecipientAdapter extends BaseAdapter { private static final int NUMBER_OF_FIXED_LIST_ITEMS = 2; private static final int POSITION_HEADER_VIEW = 0; private static final int POSITION_CURRENT_ADDRESS = 1; private final Context context; private final AlternateRecipientListener listener; private List<Recipient> recipients; private Recipient currentRecipient; private AlternateRecipientListener listener; public AlternateRecipientAdapter(Context context, AlternateRecipientListener listener) { super(); Loading @@ -48,20 +52,20 @@ public class AlternateRecipientAdapter extends BaseAdapter { @Override public int getCount() { // number of alternate addresses, +1 for the header, +1 for the current address if (recipients == null) { return 2; return NUMBER_OF_FIXED_LIST_ITEMS; } return recipients.size() +2; return recipients.size() + NUMBER_OF_FIXED_LIST_ITEMS; } @Override public Recipient getItem(int position) { // position 0 is the recipient we display alternates for, substitute it if (position == 0 || position == 1) { if (position == POSITION_HEADER_VIEW || position == POSITION_CURRENT_ADDRESS) { return currentRecipient; } return recipients == null ? null : recipients.get(position -2); return recipients == null ? null : getRecipientFromPosition(position); } @Override Loading @@ -69,13 +73,19 @@ public class AlternateRecipientAdapter extends BaseAdapter { return position; } private Recipient getRecipientFromPosition(int position) { return recipients.get(position - NUMBER_OF_FIXED_LIST_ITEMS); } @Override public View getView(int position, View view, ViewGroup parent) { if (view == null) { view = newView(parent); } Recipient recipient = getItem(position); if (position == 0) { if (position == POSITION_HEADER_VIEW) { bindHeaderView(view, recipient); } else { bindItemView(view, recipient); Loading @@ -86,15 +96,16 @@ public class AlternateRecipientAdapter extends BaseAdapter { public View newView(ViewGroup parent) { View view = LayoutInflater.from(context).inflate(R.layout.recipient_alternate_item, parent, false); RecipientTokenHolder holder = new RecipientTokenHolder(view); view.setTag(holder); return view; } @Override public boolean isEnabled(int position) { // the header isn't clickable, all other elements are return position > 0; return position != POSITION_HEADER_VIEW; } public void bindHeaderView(View view, Recipient recipient) { Loading Loading @@ -141,19 +152,22 @@ public class AlternateRecipientAdapter extends BaseAdapter { Integer cryptoStatusRes = null, cryptoStatusColor = null; RecipientCryptoStatus cryptoStatus = recipient.getCryptoStatus(); switch (cryptoStatus) { case AVAILABLE_TRUSTED: case AVAILABLE_TRUSTED: { cryptoStatusRes = R.drawable.status_lock_closed; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_green); break; case AVAILABLE_UNTRUSTED: } case AVAILABLE_UNTRUSTED: { cryptoStatusRes = R.drawable.status_lock_error; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_orange); break; case UNAVAILABLE: } case UNAVAILABLE: { cryptoStatusRes = R.drawable.status_lock_open; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_red); break; } } if (cryptoStatusRes != null) { // we could do this easier with setImageTintList, but that's API level 21 Loading @@ -169,16 +183,16 @@ public class AlternateRecipientAdapter extends BaseAdapter { } static class RecipientTokenHolder { View layoutHeader, layoutItem; TextView headerName; QuickContactBadge headerPhoto; View headerRemove; private static class RecipientTokenHolder { public final View layoutHeader, layoutItem; public final TextView headerName; public final QuickContactBadge headerPhoto; public final View headerRemove; public final TextView itemAddress; public final TextView itemAddressLabel; public final ImageView itemCryptoStatus; TextView itemAddress; TextView itemAddressLabel; ImageView itemCryptoStatus; public RecipientTokenHolder(View view) { layoutHeader = view.findViewById(R.id.alternate_container_header); Loading @@ -203,5 +217,4 @@ public class AlternateRecipientAdapter extends BaseAdapter { void onRecipientRemove(Recipient currentRecipient); void onRecipientChange(Recipient currentRecipient, Recipient alternateRecipient); } }
k9mail/src/main/java/com/fsck/k9/activity/MessageCompose.java +1 −1 Original line number Diff line number Diff line Loading @@ -709,7 +709,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, mMessageContentView.requestFocus(); } else { // Explicitly set focus to "To:" input field (see issue 2998) recipientMvpView.requestFocusOnToFied(); recipientMvpView.requestFocusOnToField(); } if (mAction == Action.FORWARD) { Loading
k9mail/src/main/java/com/fsck/k9/activity/RecipientAdapter.java +15 −6 Original line number Diff line number Diff line Loading @@ -23,10 +23,10 @@ import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus; public class RecipientAdapter extends BaseAdapter implements Filterable { private final Context context; private List<Recipient> recipients; public RecipientAdapter(Context context) { super(); this.context = context; Loading Loading @@ -54,10 +54,10 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { @Override public View getView(int position, View view, ViewGroup parent) { if (view == null) { view = newView(parent); } Recipient recipient = getItem(position); bindView(view, recipient); Loading @@ -66,8 +66,10 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { public View newView(ViewGroup parent) { View view = LayoutInflater.from(context).inflate(R.layout.recipient_dropdown_item, parent, false); RecipientTokenHolder holder = new RecipientTokenHolder(view); view.setTag(holder); return view; } Loading @@ -84,19 +86,22 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { Integer cryptoStatusRes = null, cryptoStatusColor = null; RecipientCryptoStatus cryptoStatus = recipient.getCryptoStatus(); switch (cryptoStatus) { case AVAILABLE_TRUSTED: case AVAILABLE_TRUSTED: { cryptoStatusRes = R.drawable.status_lock_closed; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_green); break; case AVAILABLE_UNTRUSTED: } case AVAILABLE_UNTRUSTED: { cryptoStatusRes = R.drawable.status_lock_error; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_orange); break; case UNAVAILABLE: } case UNAVAILABLE: { cryptoStatusRes = R.drawable.status_lock_open; cryptoStatusColor = context.getResources().getColor(R.color.openpgp_red); break; } } if (cryptoStatusRes != null) { // we could do this easier with setImageTintList, but that's API level 21 Loading @@ -113,6 +118,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); Loading @@ -129,9 +135,11 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { if (recipients == null) { return null; } FilterResults result = new FilterResults(); result.values = recipients; result.count = recipients.size(); return result; } Loading @@ -142,12 +150,14 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { }; } 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); email = (TextView) view.findViewById(R.id.text2); Loading @@ -155,5 +165,4 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { cryptoStatus = (ImageView) view.findViewById(R.id.contact_crypto_status); } } }
k9mail/src/main/java/com/fsck/k9/activity/RecipientLoader.java +84 −56 Original line number Diff line number Diff line Loading @@ -4,8 +4,10 @@ package com.fsck.k9.activity; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.content.AsyncTaskLoader; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; Loading @@ -18,18 +20,18 @@ import com.fsck.k9.view.RecipientSelectView.RecipientCryptoStatus; public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { /** Indexes of the fields in the projection. This must match the order in * {@link #PROJECTION}. */ protected static final int INDEX_NAME = 1; protected static final int INDEX_LOOKUP_KEY = 2; protected static final int INDEX_EMAIL = 3; protected static final int INDEX_EMAIL_TYPE = 4; protected static final int INDEX_EMAIL_CUSTOM_LABEL = 5; protected static final int INDEX_CONTACT_ID = 6; protected static final int INDEX_PHOTO_URI = 7; protected static final String[] PROJECTION = { /* * Indexes of the fields in the projection. This must match the order in {@link #PROJECTION}. */ private static final int INDEX_NAME = 1; private static final int INDEX_LOOKUP_KEY = 2; private static final int INDEX_EMAIL = 3; private static final int INDEX_EMAIL_TYPE = 4; private static final int INDEX_EMAIL_CUSTOM_LABEL = 5; private static final int INDEX_CONTACT_ID = 6; private static final int INDEX_PHOTO_URI = 7; private static final String[] PROJECTION = { ContactsContract.CommonDataKinds.Email._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.LOOKUP_KEY, Loading @@ -40,26 +42,32 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { ContactsContract.Contacts.PHOTO_THUMBNAIL_URI }; protected static final String[] PROJECTION_CRYPTO_STATUS = { private static final String SORT_ORDER = "" + ContactsContract.CommonDataKinds.Email.TIMES_CONTACTED + " DESC, " + ContactsContract.Contacts.DISPLAY_NAME + ", " + ContactsContract.CommonDataKinds.Email._ID; private static final int INDEX_EMAIL_ADDRESS = 0; private static final int INDEX_EMAIL_STATUS = 1; private static final String[] PROJECTION_CRYPTO_STATUS = { "email_address", "email_status" }; protected static final String SORT_ORDER = ContactsContract.CommonDataKinds.Email.TIMES_CONTACTED + " DESC, " + ContactsContract.Contacts.DISPLAY_NAME + ", " + ContactsContract.CommonDataKinds.Email._ID; private static final int CRYPTO_PROVIDER_STATUS_UNTRUSTED = 1; private static final int CRYPTO_PROVIDER_STATUS_TRUSTED = 2; private final String query; private final Address[] addresses; private final Uri contactUri; private final String cryptoProvider; private List<Recipient> cachedRecipients; private ForceLoadContentObserver observerContact, observerKey; public RecipientLoader(Context context, String cryptoProvider, String query) { super(context); this.query = query; Loading @@ -86,8 +94,8 @@ 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>(); List<Recipient> recipients = new ArrayList<Recipient>(); Map<String, Recipient> recipientMap = new HashMap<String, Recipient>(); if (addresses != null) { fillContactDataFromAddresses(addresses, recipients, recipientMap); Loading @@ -110,8 +118,9 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { return recipients; } private void fillContactDataFromAddresses(Address[] addresses, ArrayList<Recipient> recipients, HashMap<String, Recipient> recipientMap) { private void fillContactDataFromAddresses(Address[] addresses, List<Recipient> recipients, Map<String, Recipient> recipientMap) { for (Address address : addresses) { // TODO actually query contacts - not sure if this is possible in a single query tho :( Recipient recipient = new Recipient(address); Loading @@ -120,10 +129,10 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { } } private void fillContactDataFromContactUri( Uri contactUri, ArrayList<Recipient> recipients, HashMap<String, Recipient> recipientMap) { // Get the contact id from the Uri String contactIdStr = contactUri.getLastPathSegment(); private void fillContactDataFromContactUri(Uri contactUri, List<Recipient> recipients, Map<String, Recipient> recipientMap) { String contactIdStr = getContactIdFromUri(contactUri); Cursor cursor = getContext().getContentResolver().query( ContactsContract.CommonDataKinds.Email.CONTENT_URI, Loading @@ -137,11 +146,18 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { fillContactDataFromCursor(cursor, recipients, recipientMap); } private void fillContactDataFromQuery( String query, ArrayList<Recipient> recipients, HashMap<String, Recipient> recipientMap) { private String getContactIdFromUri(Uri contactUri) { return contactUri.getLastPathSegment(); } private void fillContactDataFromQuery(String query, List<Recipient> recipients, Map<String, Recipient> recipientMap) { ContentResolver contentResolver = getContext().getContentResolver(); Uri queryUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Email.CONTENT_FILTER_URI, Uri.encode(query)); Cursor cursor = getContext().getContentResolver().query(queryUri, PROJECTION, null, null, SORT_ORDER); Cursor cursor = contentResolver.query(queryUri, PROJECTION, null, null, SORT_ORDER); if (cursor == null) { return; Loading @@ -151,12 +167,13 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { if (observerContact != null) { observerContact = new ForceLoadContentObserver(); getContext().getContentResolver().registerContentObserver(queryUri, false, observerContact); contentResolver.registerContentObserver(queryUri, false, observerContact); } } private void fillContactDataFromCursor(Cursor cursor, ArrayList<Recipient> recipients, HashMap<String, Recipient> recipientMap) { private void fillContactDataFromCursor(Cursor cursor, List<Recipient> recipients, Map<String, Recipient> recipientMap) { while (cursor.moveToNext()) { String name = cursor.getString(INDEX_NAME); String email = cursor.getString(INDEX_EMAIL); Loading @@ -172,70 +189,75 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { int addressType = cursor.getInt(INDEX_EMAIL_TYPE); String addressLabel = null; switch (addressType) { case ContactsContract.CommonDataKinds.Email.TYPE_HOME: case ContactsContract.CommonDataKinds.Email.TYPE_HOME: { addressLabel = getContext().getString(R.string.address_type_home); break; case ContactsContract.CommonDataKinds.Email.TYPE_WORK: } case ContactsContract.CommonDataKinds.Email.TYPE_WORK: { addressLabel = getContext().getString(R.string.address_type_work); break; case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: } case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: { addressLabel = getContext().getString(R.string.address_type_other); break; case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE: } case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE: { // mobile isn't listed as an option contacts app, but it has a constant so we better support it addressLabel = getContext().getString(R.string.address_type_mobile); break; case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM: } case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM: { addressLabel = cursor.getString(INDEX_EMAIL_CUSTOM_LABEL); break; } } Uri photoUri = cursor.isNull(INDEX_PHOTO_URI) ? null : Uri.parse(cursor.getString(INDEX_PHOTO_URI)); Uri photoUri = cursor.isNull(INDEX_PHOTO_URI) ? null : Uri.parse(cursor.getString(INDEX_PHOTO_URI)); Recipient recipient = new Recipient(name, email, addressLabel, contactId, lookupKey); recipient.photoThumbnailUri = photoUri; recipientMap.put(email, recipient); recipients.add(recipient); } cursor.close(); } private void fillCryptoStatusData(HashMap<String, Recipient> recipientMap) { ArrayList<String> recipientArrayList = new ArrayList<String>(recipientMap.keySet()); String[] recipientAddresses = recipientArrayList.toArray(new String[ recipientArrayList.size() ]); private void fillCryptoStatusData(Map<String, Recipient> recipientMap) { List<String> recipientList = new ArrayList<String>(recipientMap.keySet()); String[] recipientAddresses = recipientList.toArray(new String[recipientList.size()]); Uri queryUri = Uri.parse("content://" + cryptoProvider + ".provider.exported/email_status"); Cursor cursor = getContext().getContentResolver().query( queryUri, PROJECTION_CRYPTO_STATUS, null, recipientAddresses, null); Cursor cursor = getContext().getContentResolver().query(queryUri, PROJECTION_CRYPTO_STATUS, null, recipientAddresses, null); // fill all values with "unavailable", even if the query fails for (Recipient recipient : recipientMap.values()) { recipient.setCryptoStatus(RecipientCryptoStatus.UNAVAILABLE); } initializeCryptoStatusForAllRecipients(recipientMap); if (cursor == null) { return; } while (cursor.moveToNext()) { String email = cursor.getString(0); int status = cursor.getInt(1); String email = cursor.getString(INDEX_EMAIL_ADDRESS); int status = cursor.getInt(INDEX_EMAIL_STATUS); for (Address address : Address.parseUnencoded(email)) { if (recipientMap.containsKey(address.getAddress())) { Recipient recipient = recipientMap.get(address.getAddress()); String emailAddress = address.getAddress(); if (recipientMap.containsKey(emailAddress)) { Recipient recipient = recipientMap.get(emailAddress); switch (status) { case 1: case CRYPTO_PROVIDER_STATUS_UNTRUSTED: { recipient.setCryptoStatus(RecipientCryptoStatus.AVAILABLE_UNTRUSTED); break; case 2: } case CRYPTO_PROVIDER_STATUS_TRUSTED: { recipient.setCryptoStatus(RecipientCryptoStatus.AVAILABLE_TRUSTED); break; } } } } } cursor.close(); if (observerKey != null) { Loading @@ -244,6 +266,12 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { } } private void initializeCryptoStatusForAllRecipients(Map<String, Recipient> recipientMap) { for (Recipient recipient : recipientMap.values()) { recipient.setCryptoStatus(RecipientCryptoStatus.UNAVAILABLE); } } @Override public void deliverResult(List<Recipient> data) { cachedRecipients = data; Loading @@ -268,6 +296,7 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { @Override protected void onAbandon() { super.onAbandon(); if (observerKey != null) { getContext().getContentResolver().unregisterContentObserver(observerKey); } Loading @@ -275,5 +304,4 @@ public class RecipientLoader extends AsyncTaskLoader<List<Recipient>> { getContext().getContentResolver().unregisterContentObserver(observerContact); } } }
k9mail/src/main/java/com/fsck/k9/activity/RecipientMvpView.java +56 −32 File changed.Preview size limit exceeded, changes collapsed. Show changes