Fix the build by copying the now defunct ContactHeaderWidget into the app.

Change-Id: I76ae8ede2cbb95acbfdad0441486888eaeadd1a0
diff --git a/LoaderApp/res/drawable-hdpi/ic_contact_picture.png b/LoaderApp/res/drawable-hdpi/ic_contact_picture.png
new file mode 100644
index 0000000..a60565a
--- /dev/null
+++ b/LoaderApp/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/LoaderApp/res/drawable-hdpi/ic_contact_picture_2.png b/LoaderApp/res/drawable-hdpi/ic_contact_picture_2.png
new file mode 100755
index 0000000..5e65276
--- /dev/null
+++ b/LoaderApp/res/drawable-hdpi/ic_contact_picture_2.png
Binary files differ
diff --git a/LoaderApp/res/drawable-hdpi/ic_contact_picture_3.png b/LoaderApp/res/drawable-hdpi/ic_contact_picture_3.png
new file mode 100755
index 0000000..a8ec1e1
--- /dev/null
+++ b/LoaderApp/res/drawable-hdpi/ic_contact_picture_3.png
Binary files differ
diff --git a/LoaderApp/res/drawable-hdpi/title_bar_medium.9.png b/LoaderApp/res/drawable-hdpi/title_bar_medium.9.png
new file mode 100644
index 0000000..311a54a
--- /dev/null
+++ b/LoaderApp/res/drawable-hdpi/title_bar_medium.9.png
Binary files differ
diff --git a/LoaderApp/res/drawable-mdpi/ic_contact_picture.png b/LoaderApp/res/drawable-mdpi/ic_contact_picture.png
new file mode 100644
index 0000000..3a338e8
--- /dev/null
+++ b/LoaderApp/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/LoaderApp/res/drawable-mdpi/ic_contact_picture_2.png b/LoaderApp/res/drawable-mdpi/ic_contact_picture_2.png
new file mode 100644
index 0000000..8b184af
--- /dev/null
+++ b/LoaderApp/res/drawable-mdpi/ic_contact_picture_2.png
Binary files differ
diff --git a/LoaderApp/res/drawable-mdpi/ic_contact_picture_3.png b/LoaderApp/res/drawable-mdpi/ic_contact_picture_3.png
new file mode 100644
index 0000000..a2d08b5
--- /dev/null
+++ b/LoaderApp/res/drawable-mdpi/ic_contact_picture_3.png
Binary files differ
diff --git a/LoaderApp/res/drawable-mdpi/title_bar_medium.9.png b/LoaderApp/res/drawable-mdpi/title_bar_medium.9.png
new file mode 100644
index 0000000..2d41d02
--- /dev/null
+++ b/LoaderApp/res/drawable-mdpi/title_bar_medium.9.png
Binary files differ
diff --git a/LoaderApp/res/layout/contact_details.xml b/LoaderApp/res/layout/contact_details.xml
index f4652e9..db1fb09 100644
--- a/LoaderApp/res/layout/contact_details.xml
+++ b/LoaderApp/res/layout/contact_details.xml
@@ -20,10 +20,11 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     
-    <com.android.internal.widget.ContactHeaderWidget
+    <com.android.loaderapp.ContactHeaderWidget
         android:id="@+id/contact_header_widget"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
+        android:layout_height="wrap_content"
+    />
         
     <ListView android:id="@android:id/list"
         android:layout_width="match_parent"
diff --git a/LoaderApp/res/layout/contact_header.xml b/LoaderApp/res/layout/contact_header.xml
new file mode 100644
index 0000000..8abe061
--- /dev/null
+++ b/LoaderApp/res/layout/contact_header.xml
@@ -0,0 +1,96 @@
+<?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="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:background="@drawable/title_bar_medium"
+    android:paddingRight="5dip">
+
+    <android.widget.QuickContactBadge android:id="@+id/photo"
+        android:layout_gravity="center_vertical"
+        android:layout_marginRight="8dip"
+        android:layout_marginLeft="-1dip"
+        style="@*android:style/Widget.QuickContactBadge.WindowSmall" />
+    />
+
+    <LinearLayout
+        android:layout_width="0dip"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:layout_gravity="center_vertical" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView android:id="@+id/name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:textStyle="bold"
+                android:shadowColor="#BB000000"
+                android:shadowRadius="2.75"
+                />
+        </LinearLayout>
+
+        <TextView android:id="@+id/phonetic_name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:layout_marginTop="-2dip"
+            android:visibility="gone"
+        />
+
+        <TextView android:id="@+id/status"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:layout_marginTop="-2dip"
+            android:visibility="gone"
+        />
+
+        <TextView android:id="@+id/status_date"
+            android:layout_width="match_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textSize="12sp"
+            android:layout_marginTop="-2dip"
+            android:visibility="gone"
+        />
+    </LinearLayout>
+
+    <ImageView
+        android:id="@+id/presence"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingLeft="3dip"
+        android:paddingRight="6dip"
+        android:visibility="gone"
+    />
+</LinearLayout>
diff --git a/LoaderApp/res/values/strings.xml b/LoaderApp/res/values/strings.xml
index 5b06ebc..0a2dba7 100644
--- a/LoaderApp/res/values/strings.xml
+++ b/LoaderApp/res/values/strings.xml
@@ -1143,4 +1143,10 @@
 
     <!-- Title shown in the search result activity of contacts app while searching -->
     <string name="search_results_searching">Searching...</string>
+
+    <!-- Attbution of a contact status update, when the time of update is unknown -->
+    <string name="contact_status_update_attribution">via <xliff:g id="source" example="Google Talk">%1$s</xliff:g></string>
+
+    <!-- Attbution of a contact status update, when the time of update is known -->
+    <string name="contact_status_update_attribution_with_date"><xliff:g id="date" example="3 hours ago">%1$s</xliff:g> via <xliff:g id="source" example="Google Talk">%2$s</xliff:g></string>
 </resources>
diff --git a/LoaderApp/src/com/android/loaderapp/ContactHeaderWidget.java b/LoaderApp/src/com/android/loaderapp/ContactHeaderWidget.java
new file mode 100644
index 0000000..e6824a2
--- /dev/null
+++ b/LoaderApp/src/com/android/loaderapp/ContactHeaderWidget.java
@@ -0,0 +1,686 @@
+/*
+ * 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.loaderapp;
+
+import android.Manifest;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.StatusUpdates;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.QuickContactBadge;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * Header used across system for displaying a title bar with contact info. You
+ * can bind specific values on the header, or use helper methods like
+ * {@link #bindFromContactLookupUri(Uri)} to populate asynchronously.
+ * <p>
+ * The parent must request the {@link Manifest.permission#READ_CONTACTS}
+ * permission to access contact data.
+ */
+public class ContactHeaderWidget extends FrameLayout implements View.OnClickListener {
+
+    private static final String TAG = "ContactHeaderWidget";
+
+    private TextView mDisplayNameView;
+    private View mAggregateBadge;
+    private TextView mPhoneticNameView;
+    private CheckBox mStarredView;
+    private QuickContactBadge mPhotoView;
+    private ImageView mPresenceView;
+    private TextView mStatusView;
+    private TextView mStatusAttributionView;
+    private int mNoPhotoResource;
+    private QueryHandler mQueryHandler;
+
+    protected Uri mContactUri;
+
+    protected String[] mExcludeMimes = null;
+
+    protected ContentResolver mContentResolver;
+
+    /**
+     * Interface for callbacks invoked when the user interacts with a header.
+     */
+    public interface ContactHeaderListener {
+        public void onPhotoClick(View view);
+        public void onDisplayNameClick(View view);
+    }
+
+    private ContactHeaderListener mListener;
+
+
+    private interface ContactQuery {
+        //Projection used for the summary info in the header.
+        String[] COLUMNS = new String[] {
+            Contacts._ID,
+            Contacts.LOOKUP_KEY,
+            Contacts.PHOTO_ID,
+            Contacts.DISPLAY_NAME,
+            Contacts.PHONETIC_NAME,
+            Contacts.STARRED,
+            Contacts.CONTACT_PRESENCE,
+            Contacts.CONTACT_STATUS,
+            Contacts.CONTACT_STATUS_TIMESTAMP,
+            Contacts.CONTACT_STATUS_RES_PACKAGE,
+            Contacts.CONTACT_STATUS_LABEL,
+        };
+        int _ID = 0;
+        int LOOKUP_KEY = 1;
+        int PHOTO_ID = 2;
+        int DISPLAY_NAME = 3;
+        int PHONETIC_NAME = 4;
+        //TODO: We need to figure out how we're going to get the phonetic name.
+        //static final int HEADER_PHONETIC_NAME_COLUMN_INDEX
+        int STARRED = 5;
+        int CONTACT_PRESENCE_STATUS = 6;
+        int CONTACT_STATUS = 7;
+        int CONTACT_STATUS_TIMESTAMP = 8;
+        int CONTACT_STATUS_RES_PACKAGE = 9;
+        int CONTACT_STATUS_LABEL = 10;
+    }
+
+    private interface PhotoQuery {
+        String[] COLUMNS = new String[] {
+            Photo.PHOTO
+        };
+
+        int PHOTO = 0;
+    }
+
+    //Projection used for looking up contact id from phone number
+    protected static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
+        PhoneLookup._ID,
+        PhoneLookup.LOOKUP_KEY,
+    };
+    protected static final int PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
+    protected static final int PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX = 1;
+
+    //Projection used for looking up contact id from email address
+    protected static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
+        RawContacts.CONTACT_ID,
+        Contacts.LOOKUP_KEY,
+    };
+    protected static final int EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
+    protected static final int EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX = 1;
+
+    protected static final String[] CONTACT_LOOKUP_PROJECTION = new String[] {
+        Contacts._ID,
+    };
+    protected static final int CONTACT_LOOKUP_ID_COLUMN_INDEX = 0;
+
+    private static final int TOKEN_CONTACT_INFO = 0;
+    private static final int TOKEN_PHONE_LOOKUP = 1;
+    private static final int TOKEN_EMAIL_LOOKUP = 2;
+    private static final int TOKEN_PHOTO_QUERY = 3;
+
+    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);
+
+        mPhotoView = (QuickContactBadge) findViewById(R.id.photo);
+
+        mPresenceView = (ImageView) findViewById(R.id.presence);
+
+        mStatusView = (TextView)findViewById(R.id.status);
+        mStatusAttributionView = (TextView)findViewById(R.id.status_date);
+
+        // 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;
+        }
+
+        resetAsyncQueryHandler();
+    }
+
+    public void enableClickListeners() {
+        mDisplayNameView.setOnClickListener(this);
+        mPhotoView.setOnClickListener(this);
+    }
+
+    /**
+     * Set the given {@link ContactHeaderListener} to handle header events.
+     */
+    public void setContactHeaderListener(ContactHeaderListener listener) {
+        mListener = listener;
+    }
+
+    private void performPhotoClick() {
+        if (mListener != null) {
+            mListener.onPhotoClick(mPhotoView);
+        }
+    }
+
+    private void performDisplayNameClick() {
+        if (mListener != null) {
+            mListener.onDisplayNameClick(mDisplayNameView);
+        }
+    }
+
+    private class QueryHandler extends AsyncQueryHandler {
+
+        public QueryHandler(ContentResolver cr) {
+            super(cr);
+        }
+
+        @Override
+        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+            try{
+                if (this != mQueryHandler) {
+                    Log.d(TAG, "onQueryComplete: discard result, the query handler is reset!");
+                    return;
+                }
+
+                switch (token) {
+                    case TOKEN_PHOTO_QUERY: {
+                        //Set the photo
+                        Bitmap photoBitmap = null;
+                        if (cursor != null && cursor.moveToFirst()
+                                && !cursor.isNull(PhotoQuery.PHOTO)) {
+                            byte[] photoData = cursor.getBlob(PhotoQuery.PHOTO);
+                            photoBitmap = BitmapFactory.decodeByteArray(photoData, 0,
+                                    photoData.length, null);
+                        }
+
+                        if (photoBitmap == null) {
+                            photoBitmap = loadPlaceholderPhoto(null);
+                        }
+                        setPhoto(photoBitmap);
+                        if (cookie != null && cookie instanceof Uri) {
+                            mPhotoView.assignContactUri((Uri) cookie);
+                        }
+                        invalidate();
+                        break;
+                    }
+                    case TOKEN_CONTACT_INFO: {
+                        if (cursor != null && cursor.moveToFirst()) {
+                            bindContactInfo(cursor);
+                            final Uri lookupUri = Contacts.getLookupUri(
+                                    cursor.getLong(ContactQuery._ID),
+                                    cursor.getString(ContactQuery.LOOKUP_KEY));
+
+                            final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
+
+                            setPhotoId(photoId, lookupUri);
+                        } else {
+                            // shouldn't really happen
+                            setDisplayName(null, null);
+                            setSocialSnippet(null);
+                            setPhoto(loadPlaceholderPhoto(null));
+                        }
+                        break;
+                    }
+                    case TOKEN_PHONE_LOOKUP: {
+                        if (cursor != null && cursor.moveToFirst()) {
+                            long contactId = cursor.getLong(PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX);
+                            String lookupKey = cursor.getString(
+                                    PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+                            bindFromContactUriInternal(Contacts.getLookupUri(contactId, lookupKey),
+                                    false /* don't reset query handler */);
+                        } else {
+                            String phoneNumber = (String) cookie;
+                            setDisplayName(phoneNumber, null);
+                            setSocialSnippet(null);
+                            setPhoto(loadPlaceholderPhoto(null));
+                            mPhotoView.assignContactFromPhone(phoneNumber, true);
+                        }
+                        break;
+                    }
+                    case TOKEN_EMAIL_LOOKUP: {
+                        if (cursor != null && cursor.moveToFirst()) {
+                            long contactId = cursor.getLong(EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX);
+                            String lookupKey = cursor.getString(
+                                    EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+                            bindFromContactUriInternal(Contacts.getLookupUri(contactId, lookupKey),
+                                    false /* don't reset query handler */);
+                        } else {
+                            String emailAddress = (String) cookie;
+                            setDisplayName(emailAddress, null);
+                            setSocialSnippet(null);
+                            setPhoto(loadPlaceholderPhoto(null));
+                            mPhotoView.assignContactFromEmail(emailAddress, true);
+                        }
+                        break;
+                    }
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+    }
+
+    /**
+     * Manually set the presence.
+     */
+    public void setPresence(int presence) {
+        mPresenceView.setImageResource(StatusUpdates.getPresenceIconResourceId(presence));
+    }
+
+    /**
+     * Manually set the presence. If presence is null, it is hidden.
+     * This doesn't change the underlying {@link Contacts} value, only the UI state.
+     * @hide
+     */
+    public void setPresence(Integer presence) {
+        if (presence == null) {
+            showPresence(false);
+        } else {
+            showPresence(true);
+            setPresence(presence.intValue());
+        }
+    }
+
+    /**
+     * Turn on/off showing the presence.
+     * @hide this is here for consistency with setStared/showStar and should be public
+     */
+    public void showPresence(boolean showPresence) {
+        mPresenceView.setVisibility(showPresence ? View.VISIBLE : View.GONE);
+    }
+
+    /**
+     * Manually set the contact uri without loading any data
+     */
+    public void setContactUri(Uri uri) {
+        setContactUri(uri, true);
+    }
+
+    /**
+     * Manually set the contact uri without loading any data
+     */
+    public void setContactUri(Uri uri, boolean sendToQuickContact) {
+        mContactUri = uri;
+        if (sendToQuickContact) {
+            mPhotoView.assignContactUri(uri);
+        }
+    }
+
+    /**
+     * Manually set the photo to display in the header. This doesn't change the
+     * underlying {@link Contacts}, only the UI state.
+     */
+    public void setPhoto(Bitmap bitmap) {
+        mPhotoView.setImageBitmap(bitmap);
+    }
+
+    /**
+     * Manually set the photo given its id. If the id is 0, a placeholder picture will
+     * be loaded. For any other Id, an async query is started
+     * @hide
+     */
+    public void setPhotoId(final long photoId, final Uri lookupUri) {
+        if (photoId == 0) {
+            setPhoto(loadPlaceholderPhoto(null));
+            mPhotoView.assignContactUri(lookupUri);
+            invalidate();
+        } else {
+            startPhotoQuery(photoId, lookupUri,
+                    false /* don't reset query handler */);
+        }
+    }
+
+    /**
+     * Manually set the display name and phonetic name to show in the header.
+     * This doesn't change the underlying {@link Contacts}, only the UI state.
+     */
+    public void setDisplayName(CharSequence displayName, CharSequence phoneticName) {
+        mDisplayNameView.setText(displayName);
+        if (!TextUtils.isEmpty(phoneticName)) {
+            mPhoneticNameView.setText(phoneticName);
+            mPhoneticNameView.setVisibility(View.VISIBLE);
+        } else {
+            mPhoneticNameView.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Manually set the social snippet text to display in the header. This doesn't change the
+     * underlying {@link Contacts}, only the UI state.
+     */
+    public void setSocialSnippet(CharSequence snippet) {
+        if (snippet == null) {
+            mStatusView.setVisibility(View.GONE);
+            mStatusAttributionView.setVisibility(View.GONE);
+        } else {
+            mStatusView.setText(snippet);
+            mStatusView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /**
+     * Manually set the status attribution text to display in the header.
+     * This doesn't change the underlying {@link Contacts}, only the UI state.
+     * @hide
+     */
+    public void setStatusAttribution(CharSequence attribution) {
+        if (attribution != null) {
+            mStatusAttributionView.setText(attribution);
+            mStatusAttributionView.setVisibility(View.VISIBLE);
+        } else {
+            mStatusAttributionView.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * 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;
+        mPhotoView.setExcludeMimes(excludeMimes);
+    }
+
+    /**
+     * Manually set all the status values to display in the header.
+     * This doesn't change the underlying {@link Contacts}, only the UI state.
+     * @hide
+     * @param status             The status of the contact. If this is either null or empty,
+     *                           the status is cleared and the other parameters are ignored.
+     * @param statusTimestamp    The timestamp (retrieved via a call to
+     *                           {@link System#currentTimeMillis()}) of the last status update.
+     *                           This value can be null if it is not known.
+     * @param statusLabel        The id of a resource string that specifies the current
+     *                           status. This value can be null if no Label should be used.
+     * @param statusResPackage   The name of the resource package containing the resource string
+     *                           referenced in the parameter statusLabel.
+     */
+    public void setStatus(final String status, final Long statusTimestamp,
+            final Integer statusLabel, final String statusResPackage) {
+        if (TextUtils.isEmpty(status)) {
+            setSocialSnippet(null);
+            return;
+        }
+
+        setSocialSnippet(status);
+
+        final CharSequence timestampDisplayValue;
+
+        if (statusTimestamp != null) {
+            // Set the date/time field by mixing relative and absolute
+            // times.
+            int flags = DateUtils.FORMAT_ABBREV_RELATIVE;
+
+            timestampDisplayValue = DateUtils.getRelativeTimeSpanString(
+                    statusTimestamp.longValue(), System.currentTimeMillis(),
+                    DateUtils.MINUTE_IN_MILLIS, flags);
+        } else {
+            timestampDisplayValue = null;
+        }
+
+
+        String labelDisplayValue = null;
+
+        if (statusLabel != null) {
+            Resources resources;
+            if (TextUtils.isEmpty(statusResPackage)) {
+                resources = getResources();
+            } else {
+                PackageManager pm = getContext().getPackageManager();
+                try {
+                    resources = pm.getResourcesForApplication(statusResPackage);
+                } catch (NameNotFoundException e) {
+                    Log.w(TAG, "Contact status update resource package not found: "
+                            + statusResPackage);
+                    resources = null;
+                }
+            }
+
+            if (resources != null) {
+                try {
+                    labelDisplayValue = resources.getString(statusLabel.intValue());
+                } catch (NotFoundException e) {
+                    Log.w(TAG, "Contact status update resource not found: " + statusResPackage + "@"
+                            + statusLabel.intValue());
+                }
+            }
+        }
+
+        final CharSequence attribution;
+        if (timestampDisplayValue != null && labelDisplayValue != null) {
+            attribution = getContext().getString(
+                    R.string.contact_status_update_attribution_with_date,
+                    timestampDisplayValue, labelDisplayValue);
+        } else if (timestampDisplayValue == null && labelDisplayValue != null) {
+            attribution = getContext().getString(
+                    R.string.contact_status_update_attribution,
+                    labelDisplayValue);
+        } else if (timestampDisplayValue != null) {
+            attribution = timestampDisplayValue;
+        } else {
+            attribution = null;
+        }
+        setStatusAttribution(attribution);
+    }
+
+    /**
+     * Convenience method for binding all available data from an existing
+     * contact.
+     *
+     * @param contactLookupUri a {Contacts.CONTENT_LOOKUP_URI} style URI.
+     */
+    public void bindFromContactLookupUri(Uri contactLookupUri) {
+        bindFromContactUriInternal(contactLookupUri, true /* reset query handler */);
+    }
+
+    /**
+     * Convenience method for binding all available data from an existing
+     * contact.
+     *
+     * @param contactUri a {Contacts.CONTENT_URI} style URI.
+     * @param resetQueryHandler whether to use a new AsyncQueryHandler or not.
+     */
+    private void bindFromContactUriInternal(Uri contactUri, boolean resetQueryHandler) {
+        mContactUri = contactUri;
+        startContactQuery(contactUri, resetQueryHandler);
+    }
+
+    /**
+     * 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) {
+        resetAsyncQueryHandler();
+
+        mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, emailAddress,
+                Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(emailAddress)),
+                EMAIL_LOOKUP_PROJECTION, null, null, null);
+    }
+
+    /**
+     * 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) {
+        resetAsyncQueryHandler();
+
+        mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, number,
+                Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)),
+                PHONE_LOOKUP_PROJECTION, null, null, null);
+    }
+
+    /**
+     * startContactQuery
+     *
+     * internal method to query contact by Uri.
+     *
+     * @param contactUri the contact uri
+     * @param resetQueryHandler whether to use a new AsyncQueryHandler or not
+     */
+    private void startContactQuery(Uri contactUri, boolean resetQueryHandler) {
+        if (resetQueryHandler) {
+            resetAsyncQueryHandler();
+        }
+
+        mQueryHandler.startQuery(TOKEN_CONTACT_INFO, contactUri, contactUri, ContactQuery.COLUMNS,
+                null, null, null);
+    }
+
+    /**
+     * startPhotoQuery
+     *
+     * internal method to query contact photo by photo id and uri.
+     *
+     * @param photoId the photo id.
+     * @param lookupKey the lookup uri.
+     * @param resetQueryHandler whether to use a new AsyncQueryHandler or not.
+     */
+    protected void startPhotoQuery(long photoId, Uri lookupKey, boolean resetQueryHandler) {
+        if (resetQueryHandler) {
+            resetAsyncQueryHandler();
+        }
+
+        mQueryHandler.startQuery(TOKEN_PHOTO_QUERY, lookupKey,
+                ContentUris.withAppendedId(Data.CONTENT_URI, photoId), PhotoQuery.COLUMNS,
+                null, null, null);
+    }
+
+    /**
+     * Method to force this widget to forget everything it knows about the contact.
+     * We need to stop any existing async queries for phone, email, contact, and photos.
+     */
+    public void wipeClean() {
+        resetAsyncQueryHandler();
+
+        setDisplayName(null, null);
+        setPhoto(loadPlaceholderPhoto(null));
+        setSocialSnippet(null);
+        setPresence(0);
+        mContactUri = null;
+        mExcludeMimes = null;
+    }
+
+
+    private void resetAsyncQueryHandler() {
+        // the api AsyncQueryHandler.cancelOperation() doesn't really work. Since we really
+        // need the old async queries to be cancelled, let's do it the hard way.
+        mQueryHandler = new QueryHandler(mContentResolver);
+    }
+
+    /**
+     * Bind the contact details provided by the given {@link Cursor}.
+     */
+    protected void bindContactInfo(Cursor c) {
+        final String displayName = c.getString(ContactQuery.DISPLAY_NAME);
+        final String phoneticName = c.getString(ContactQuery.PHONETIC_NAME);
+        this.setDisplayName(displayName, phoneticName);
+
+        //Set the presence status
+        if (!c.isNull(ContactQuery.CONTACT_PRESENCE_STATUS)) {
+            int presence = c.getInt(ContactQuery.CONTACT_PRESENCE_STATUS);
+            setPresence(presence);
+            showPresence(true);
+        } else {
+            showPresence(false);
+        }
+
+        //Set the status update
+        final String status = c.getString(ContactQuery.CONTACT_STATUS);
+        final Long statusTimestamp = c.isNull(ContactQuery.CONTACT_STATUS_TIMESTAMP)
+                ? null
+                : c.getLong(ContactQuery.CONTACT_STATUS_TIMESTAMP);
+        final Integer statusLabel = c.isNull(ContactQuery.CONTACT_STATUS_LABEL)
+                ? null
+                : c.getInt(ContactQuery.CONTACT_STATUS_LABEL);
+        final String statusResPackage = c.getString(ContactQuery.CONTACT_STATUS_RES_PACKAGE);
+
+        setStatus(status, statusTimestamp, statusLabel, statusResPackage);
+    }
+
+    public void onClick(View view) {
+        switch (view.getId()) {
+            case R.id.photo: {
+                performPhotoClick();
+                break;
+            }
+            case R.id.name: {
+                performDisplayNameClick();
+                break;
+            }
+        }
+    }
+
+    private Bitmap loadPlaceholderPhoto(BitmapFactory.Options options) {
+        if (mNoPhotoResource == 0) {
+            return null;
+        }
+        return BitmapFactory.decodeResource(mContext.getResources(),
+                mNoPhotoResource, options);
+    }
+}
diff --git a/LoaderApp/src/com/android/loaderapp/fragments/ContactFragment.java b/LoaderApp/src/com/android/loaderapp/fragments/ContactFragment.java
index f88d160..672d438 100644
--- a/LoaderApp/src/com/android/loaderapp/fragments/ContactFragment.java
+++ b/LoaderApp/src/com/android/loaderapp/fragments/ContactFragment.java
@@ -16,7 +16,7 @@
 
 package com.android.loaderapp.fragments;
 
-import com.android.internal.widget.ContactHeaderWidget;
+import com.android.loaderapp.ContactHeaderWidget;
 import com.android.loaderapp.R;
 import com.android.loaderapp.model.Collapser;
 import com.android.loaderapp.model.ContactLoader;
@@ -158,7 +158,6 @@
         mInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
         mContactHeaderWidget = (ContactHeaderWidget) view.findViewById(R.id.contact_header_widget);
-        mContactHeaderWidget.showStar(true);
         mContactHeaderWidget.setExcludeMimes(new String[] { Contacts.CONTENT_ITEM_TYPE });
 
         mListView = (ListView) view.findViewById(android.R.id.list);