diff options
| author | 2009-07-31 15:43:56 -0700 | |
|---|---|---|
| committer | 2009-08-01 15:59:08 -0700 | |
| commit | b3ac9318efde5de9938fea8b55b1f84addb80d4f (patch) | |
| tree | ed32ee4dbe05a74f2f1ff70fb87d38d89e516920 | |
| parent | cf31b1bcbe5b9081470ec67421c78894d59363f6 (diff) | |
Adds the ContactHeaderWidget for use by system apps.
The ContactHeaderWidget is a new widget that is used as a header banner
in various system applications, including contacts, mms, and gtalk.
Convenience methods are supplied for bind the widget to a specific
contact, which can be specified by contact id, email, or phone number.
| -rw-r--r-- | core/java/com/android/internal/widget/ContactHeaderWidget.java | 349 | ||||
| -rw-r--r-- | core/res/res/drawable/contact_header_bg.9.png | bin | 0 -> 231 bytes | |||
| -rw-r--r-- | core/res/res/drawable/contact_picture_bg.9.png | bin | 0 -> 1699 bytes | |||
| -rw-r--r-- | core/res/res/drawable/ic_contact_picture_2.png | bin | 0 -> 1894 bytes | |||
| -rw-r--r-- | core/res/res/drawable/ic_contact_picture_3.png | bin | 0 -> 1364 bytes | |||
| -rw-r--r-- | core/res/res/layout-ja/contact_header_name.xml | 39 | ||||
| -rw-r--r-- | core/res/res/layout/contact_header.xml | 56 | ||||
| -rw-r--r-- | core/res/res/layout/contact_header_name.xml | 26 |
8 files changed, 470 insertions, 0 deletions
diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java new file mode 100644 index 000000000000..94d4edc9bd76 --- /dev/null +++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2009 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. + */ + +package com.android.internal.widget; + +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.provider.SocialContract; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.PhoneLookup; +import android.provider.ContactsContract.RawContacts; +import android.provider.ContactsContract.CommonDataKinds.Photo; +import android.provider.SocialContract.Activities; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.android.internal.R; + + +/* Widget that is used across system apps for displaying a header banner with contact info */ +public class ContactHeaderWidget extends FrameLayout implements View.OnClickListener { + + private static final String TAG = "ContactHeaderWidget"; + + private TextView mDisplayNameView; + private TextView mPhoneticNameView; + private CheckBox mStarredView; + private ImageView mPhotoView; + private TextView mStatusView; + private int mNoPhotoResource; + private QueryHandler mQueryHandler; + + protected long mContactId; + protected Uri mContactDataUri; + protected Uri mContactUri; + protected Uri mStatusUri; + + protected ContentResolver mContentResolver; + + //Projection used for the summary info in the header. + protected static final String[] HEADER_PROJECTION = new String[] { + Contacts.DISPLAY_NAME, + Contacts.STARRED, + Contacts.PHOTO_ID, + }; + protected static final int HEADER_DISPLAY_NAME_COLUMN_INDEX = 0; + //TODO: We need to figure out how we're going to get the phonetic name. + //static final int HEADER_PHONETIC_NAME_COLUMN_INDEX + protected static final int HEADER_STARRED_COLUMN_INDEX = 1; + protected static final int HEADER_PHOTO_ID_COLUMN_INDEX = 2; + + //Projection used for finding the most recent social status. + protected static final String[] SOCIAL_PROJECTION = new String[] { + Activities.TITLE, + Activities.PUBLISHED, + }; + protected static final int SOCIAL_TITLE_COLUMN_INDEX = 0; + protected static final int SOCIAL_PUBLISHED_COLUMN_INDEX = 1; + + //Projection used for looking up contact id from phone number + protected static final String[] PHONE_LOOKUP_PROJECTION = new String[] { + RawContacts.CONTACT_ID, + }; + protected static final int PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0; + + //Projection used for looking up contact id from email address + protected static final String[] EMAIL_LOOKUP_PROJECTION = new String[] { + RawContacts.CONTACT_ID, + }; + protected static final int EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0; + + + private static final int TOKEN_CONTACT_INFO = 0; + private static final int TOKEN_SOCIAL = 1; + + public ContactHeaderWidget(Context context) { + this(context, null); + } + + public ContactHeaderWidget(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ContactHeaderWidget(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + mContentResolver = mContext.getContentResolver(); + + LayoutInflater inflater = + (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.contact_header, this); + + mDisplayNameView = (TextView) findViewById(R.id.name); + mPhoneticNameView = (TextView) findViewById(R.id.phonetic_name); + mStarredView = (CheckBox) findViewById(R.id.star); + mStarredView.setOnClickListener(this); + // Don't show start by default. + mStarredView.setVisibility(View.GONE); + mPhotoView = (ImageView) findViewById(R.id.photo); + mStatusView = (TextView) findViewById(R.id.status); + + // Set the photo with a random "no contact" image + long now = SystemClock.elapsedRealtime(); + int num = (int) now & 0xf; + if (num < 9) { + // Leaning in from right, common + mNoPhotoResource = R.drawable.ic_contact_picture; + } else if (num < 14) { + // Leaning in from left uncommon + mNoPhotoResource = R.drawable.ic_contact_picture_2; + } else { + // Coming in from the top, rare + mNoPhotoResource = R.drawable.ic_contact_picture_3; + } + + mQueryHandler = new QueryHandler(mContentResolver); + } + + private class QueryHandler extends AsyncQueryHandler { + + public QueryHandler(ContentResolver cr) { + super(cr); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + try{ + if (token == TOKEN_CONTACT_INFO) { + bindContactInfo(cursor); + invalidate(); + } else if (token == TOKEN_SOCIAL) { + bindSocial(cursor); + invalidate(); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + } + + + /** {@inheritDoc} */ + public void onQueryComplete(int token, Object cookie, Cursor cursor) { + try{ + if (token == TOKEN_CONTACT_INFO) { + bindContactInfo(cursor); + invalidate(); + } else if (token == TOKEN_SOCIAL) { + bindSocial(cursor); + invalidate(); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + } + + /** + * Turn on/off showing of the star element. + */ + public void showStar(boolean showStar) { + mStarredView.setVisibility(showStar ? View.VISIBLE : View.GONE); + } + + /** + * Convenience method for binding all available data from an existing + * contact. + * + * @param contactId the contact id of the contact whose info should be displayed. + */ + public void bindFromContactId(long contactId) { + mContactId = contactId; + mContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, mContactId); + mContactDataUri = Uri.withAppendedPath(mContactUri, Contacts.Data.CONTENT_DIRECTORY); + mStatusUri = ContentUris.withAppendedId( + SocialContract.Activities.CONTENT_CONTACT_STATUS_URI, mContactId); + redrawHeader(); + } + + /** + * Convenience method for binding all available data from an existing + * contact. + * + * @param emailAddress The email address used to do a reverse lookup in + * the contacts database. If more than one contact contains this email + * address, one of them will be chosen to bind to. + */ + public void bindFromEmail(String emailAddress) { + Cursor c = null; + try { + c = mContentResolver.query(Uri.withAppendedPath( + RawContacts.CONTENT_FILTER_EMAIL_URI, Uri.encode(emailAddress)), + EMAIL_LOOKUP_PROJECTION, null, null, null); + c.moveToFirst(); + long contactId = c.getLong(EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX); + bindFromContactId(contactId); + } finally { + if (c != null) { + c.close(); + } + } + } + + /** + * Convenience method for binding all available data from an existing + * contact. + * + * @param number The phone number used to do a reverse lookup in + * the contacts database. If more than one contact contains this phone + * number, one of them will be chosen to bind to. + */ + public void bindFromPhoneNumber(String number) { + Cursor c = null; + try { + c = mContentResolver.query(Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number), + PHONE_LOOKUP_PROJECTION, null, null, null); + c.moveToFirst(); + long contactId = c.getLong(PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX); + bindFromContactId(contactId); + } finally { + if (c != null) { + c.close(); + } + } + } + + protected void redrawHeader() { + if (mContactDataUri != null) { + mQueryHandler.startQuery(TOKEN_CONTACT_INFO, null, mContactDataUri, HEADER_PROJECTION, + null, null, null); + } + + if (mStatusUri != null) { + mQueryHandler.startQuery(TOKEN_SOCIAL, null, mStatusUri, SOCIAL_PROJECTION, + null, null, null); + } + } + + protected void bindContactInfo(Cursor c) { + if (c == null) { + return; + } + if (c.moveToFirst()) { + //Set name + String displayName = c.getString(HEADER_DISPLAY_NAME_COLUMN_INDEX); + Log.i(TAG, displayName); + mDisplayNameView.setText(displayName); + //TODO: Bring back phonetic name + /*if (mPhoneticNameView != null) { + String phoneticName = c.getString(CONTACT_PHONETIC_NAME_COLUMN); + mPhoneticNameView.setText(phoneticName); + }*/ + + //Set starred + mStarredView.setChecked(c.getInt(HEADER_STARRED_COLUMN_INDEX) == 1); + + //Set the photo + Bitmap photoBitmap = loadContactPhoto(c.getLong(HEADER_PHOTO_ID_COLUMN_INDEX), null); + if (photoBitmap == null) { + photoBitmap = loadPlaceholderPhoto(null); + } + mPhotoView.setImageBitmap(photoBitmap); + } + } + + protected void bindSocial(Cursor c) { + if (c == null) { + return; + } + if (c.moveToFirst()) { + String status = c.getString(SOCIAL_TITLE_COLUMN_INDEX); + mStatusView.setText(status); + } + } + + public void onClick(View view) { + if (view.getId() == R.id.star) { + ContentValues values = new ContentValues(1); + values.put(Contacts.STARRED, mStarredView.isChecked()); + mContentResolver.update(mContactUri, values, null, null); + } + } + + private Bitmap loadContactPhoto(long photoId, BitmapFactory.Options options) { + Cursor photoCursor = null; + Bitmap photoBm = null; + + try { + photoCursor = mContentResolver.query( + ContentUris.withAppendedId(Data.CONTENT_URI, photoId), + new String[] { Photo.PHOTO }, + null, null, null); + + if (photoCursor.moveToFirst() && !photoCursor.isNull(0)) { + byte[] photoData = photoCursor.getBlob(0); + photoBm = BitmapFactory.decodeByteArray(photoData, 0, + photoData.length, options); + } + } finally { + if (photoCursor != null) { + photoCursor.close(); + } + } + + return photoBm; + } + + private Bitmap loadPlaceholderPhoto(BitmapFactory.Options options) { + if (mNoPhotoResource == 0) { + return null; + } + return BitmapFactory.decodeResource(mContext.getResources(), + mNoPhotoResource, options); + } +} diff --git a/core/res/res/drawable/contact_header_bg.9.png b/core/res/res/drawable/contact_header_bg.9.png Binary files differnew file mode 100644 index 000000000000..7f9a5a3e0d79 --- /dev/null +++ b/core/res/res/drawable/contact_header_bg.9.png diff --git a/core/res/res/drawable/contact_picture_bg.9.png b/core/res/res/drawable/contact_picture_bg.9.png Binary files differnew file mode 100644 index 000000000000..ae9c70920995 --- /dev/null +++ b/core/res/res/drawable/contact_picture_bg.9.png diff --git a/core/res/res/drawable/ic_contact_picture_2.png b/core/res/res/drawable/ic_contact_picture_2.png Binary files differnew file mode 100644 index 000000000000..8b184af7cfe4 --- /dev/null +++ b/core/res/res/drawable/ic_contact_picture_2.png diff --git a/core/res/res/drawable/ic_contact_picture_3.png b/core/res/res/drawable/ic_contact_picture_3.png Binary files differnew file mode 100644 index 000000000000..a2d08b55fe8e --- /dev/null +++ b/core/res/res/drawable/ic_contact_picture_3.png diff --git a/core/res/res/layout-ja/contact_header_name.xml b/core/res/res/layout-ja/contact_header_name.xml new file mode 100644 index 000000000000..03332b11d4b4 --- /dev/null +++ b/core/res/res/layout-ja/contact_header_name.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 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. +--> + +<!-- In Japanese-language locales, the "Name" field contains two separate + TextViews: the name itself, and also the phonetic ("furigana") field. --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="0dip" + android:layout_weight="1" + android:layout_height="wrap_content"> + + <TextView android:id="@+id/name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLargeInverse" + android:textColor="@android:color/secondary_text_light" + /> + + <TextView android:id="@+id/phonetic_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmallInverse" + android:textColor="@android:color/secondary_text_light" + /> + +</LinearLayout> diff --git a/core/res/res/layout/contact_header.xml b/core/res/res/layout/contact_header.xml new file mode 100644 index 000000000000..04b34b8fd62c --- /dev/null +++ b/core/res/res/layout/contact_header.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 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:id="@+id/banner" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:background="@drawable/contact_header_bg" + android:paddingRight="5dip" + android:gravity="center_vertical"> + + <ImageView android:id="@+id/photo" + android:layout_width="64dip" + android:layout_height="64dip" + android:layout_marginRight="7dip" + android:layout_marginLeft="2dip" + android:scaleType="fitCenter" + android:background="@drawable/contact_picture_bg"/> + + <LinearLayout + android:layout_width="0dip" + android:layout_height="fill_parent" + android:layout_weight="1" + android:orientation="vertical"> + + <!-- "Name" field is locale-specific. --> + <include layout="@layout/contact_header_name"/> + + <TextView android:id="@+id/status" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmall" + android:maxLines="2"/> + + </LinearLayout> + + <CheckBox android:id="@+id/star" + style="?android:attr/starStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + +</LinearLayout>
\ No newline at end of file diff --git a/core/res/res/layout/contact_header_name.xml b/core/res/res/layout/contact_header_name.xml new file mode 100644 index 000000000000..aec943e4cf6e --- /dev/null +++ b/core/res/res/layout/contact_header_name.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2009 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. +--> + +<!-- In the default locale, the "Name" field is a single TextView --> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/name" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLargeInverse" + android:textColor="@android:color/secondary_text_light" + android:maxLines="2" + android:ellipsize="end" + /> |