New layout of the contact widget

Change-Id: Id440fb606fd5ddc6167af2b1491426080980a9ff
diff --git a/res/drawable-hdpi/statusbox_attribute_holo.9.png b/res/drawable-hdpi/statusbox_attribute_holo.9.png
new file mode 100644
index 0000000..13ffd7a
--- /dev/null
+++ b/res/drawable-hdpi/statusbox_attribute_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/statusbox_attribute_holo.9.png b/res/drawable-mdpi/statusbox_attribute_holo.9.png
new file mode 100644
index 0000000..b124392
--- /dev/null
+++ b/res/drawable-mdpi/statusbox_attribute_holo.9.png
Binary files differ
diff --git a/res/layout/social_widget.xml b/res/layout/social_widget.xml
index 54ece47..5adccf1 100644
--- a/res/layout/social_widget.xml
+++ b/res/layout/social_widget.xml
@@ -41,7 +41,7 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
     </FrameLayout>
-    <FrameLayout
+    <RelativeLayout
         android:layout_width="0dip"
         android:layout_height="match_parent"
         android:layout_weight="1"
@@ -51,44 +51,38 @@
         android:layout_marginBottom="6dip"
         android:layout_marginLeft="0dip"
         android:paddingLeft="47dip"
-        android:paddingRight="8dip">
+        android:paddingRight="8dip"
+        android:paddingTop="3dip"
+        android:paddingBottom="6dip">
 
-        <LinearLayout
+        <TextView
+            android:id="@+id/name_and_snippet"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:maxLines="3"
+            android:lineSpacingExtra="2sp"
+            android:textColor="#FFFFFFFF"
+            android:textSize="@dimen/widget_text_size_snippet" />
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:textColor="#FFFFFFFF"
+            android:textSize="@dimen/widget_text_size_name" />
+
+        <TextView
+            android:id="@+id/status_date"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="left|center_vertical"
-            android:orientation="vertical"
-            android:paddingTop="2dip"
-            android:paddingBottom="2dip"
-            android:paddingRight="4dip">
-
-            <TextView
-                android:id="@+id/name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:ellipsize="end"
-                android:textSize="14sp"
-                android:textStyle="bold"
-                android:textColor="#FFFFFFFF" />
-
-            <TextView
-                android:id="@+id/status"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:textSize="13sp"
-                android:textColor="#FFFFFFFF"
-                android:maxLines="1"
-                android:ellipsize="end"
-                android:visibility="gone" />
-
-            <TextView
-                android:id="@+id/status_date"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:textSize="13sp"
-                android:textColor="#FF444444"
-                android:visibility="gone" />
-        </LinearLayout>
-    </FrameLayout>
+            android:layout_alignParentRight="true"
+            android:layout_alignParentBottom="true"
+            android:background="@drawable/statusbox_attribute_holo"
+            android:textSize="13sp"
+            android:textColor="#FF444444"
+            android:visibility="gone" />
+    </RelativeLayout>
 </LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 00f5134..fdf2b4a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -48,4 +48,10 @@
 
     <!-- Padding to be used between a visible scrollbar and the contact list -->
     <dimen name="list_visible_scrollbar_padding">56dip</dimen>
+
+    <!-- Font size used for the contact name in the widget -->
+    <dimen name="widget_text_size_name">14sp</dimen>
+
+    <!-- Font size used for the social status in the widget -->
+    <dimen name="widget_text_size_snippet">13sp</dimen>
 </resources>
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetConfigureActivity.java b/src/com/android/contacts/socialwidget/SocialWidgetConfigureActivity.java
index 0d63036..7bfca98 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetConfigureActivity.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetConfigureActivity.java
@@ -49,7 +49,8 @@
             SocialWidgetSettings.getInstance().setContactUri(context, widgetId, data.getData());
 
             // Update the widget
-            SocialWidgetProvider.loadWidgetData(context, widgetId);
+            SocialWidgetProvider.loadWidgetData(
+                    context, AppWidgetManager.getInstance(this), widgetId);
 
             // Return OK so that the system won't remove the widget
             final Intent resultValue = new Intent();
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index d5cf861..0c132b7 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -28,9 +28,13 @@
 import android.content.Loader;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Typeface;
 import android.net.Uri;
 import android.provider.ContactsContract.QuickContact;
+import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.StyleSpan;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.View;
@@ -38,6 +42,13 @@
 
 public class SocialWidgetProvider extends AppWidgetProvider {
     private static final String TAG = "SocialWidgetProvider";
+
+    /**
+     * Max length of a snippet that is considered "short" and displayed in
+     * a separate line.
+     */
+    private static final int SHORT_SNIPPET_LENGTH = 48;
+
     private static SparseArray<ContactLoader> sLoaders = new SparseArray<ContactLoader>();
 
     @Override
@@ -47,7 +58,7 @@
         }
 
         for (int appWidgetId : appWidgetIds) {
-            loadWidgetData(context, appWidgetId);
+            loadWidgetData(context, appWidgetManager, appWidgetId);
         }
     }
 
@@ -64,19 +75,21 @@
         SocialWidgetSettings.getInstance().remove(context, appWidgetIds);
     }
 
-    public static void loadWidgetData(final Context context, final int widgetId) {
+    public static void loadWidgetData(
+            final Context context, final AppWidgetManager appWidgetManager, final int widgetId) {
         final ContactLoader previousLoader = sLoaders.get(widgetId);
 
         if (previousLoader != null) {
             previousLoader.startLoading();
         } else {
             // Show that we are loading
-            final AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
             final RemoteViews loadingViews =
                     new RemoteViews(context.getPackageName(), R.layout.social_widget);
             loadingViews.setTextViewText(R.id.name,
                     context.getString(R.string.social_widget_loading));
-            widgetManager.updateAppWidget(widgetId, loadingViews);
+            loadingViews.setViewVisibility(R.id.name, View.VISIBLE);
+            loadingViews.setViewVisibility(R.id.name_and_snippet, View.GONE);
+            appWidgetManager.updateAppWidget(widgetId, loadingViews);
 
             // Load
             final Uri contactUri =
@@ -91,89 +104,106 @@
                         @Override
                         public void onLoadComplete(Loader<ContactLoader.Result> loader,
                                 ContactLoader.Result contactData) {
-                            if (contactData == ContactLoader.Result.ERROR ||
-                                    contactData == ContactLoader.Result.NOT_FOUND) {
-                                return;
-                            }
-                            Log.d(TAG, "Loaded " + contactData.getLookupKey()
-                                    + " for widget with id=" + widgetId);
-                            final RemoteViews views = new RemoteViews(context.getPackageName(),
-                                    R.layout.social_widget);
-
-                            setDisplayName(views, contactData.getDisplayName(),
-                                    contactData.getPhoneticName());
-
-                            byte[] photo = contactData.getPhotoBinaryData();
-                            setPhoto(views, photo != null
-                                    ? BitmapFactory.decodeByteArray(photo, 0, photo.length)
-                                    : ContactBadgeUtil.loadPlaceholderPhoto(context));
-                            setSocialSnippet(views, contactData.getSocialSnippet());
-                            setStatusAttribution(views, ContactBadgeUtil.getSocialDate(
-                                    contactData, context));
-
-                            // OnClick launch QuickContact
-                            final Intent intent = new Intent(QuickContact.ACTION_QUICK_CONTACT);
-                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                                    | Intent.FLAG_ACTIVITY_CLEAR_TOP
-                                    | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
-                            intent.setData(contactData.getLookupUri());
-                            intent.putExtra(QuickContact.EXTRA_MODE, QuickContact.MODE_SMALL);
-
-                            final PendingIntent pendingIntent = PendingIntent.getActivity(context,
-                                    0, intent, 0);
-                            views.setOnClickPendingIntent(R.id.border, pendingIntent);
-
-                            // Configure Ui
-                            widgetManager.updateAppWidget(widgetId, views);
-                        }
-
-                        private void setPhoto(RemoteViews views, Bitmap photo) {
-                            views.setImageViewBitmap(R.id.image, photo);
-                        }
-
-                        /**
-                         * Set the display name and phonetic name to show in the header.
-                         */
-                        private void setDisplayName(RemoteViews views, CharSequence displayName,
-                                CharSequence phoneticName) {
-                            if (TextUtils.isEmpty(phoneticName)) {
-                                views.setTextViewText(R.id.name, displayName);
-                            } else {
-                                final String combinedName =
-                                        context.getString(R.string.widget_name_and_phonetic,
-                                        displayName, phoneticName);
-                                views.setTextViewText(R.id.name, combinedName);
-                            }
-                        }
-
-                        /**
-                         * Set the social snippet text to display in the header.
-                         */
-                        private void setSocialSnippet(RemoteViews views, CharSequence snippet) {
-                            if (TextUtils.isEmpty(snippet)) {
-                                views.setViewVisibility(R.id.status, View.GONE);
-                            } else {
-                                views.setTextViewText(R.id.status, snippet);
-                                views.setViewVisibility(R.id.status, View.VISIBLE);
-                            }
-                        }
-
-                        /**
-                         * Set the status attribution text to display in the header.
-                         */
-                        private void setStatusAttribution(RemoteViews views,
-                                CharSequence attribution) {
-                            if (attribution == null) {
-                                views.setViewVisibility(R.id.status_date, View.GONE);
-                            } else {
-                                views.setTextViewText(R.id.status_date, attribution);
-                                views.setViewVisibility(R.id.status_date, View.VISIBLE);
-                            }
+                            bindRemoteViews(context, widgetId, appWidgetManager, contactData);
                         }
                     });
             contactLoader.startLoading();
             sLoaders.append(widgetId, contactLoader);
         }
     }
+
+    private static void bindRemoteViews(final Context context, final int widgetId,
+            final AppWidgetManager widgetManager, ContactLoader.Result contactData) {
+        if (contactData == ContactLoader.Result.ERROR ||
+                contactData == ContactLoader.Result.NOT_FOUND) {
+            return;
+        }
+
+        Log.d(TAG, "Loaded " + contactData.getLookupKey()
+                + " for widget with id=" + widgetId);
+        final RemoteViews views = new RemoteViews(context.getPackageName(),
+                R.layout.social_widget);
+
+        setDisplayNameAndSnippet(context, views, contactData.getDisplayName(),
+                contactData.getPhoneticName(), contactData.getSocialSnippet());
+
+        byte[] photo = contactData.getPhotoBinaryData();
+        setPhoto(views, photo != null
+                ? BitmapFactory.decodeByteArray(photo, 0, photo.length)
+                : ContactBadgeUtil.loadPlaceholderPhoto(context));
+        setStatusAttribution(views, ContactBadgeUtil.getSocialDate(
+                contactData, context));
+
+        // OnClick launch QuickContact
+        final Intent intent = new Intent(QuickContact.ACTION_QUICK_CONTACT);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        intent.setData(contactData.getLookupUri());
+        intent.putExtra(QuickContact.EXTRA_MODE, QuickContact.MODE_SMALL);
+
+        final PendingIntent pendingIntent = PendingIntent.getActivity(context,
+                0, intent, 0);
+        views.setOnClickPendingIntent(R.id.border, pendingIntent);
+
+        // Configure UI
+        widgetManager.updateAppWidget(widgetId, views);
+    }
+
+
+    private static void setPhoto(RemoteViews views, Bitmap photo) {
+        views.setImageViewBitmap(R.id.image, photo);
+    }
+
+    /**
+     * Set the display name, phonetic name and the social snippet.
+     */
+    private static void setDisplayNameAndSnippet(Context context, RemoteViews views,
+            CharSequence displayName, CharSequence phoneticName,
+            CharSequence snippet) {
+        SpannableStringBuilder sb = new SpannableStringBuilder();
+
+        CharSequence name = displayName;
+        if (!TextUtils.isEmpty(phoneticName)) {
+            name = context.getString(R.string.widget_name_and_phonetic,
+                    name, phoneticName);
+        }
+        sb.append(name);
+
+        AbsoluteSizeSpan sizeSpan = new AbsoluteSizeSpan(
+                context.getResources().getDimensionPixelSize(R.dimen.widget_text_size_name));
+        StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
+        sb.setSpan(sizeSpan, 0, name.length(), 0);
+        sb.setSpan(styleSpan, 0, name.length(), 0);
+
+        if (TextUtils.isEmpty(snippet)) {
+            views.setTextViewText(R.id.name, sb);
+            views.setViewVisibility(R.id.name, View.VISIBLE);
+            views.setViewVisibility(R.id.name_and_snippet, View.GONE);
+        } else {
+            if (snippet.length() <= SHORT_SNIPPET_LENGTH) {
+                sb.append("\n");
+            } else {
+                sb.append("  ");
+            }
+            sb.append(snippet);
+            views.setTextViewText(R.id.name_and_snippet, sb);
+            views.setViewVisibility(R.id.name, View.GONE);
+            views.setViewVisibility(R.id.name_and_snippet, View.VISIBLE);
+        }
+    }
+
+    /**
+     * Set the status attribution text to display in the header.
+     */
+    private static void setStatusAttribution(RemoteViews views,
+            CharSequence attribution) {
+        if (attribution == null) {
+            views.setViewVisibility(R.id.status_date, View.GONE);
+        } else {
+            views.setTextViewText(R.id.status_date, attribution);
+            views.setViewVisibility(R.id.status_date, View.VISIBLE);
+        }
+    }
 }