diff options
-rw-r--r-- | chips/java/com/android/ex/chips/BaseRecipientAdapter.java | 185 | ||||
-rw-r--r-- | chips/res/drawable-hdpi/bg_separator.9.png | bin | 0 -> 153 bytes | |||
-rw-r--r-- | chips/res/drawable-hdpi/ic_contact_picture.png | bin | 0 -> 2270 bytes | |||
-rw-r--r-- | chips/res/drawable-mdpi/bg_separator.9.png | bin | 0 -> 153 bytes | |||
-rw-r--r-- | chips/res/drawable-mdpi/ic_contact_picture.png | bin | 0 -> 1553 bytes | |||
-rw-r--r-- | chips/res/drawable/bg_separator_inset.xml | 19 | ||||
-rw-r--r-- | chips/res/layout/chips_recipient_dropdown_item.xml | 60 | ||||
-rw-r--r-- | chips/res/layout/chips_separator.xml | 20 | ||||
-rw-r--r-- | chips/res/layout/chips_separator_within_group.xml | 20 |
9 files changed, 245 insertions, 59 deletions
diff --git a/chips/java/com/android/ex/chips/BaseRecipientAdapter.java b/chips/java/com/android/ex/chips/BaseRecipientAdapter.java index e34821a..4f27b8d 100644 --- a/chips/java/com/android/ex/chips/BaseRecipientAdapter.java +++ b/chips/java/com/android/ex/chips/BaseRecipientAdapter.java @@ -30,6 +30,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.CommonDataKinds.Photo; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Directory; @@ -37,6 +38,7 @@ import android.text.TextUtils; import android.text.util.Rfc822Token; import android.util.Log; import android.util.LruCache; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AutoCompleteTextView; @@ -76,6 +78,9 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter /** The number of photos cached in this Adapter. */ private static final int PHOTO_CACHE_SIZE = 20; + public static final int QUERY_TYPE_EMAIL = 0; + public static final int QUERY_TYPE_PHONE = 1; + /** * Model object for a {@link Directory} row. */ @@ -103,7 +108,18 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter public static final int PHOTO_THUMBNAIL_URI = 3; } - // TODO: PhoneQuery + private static class PhoneQuery { + public static final String[] PROJECTION = { + Contacts.DISPLAY_NAME, // 0 + Phone.DATA, // 1 + Phone.CONTACT_ID, // 2 + Contacts.PHOTO_THUMBNAIL_URI // 3 + }; + public static final int NAME = 0; + public static final int NUMBER = 1; + public static final int CONTACT_ID = 2; + public static final int PHOTO_THUMBNAIL_URI = 3; + } private static class PhotoQuery { public static final String[] PROJECTION = { @@ -145,12 +161,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter final FilterResults results = new FilterResults(); Cursor cursor = null; if (!TextUtils.isEmpty(constraint)) { - Uri uri = Email.CONTENT_FILTER_URI.buildUpon() - .appendPath(constraint.toString()) - .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, - String.valueOf(mPreferredMaxResultCount)) - .build(); - cursor = mContentResolver.query(uri, EmailQuery.PROJECTION, null, null, null); + cursor = doQuery(constraint, mPreferredMaxResultCount, null); if (cursor != null) { results.count = cursor.getCount(); } @@ -219,15 +230,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter protected FilterResults performFiltering(CharSequence constraint) { final FilterResults results = new FilterResults(); if (!TextUtils.isEmpty(constraint)) { - final Uri uri = Email.CONTENT_FILTER_URI.buildUpon() - .appendPath(constraint.toString()) - .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, - String.valueOf(mParams.directoryId)) - .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, - String.valueOf(getLimit() + ALLOWANCE_FOR_DUPLICATES)) - .build(); - final Cursor cursor = mContentResolver.query( - uri, EmailQuery.PROJECTION, null, null, null); + final Cursor cursor = doQuery(constraint, getLimit(), mParams.directoryId); if (cursor != null) { results.values = cursor; } @@ -253,6 +256,8 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter private final Context mContext; private final ContentResolver mContentResolver; + private final LayoutInflater mInflater; + private final int mQueryType; private Account mAccount; private final int mPreferredMaxResultCount; private final Handler mHandler = new Handler(); @@ -284,13 +289,22 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter private final Handler mPhotoHandler; private final LruCache<Uri, byte[]> mPhotoCacheMap; + /** + * Constructor for email queries. + */ public BaseRecipientAdapter(Context context) { - this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT); + this(context, QUERY_TYPE_EMAIL, DEFAULT_PREFERRED_MAX_RESULT_COUNT); + } + + public BaseRecipientAdapter(Context context, int queryType) { + this(context, queryType, DEFAULT_PREFERRED_MAX_RESULT_COUNT); } - public BaseRecipientAdapter(Context context, int preferredMaxResultCount) { + public BaseRecipientAdapter(Context context, int queryType, int preferredMaxResultCount) { mContext = context; mContentResolver = context.getContentResolver(); + mInflater = LayoutInflater.from(context); + mQueryType = queryType; mPreferredMaxResultCount = preferredMaxResultCount; mEntryMap = new HashMap<Integer, List<RecipientListEntry>>(); mNonAggregatedEntries = new ArrayList<RecipientListEntry>(); @@ -461,11 +475,23 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter private void putEntriesWithCursor(Cursor cursor, boolean validContactId) { cursor.move(-1); while (cursor.moveToNext()) { - final String displayName = cursor.getString(EmailQuery.NAME); - final String destination = cursor.getString(EmailQuery.ADDRESS); - // Contact ID can be invalid if the contact isn't locally stored and thus aggregated. - final int contactId = cursor.getInt(EmailQuery.CONTACT_ID); - final String thumbnailUriString = cursor.getString(EmailQuery.PHOTO_THUMBNAIL_URI); + final String displayName; + final String destination; + final int contactId; + final String thumbnailUriString; + if (mQueryType == QUERY_TYPE_EMAIL) { + displayName = cursor.getString(EmailQuery.NAME); + destination = cursor.getString(EmailQuery.ADDRESS); + contactId = cursor.getInt(EmailQuery.CONTACT_ID); + thumbnailUriString = cursor.getString(EmailQuery.PHOTO_THUMBNAIL_URI); + } else if (mQueryType == QUERY_TYPE_PHONE) { + displayName = cursor.getString(PhoneQuery.NAME); + destination = cursor.getString(PhoneQuery.NUMBER); + contactId = cursor.getInt(PhoneQuery.CONTACT_ID); + thumbnailUriString = cursor.getString(PhoneQuery.PHOTO_THUMBNAIL_URI); + } else { + throw new IndexOutOfBoundsException("Unexpected query type: " + mQueryType); + } // Note: At this point each entry doesn't contain have any photo (thus getPhotoBytes() // returns null). @@ -583,6 +609,36 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter }); } + private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) { + final Cursor cursor; + if (mQueryType == QUERY_TYPE_EMAIL) { + final Uri.Builder builder = Email.CONTENT_FILTER_URI.buildUpon() + .appendPath(constraint.toString()) + .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, + String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES)); + if (directoryId != null) { + builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, + String.valueOf(directoryId)); + } + cursor = mContentResolver.query( + builder.build(), EmailQuery.PROJECTION, null, null, null); + } else if (mQueryType == QUERY_TYPE_PHONE){ + final Uri.Builder builder = Phone.CONTENT_FILTER_URI.buildUpon() + .appendPath(constraint.toString()) + .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, + String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES)); + if (directoryId != null) { + builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, + String.valueOf(directoryId)); + } + cursor = mContentResolver.query( + builder.build(), PhoneQuery.PROJECTION, null, null, null); + } else { + cursor = null; + } + return cursor; + } + public void close() { mEntryMap.clear(); mNonAggregatedEntries.clear(); @@ -624,11 +680,12 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter final RecipientListEntry entry = mEntries.get(position); switch (entry.getEntryType()) { case RecipientListEntry.ENTRY_TYPE_SEP_NORMAL: { - return convertView != null ? convertView : inflateSeparatorView(parent); + return convertView != null ? convertView + : mInflater.inflate(getSeparatorLayout(), parent, false); } case RecipientListEntry.ENTRY_TYPE_SEP_WITHIN_GROUP: { return convertView != null ? convertView - : inflateSeparatorViewWithinGroup(parent); + : mInflater.inflate(getSeparatorWithinGroupLayout(), parent, false); } default: { String displayName = entry.getDisplayName(); @@ -639,21 +696,21 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter emailAddress = null; } - final View itemView = convertView != null ? convertView : inflateItemView(parent); - final TextView displayNameView = getDisplayNameView(itemView); - final TextView emailAddressView = getDestinationView(itemView); - final ImageView imageView = getPhotoView(itemView); - final View photoContainerView = getPhotoContainerView(itemView); + final View itemView = convertView != null ? convertView + : mInflater.inflate(getItemLayout(), parent, false); + final TextView displayNameView = + (TextView)itemView.findViewById(getDisplayNameId()); + final TextView emailAddressView = + (TextView)itemView.findViewById(getDestinationId()); + final ImageView imageView = (ImageView)itemView.findViewById(getPhotoId()); displayNameView.setText(displayName); if (!TextUtils.isEmpty(emailAddress)) { emailAddressView.setText(emailAddress); } if (entry.isFirstLevel()) { displayNameView.setVisibility(View.VISIBLE); - if (photoContainerView != null) { - photoContainerView.setVisibility(View.VISIBLE); - } if (imageView != null) { + imageView.setVisibility(View.VISIBLE); final byte[] photoBytes = entry.getPhotoBytes(); if (photoBytes != null && imageView != null) { final Bitmap photo = BitmapFactory.decodeByteArray( @@ -665,9 +722,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter } } else { displayNameView.setVisibility(View.GONE); - if (photoContainerView != null) { - photoContainerView.setVisibility(View.GONE); - } + if (imageView != null) imageView.setVisibility(View.GONE); } return itemView; } @@ -675,36 +730,48 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter } /** - * Inflates a View for each item inside auto-complete list. Subclasses must return the View - * containing two TextViews (for display name and destination) and one ImageView (for photo). - * The photo View should be surrounded by container (like FrameLayout) - * @see #getDisplayNameView(View) - * @see #getDestinationView(View) - * @see #getPhotoView(View) - * @see #getPhotoContainerView(View) - */ - protected abstract View inflateItemView(ViewGroup parent); - /** Inflates a View for a separator dividing two person or groups. */ - protected abstract View inflateSeparatorView(ViewGroup parent); - /** Inflates a View for a separator dividing two destinations for a same person or group. */ - protected abstract View inflateSeparatorViewWithinGroup(ViewGroup parent); - - /** Returns TextView in itemView for showing a display name. */ - protected abstract TextView getDisplayNameView(View itemView); - /** - * Returns TextView in itemView for showing a destination (an email address or a phone number). + * Returns a layout id for each item inside auto-complete list. + * + * Each View must contain two TextViews (for display name and destination) and one ImageView + * (for photo). Ids for those should be available via {@link #getDisplayNameId()}, + * {@link #getDestinationId()}, and {@link #getPhotoId()}. */ - protected abstract TextView getDestinationView(View itemView); - /** Returns ImageView in itemView for showing photo image for a person. */ - protected abstract ImageView getPhotoView(View itemView); + protected abstract int getItemLayout(); + /** Returns a layout id for a separator dividing two person or groups. */ + protected abstract int getSeparatorLayout(); /** - * Returns a View containing ImageView given by {@link #getPhotoView(View)}. Can be null. + * Returns a layout id for a separator dividing two destinations for a same person or group. */ - protected abstract View getPhotoContainerView(View itemView); + protected abstract int getSeparatorWithinGroupLayout(); /** * Returns a resource ID representing an image which should be shown when ther's no relevant * photo is available. */ protected abstract int getDefaultPhotoResource(); + + /** + * Returns an id for TextView in an item View for showing a display name. In default + * {@link android.R.id#text1} is returned. + */ + protected int getDisplayNameId() { + return android.R.id.text1; + } + + /** + * Returns an id for TextView in an item View for showing a destination + * (an email address or a phone number). + * In default {@link android.R.id#text2} is returned. + */ + protected int getDestinationId() { + return android.R.id.text2; + } + + /** + * Returns an id for ImageView in an item View for showing photo image for a person. In default + * {@link android.R.id#icon} is returned. + */ + protected int getPhotoId() { + return android.R.id.icon; + } } diff --git a/chips/res/drawable-hdpi/bg_separator.9.png b/chips/res/drawable-hdpi/bg_separator.9.png Binary files differnew file mode 100644 index 0000000..2b269e5 --- /dev/null +++ b/chips/res/drawable-hdpi/bg_separator.9.png diff --git a/chips/res/drawable-hdpi/ic_contact_picture.png b/chips/res/drawable-hdpi/ic_contact_picture.png Binary files differnew file mode 100644 index 0000000..31953dd --- /dev/null +++ b/chips/res/drawable-hdpi/ic_contact_picture.png diff --git a/chips/res/drawable-mdpi/bg_separator.9.png b/chips/res/drawable-mdpi/bg_separator.9.png Binary files differnew file mode 100644 index 0000000..2b269e5 --- /dev/null +++ b/chips/res/drawable-mdpi/bg_separator.9.png diff --git a/chips/res/drawable-mdpi/ic_contact_picture.png b/chips/res/drawable-mdpi/ic_contact_picture.png Binary files differnew file mode 100644 index 0000000..ff6c61a --- /dev/null +++ b/chips/res/drawable-mdpi/ic_contact_picture.png diff --git a/chips/res/drawable/bg_separator_inset.xml b/chips/res/drawable/bg_separator_inset.xml new file mode 100644 index 0000000..e263b81 --- /dev/null +++ b/chips/res/drawable/bg_separator_inset.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/bg_separator" + android:insetLeft="50dip"/> diff --git a/chips/res/layout/chips_recipient_dropdown_item.xml b/chips/res/layout/chips_recipient_dropdown_item.xml new file mode 100644 index 0000000..b5c4e9d --- /dev/null +++ b/chips/res/layout/chips_recipient_dropdown_item.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="30dip" + android:orientation="horizontal" + android:gravity="left|center_vertical"> + <FrameLayout + android:layout_width="50dip" + android:layout_height="wrap_content"> + <ImageView + android:id="@android:id/icon" + android:layout_width="42dip" + android:layout_height="42dip" + android:layout_margin="4dip" + android:src="@drawable/ic_contact_picture" + android:cropToPadding="true" + android:scaleType="centerCrop" /> + </FrameLayout> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="left|center_vertical"> + <TextView android:id="@android:id/text1" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:paddingLeft="6dip" + android:singleLine="true" + android:ellipsize="end" /> + <TextView android:id="@android:id/text2" + android:textColor="?android:attr/textColorSecondary" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:paddingLeft="6dip" + android:singleLine="true" + android:ellipsize="end" /> + </LinearLayout> +</LinearLayout> diff --git a/chips/res/layout/chips_separator.xml b/chips/res/layout/chips_separator.xml new file mode 100644 index 0000000..f9193d4 --- /dev/null +++ b/chips/res/layout/chips_separator.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<View xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="2px" + android:background="#ff444444" /> diff --git a/chips/res/layout/chips_separator_within_group.xml b/chips/res/layout/chips_separator_within_group.xml new file mode 100644 index 0000000..f823b6e --- /dev/null +++ b/chips/res/layout/chips_separator_within_group.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<View xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="1px" + android:background="@drawable/bg_separator_inset" /> |