From 923d91b0126e65037863cfc279e86566936d6255 Mon Sep 17 00:00:00 2001 From: madRat Date: Fri, 26 Aug 2016 15:06:30 +0300 Subject: [PATCH] Replace QuickContactBadge with custom widget --- .../activity/AlternateRecipientAdapter.java | 6 +- .../activity/misc/ContactPictureLoader.java | 6 +- .../fsck/k9/fragment/MessageListFragment.java | 10 +- .../main/java/com/fsck/k9/helper/Utility.java | 4 +- .../java/com/fsck/k9/ui/ContactBadge.java | 306 ++++++++++++++++++ .../java/com/fsck/k9/view/MessageHeader.java | 6 +- .../src/main/res/layout/message_list_item.xml | 2 +- .../main/res/layout/message_view_header.xml | 2 +- .../res/layout/recipient_alternate_item.xml | 4 +- 9 files changed, 326 insertions(+), 20 deletions(-) create mode 100644 k9mail/src/main/java/com/fsck/k9/ui/ContactBadge.java diff --git a/k9mail/src/main/java/com/fsck/k9/activity/AlternateRecipientAdapter.java b/k9mail/src/main/java/com/fsck/k9/activity/AlternateRecipientAdapter.java index 7e13348b9..cc8c39666 100644 --- a/k9mail/src/main/java/com/fsck/k9/activity/AlternateRecipientAdapter.java +++ b/k9mail/src/main/java/com/fsck/k9/activity/AlternateRecipientAdapter.java @@ -17,11 +17,11 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; -import android.widget.QuickContactBadge; import android.widget.TextView; import com.fsck.k9.R; import com.fsck.k9.activity.compose.RecipientAdapter; +import com.fsck.k9.ui.ContactBadge; import com.fsck.k9.view.RecipientSelectView.Recipient; import com.fsck.k9.view.ThemeUtils; @@ -207,7 +207,7 @@ public class AlternateRecipientAdapter extends BaseAdapter { public final View layoutHeader, layoutItem; public final TextView headerName; public final TextView headerAddressLabel; - public final QuickContactBadge headerPhoto; + public final ContactBadge headerPhoto; public final View headerRemove; public final TextView itemAddress; public final TextView itemAddressLabel; @@ -221,7 +221,7 @@ public class AlternateRecipientAdapter extends BaseAdapter { headerName = (TextView) view.findViewById(R.id.alternate_header_name); headerAddressLabel = (TextView) view.findViewById(R.id.alternate_header_label); - headerPhoto = (QuickContactBadge) view.findViewById(R.id.alternate_contact_photo); + headerPhoto = (ContactBadge) view.findViewById(R.id.alternate_contact_photo); headerRemove = view.findViewById(R.id.alternate_remove); itemAddress = (TextView) view.findViewById(R.id.alternate_address); diff --git a/k9mail/src/main/java/com/fsck/k9/activity/misc/ContactPictureLoader.java b/k9mail/src/main/java/com/fsck/k9/activity/misc/ContactPictureLoader.java index b11a1d2b3..46ca7f895 100644 --- a/k9mail/src/main/java/com/fsck/k9/activity/misc/ContactPictureLoader.java +++ b/k9mail/src/main/java/com/fsck/k9/activity/misc/ContactPictureLoader.java @@ -130,7 +130,7 @@ public class ContactPictureLoader { * Load a contact picture and display it using the supplied {@link ImageView} instance. * *

- * If a picture is found in the cache, it is displayed in the {@code QuickContactBadge} + * If a picture is found in the cache, it is displayed in the {@code ContactBadge} * immediately. Otherwise a {@link ContactPictureRetrievalTask} is started to try to load the * contact picture in a background thread. Depending on the result the contact picture or a * fallback picture is then stored in the bitmap cache. @@ -140,7 +140,7 @@ public class ContactPictureLoader { * The {@link Address} instance holding the email address that is used to search the * contacts database. * @param imageView - * The {@code QuickContactBadge} instance to receive the picture. + * The {@code ContactBadge} instance to receive the picture. * * @see #mBitmapCache * @see #calculateFallbackBitmap(Address) @@ -242,7 +242,7 @@ public class ContactPictureLoader { } } - // No task associated with the QuickContactBadge, or an existing task was cancelled + // No task associated with the ContactBadge, or an existing task was cancelled return true; } diff --git a/k9mail/src/main/java/com/fsck/k9/fragment/MessageListFragment.java b/k9mail/src/main/java/com/fsck/k9/fragment/MessageListFragment.java index d128f2b1b..8277dc422 100644 --- a/k9mail/src/main/java/com/fsck/k9/fragment/MessageListFragment.java +++ b/k9mail/src/main/java/com/fsck/k9/fragment/MessageListFragment.java @@ -62,7 +62,6 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.CheckBox; import android.widget.CursorAdapter; import android.widget.ListView; -import android.widget.QuickContactBadge; import android.widget.TextView; import android.widget.Toast; @@ -114,6 +113,7 @@ import com.fsck.k9.search.SearchSpecification; import com.fsck.k9.search.SearchSpecification.SearchCondition; import com.fsck.k9.search.SearchSpecification.SearchField; import com.fsck.k9.search.SqlQueryBuilder; +import com.fsck.k9.ui.ContactBadge; public class MessageListFragment extends Fragment implements OnItemClickListener, @@ -1691,8 +1691,8 @@ public class MessageListFragment extends Fragment implements OnItemClickListener } - QuickContactBadge contactBadge = - (QuickContactBadge) view.findViewById(R.id.contact_badge); + ContactBadge contactBadge = + (ContactBadge) view.findViewById(R.id.contact_badge); if (mContactsPictureLoader != null) { holder.contactBadge = contactBadge; } else { @@ -1802,7 +1802,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener Utility.setContactForBadge(holder.contactBadge, counterpartyAddress); /* * At least in Android 2.2 a different background + padding is used when no - * email address is available. ListView reuses the views but QuickContactBadge + * email address is available. ListView reuses the views but ContactBadge * doesn't reset the padding, so we do it ourselves. */ holder.contactBadge.setPadding(0, 0, 0, 0); @@ -1960,7 +1960,7 @@ public class MessageListFragment extends Fragment implements OnItemClickListener public CheckBox flagged; public CheckBox selected; public int position = -1; - public QuickContactBadge contactBadge; + public ContactBadge contactBadge; @Override public void onClick(View view) { if (position != -1) { diff --git a/k9mail/src/main/java/com/fsck/k9/helper/Utility.java b/k9mail/src/main/java/com/fsck/k9/helper/Utility.java index 66e8874bb..d694298fb 100644 --- a/k9mail/src/main/java/com/fsck/k9/helper/Utility.java +++ b/k9mail/src/main/java/com/fsck/k9/helper/Utility.java @@ -14,10 +14,10 @@ import android.text.Editable; import android.text.TextUtils; import android.util.Log; import android.widget.EditText; -import android.widget.QuickContactBadge; import android.widget.TextView; import com.fsck.k9.K9; +import com.fsck.k9.ui.ContactBadge; import com.fsck.k9.mail.Address; import org.apache.james.mime4j.util.MimeUtil; @@ -483,7 +483,7 @@ public class Utility { * @param contactBadge the badge to the set the contact for * @param address the address to look for a contact for. */ - public static void setContactForBadge(QuickContactBadge contactBadge, + public static void setContactForBadge(ContactBadge contactBadge, Address address) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { Bundle extraContactInfo = new Bundle(); diff --git a/k9mail/src/main/java/com/fsck/k9/ui/ContactBadge.java b/k9mail/src/main/java/com/fsck/k9/ui/ContactBadge.java new file mode 100644 index 000000000..b22fb3e32 --- /dev/null +++ b/k9mail/src/main/java/com/fsck/k9/ui/ContactBadge.java @@ -0,0 +1,306 @@ +package com.fsck.k9.ui; + +import android.content.Context; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.ImageView; + +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.Intents; +import android.provider.ContactsContract.PhoneLookup; +import android.provider.ContactsContract.QuickContact; +import android.provider.ContactsContract.RawContacts; +import android.view.View; +import android.view.View.OnClickListener; + +/** + * ContactBadge replaces the android ContactBadge for custom drawing. + * + * Based on QuickContactBadge: + * https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/QuickContactBadge.java + */ +public class ContactBadge extends ImageView implements OnClickListener { + + private Uri mContactUri; + private String mContactEmail; + private String mContactPhone; + private QueryHandler mQueryHandler; + private Bundle mExtras = null; + + protected String[] mExcludeMimes = null; + + static final private int TOKEN_EMAIL_LOOKUP = 0; + static final private int TOKEN_PHONE_LOOKUP = 1; + static final private int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 2; + static final private int TOKEN_PHONE_LOOKUP_AND_TRIGGER = 3; + + static final private String EXTRA_URI_CONTENT = "uri_content"; + + static final String[] EMAIL_LOOKUP_PROJECTION = new String[] { + RawContacts.CONTACT_ID, + Contacts.LOOKUP_KEY, + }; + static final int EMAIL_ID_COLUMN_INDEX = 0; + static final int EMAIL_LOOKUP_STRING_COLUMN_INDEX = 1; + + static final String[] PHONE_LOOKUP_PROJECTION = new String[] { + PhoneLookup._ID, + PhoneLookup.LOOKUP_KEY, + }; + static final int PHONE_ID_COLUMN_INDEX = 0; + static final int PHONE_LOOKUP_STRING_COLUMN_INDEX = 1; + + + public ContactBadge(Context context) { + this(context, null); + } + public ContactBadge(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + public ContactBadge(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mQueryHandler = new QueryHandler(context.getContentResolver()); + setOnClickListener(this); + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + } + + /** This call has no effect anymore, as there is only one QuickContact mode */ + @SuppressWarnings("unused") + public void setMode(int size) { + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + } + + /** True if a contact, an email address or a phone number has been assigned */ + private boolean isAssigned() { + return mContactUri != null || mContactEmail != null || mContactPhone != null; + } + + /** + * Assign the contact uri that this ContactBadge should be associated + * with. Note that this is only used for displaying the QuickContact window and + * won't bind the contact's photo for you. Call {@link #setImageDrawable(Drawable)} to set the + * photo. + * + * @param contactUri Either a {@link Contacts#CONTENT_URI} or + * {@link Contacts#CONTENT_LOOKUP_URI} style URI. + */ + public void assignContactUri(Uri contactUri) { + mContactUri = contactUri; + mContactEmail = null; + mContactPhone = null; + onContactUriChanged(); + } + + /** + * Assign a contact based on an email address. This should only be used when + * the contact's URI is not available, as an extra query will have to be + * performed to lookup the URI based on the email. + * + * @param emailAddress The email address of the contact. + * @param lazyLookup If this is true, the lookup query will not be performed + * until this view is clicked. + */ + public void assignContactFromEmail(String emailAddress, boolean lazyLookup) { + assignContactFromEmail(emailAddress, lazyLookup, null); + } + + /** + * Assign a contact based on an email address. This should only be used when + * the contact's URI is not available, as an extra query will have to be + * performed to lookup the URI based on the email. + + @param emailAddress The email address of the contact. + @param lazyLookup If this is true, the lookup query will not be performed + until this view is clicked. + @param extras A bundle of extras to populate the contact edit page with if the contact + is not found and the user chooses to add the email address to an existing contact or + create a new contact. Uses the same string constants as those found in + {@link android.provider.ContactsContract.Intents.Insert} + */ + + public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) { + mContactEmail = emailAddress; + mExtras = extras; + if (!lazyLookup) { + mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null, + Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), + EMAIL_LOOKUP_PROJECTION, null, null, null); + } else { + mContactUri = null; + onContactUriChanged(); + } + } + + + /** + * Assign a contact based on a phone number. This should only be used when + * the contact's URI is not available, as an extra query will have to be + * performed to lookup the URI based on the phone number. + * + * @param phoneNumber The phone number of the contact. + * @param lazyLookup If this is true, the lookup query will not be performed + * until this view is clicked. + */ + + public void assignContactFromPhone(String phoneNumber, boolean lazyLookup) { + assignContactFromPhone(phoneNumber, lazyLookup, new Bundle()); + } + + /** + * Assign a contact based on a phone number. This should only be used when + * the contact's URI is not available, as an extra query will have to be + * performed to lookup the URI based on the phone number. + * + * @param phoneNumber The phone number of the contact. + * @param lazyLookup If this is true, the lookup query will not be performed + * until this view is clicked. + * @param extras A bundle of extras to populate the contact edit page with if the contact + * is not found and the user chooses to add the phone number to an existing contact or + * create a new contact. Uses the same string constants as those found in + * {@link android.provider.ContactsContract.Intents.Insert} + */ + public void assignContactFromPhone(String phoneNumber, boolean lazyLookup, Bundle extras) { + mContactPhone = phoneNumber; + mExtras = extras; + if (!lazyLookup) { + mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, null, + Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), + PHONE_LOOKUP_PROJECTION, null, null, null); + } else { + mContactUri = null; + onContactUriChanged(); + } + } + + private void onContactUriChanged() { + setEnabled(isAssigned()); + } + + @Override + public void onClick(View v) { + // If contact has been assigned, mExtras should no longer be null, but do a null check + // anyway just in case assignContactFromPhone or Email was called with a null bundle or + // wasn't assigned previously. + final Bundle extras = (mExtras == null) ? new Bundle() : mExtras; + if (mContactUri != null) { + QuickContact.showQuickContact(getContext(), ContactBadge.this, mContactUri, + QuickContact.MODE_LARGE, mExcludeMimes); + } else if (mContactEmail != null) { + extras.putString(EXTRA_URI_CONTENT, mContactEmail); + mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras, + Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(mContactEmail)), + EMAIL_LOOKUP_PROJECTION, null, null, null); + } else if (mContactPhone != null) { + extras.putString(EXTRA_URI_CONTENT, mContactPhone); + mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP_AND_TRIGGER, extras, + Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, mContactPhone), + PHONE_LOOKUP_PROJECTION, null, null, null); + } + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + event.setClassName(ContactBadge.class.getName()); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(ContactBadge.class.getName()); + } + + /** + * Set a list of specific MIME-types to exclude and not display. For + * example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE} + * profile icon. + */ + public void setExcludeMimes(String[] excludeMimes) { + mExcludeMimes = excludeMimes; + } + + private class QueryHandler extends AsyncQueryHandler { + + public QueryHandler(ContentResolver cr) { + super(cr); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + Uri lookupUri = null; + Uri createUri = null; + boolean trigger = false; + final Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle(); + try { + switch(token) { + case TOKEN_PHONE_LOOKUP_AND_TRIGGER: + trigger = true; + createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null); + + //$FALL-THROUGH$ + case TOKEN_PHONE_LOOKUP: { + if (cursor != null && cursor.moveToFirst()) { + long contactId = cursor.getLong(PHONE_ID_COLUMN_INDEX); + String lookupKey = cursor.getString(PHONE_LOOKUP_STRING_COLUMN_INDEX); + lookupUri = Contacts.getLookupUri(contactId, lookupKey); + } + + break; + } + case TOKEN_EMAIL_LOOKUP_AND_TRIGGER: + trigger = true; + createUri = Uri.fromParts("mailto", + extras.getString(EXTRA_URI_CONTENT), null); + + //$FALL-THROUGH$ + case TOKEN_EMAIL_LOOKUP: { + if (cursor != null && cursor.moveToFirst()) { + long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX); + String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX); + lookupUri = Contacts.getLookupUri(contactId, lookupKey); + } + break; + } + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + + mContactUri = lookupUri; + onContactUriChanged(); + + if (trigger && lookupUri != null) { + // Found contact, so trigger QuickContact + QuickContact.showQuickContact(getContext(), ContactBadge.this, lookupUri, + QuickContact.MODE_LARGE, ContactBadge.this.mExcludeMimes); + } else if (createUri != null) { + // Prompt user to add this person to contacts + final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri); + extras.remove(EXTRA_URI_CONTENT); + intent.putExtras(extras); + getContext().startActivity(intent); + } + } + } +} + diff --git a/k9mail/src/main/java/com/fsck/k9/view/MessageHeader.java b/k9mail/src/main/java/com/fsck/k9/view/MessageHeader.java index 10b0d3d3b..c823b16b2 100644 --- a/k9mail/src/main/java/com/fsck/k9/view/MessageHeader.java +++ b/k9mail/src/main/java/com/fsck/k9/view/MessageHeader.java @@ -23,7 +23,6 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.widget.CheckBox; import android.widget.LinearLayout; -import android.widget.QuickContactBadge; import android.widget.TextView; import android.widget.Toast; @@ -43,6 +42,7 @@ import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.ui.messageview.OnCryptoClickListener; +import com.fsck.k9.ui.ContactBadge; public class MessageHeader extends LinearLayout implements OnClickListener, OnLongClickListener { @@ -70,7 +70,7 @@ public class MessageHeader extends LinearLayout implements OnClickListener, OnLo private MessageHelper mMessageHelper; private ContactPictureLoader mContactsPictureLoader; - private QuickContactBadge mContactBadge; + private ContactBadge mContactBadge; private OnLayoutChangedListener mOnLayoutChangedListener; private OnCryptoClickListener onCryptoClickListener; @@ -107,7 +107,7 @@ public class MessageHeader extends LinearLayout implements OnClickListener, OnLo mCcView = (TextView) findViewById(R.id.cc); mCcLabel = (TextView) findViewById(R.id.cc_label); - mContactBadge = (QuickContactBadge) findViewById(R.id.contact_badge); + mContactBadge = (ContactBadge) findViewById(R.id.contact_badge); mSubjectView = (TextView) findViewById(R.id.subject); mAdditionalHeadersView = (TextView) findViewById(R.id.additional_headers_view); diff --git a/k9mail/src/main/res/layout/message_list_item.xml b/k9mail/src/main/res/layout/message_list_item.xml index be40e66f1..221051746 100644 --- a/k9mail/src/main/res/layout/message_list_item.xml +++ b/k9mail/src/main/res/layout/message_list_item.xml @@ -39,7 +39,7 @@ - - - - \ No newline at end of file +