Merge "Add animation for expand/collapse actions in editor"
diff --git a/res/layout-land/quickcontact_activity.xml b/res/layout-land/quickcontact_activity.xml
index dd47a0b..c398551 100644
--- a/res/layout-land/quickcontact_activity.xml
+++ b/res/layout-land/quickcontact_activity.xml
@@ -49,7 +49,7 @@
                 android:background="@color/quickcontact_tab_indicator" />
             <android.support.v4.view.ViewPager
                 android:id="@+id/item_list_pager"
-                android:background="@color/quickcontact_list_background"
+                android:background="@drawable/quickcontact_track_background"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent" />
         </LinearLayout>
diff --git a/res/layout-sw580dp-land/quickcontact_activity.xml b/res/layout-sw580dp-land/quickcontact_activity.xml
index 5c38309..230c4e0 100644
--- a/res/layout-sw580dp-land/quickcontact_activity.xml
+++ b/res/layout-sw580dp-land/quickcontact_activity.xml
@@ -45,7 +45,7 @@
                 android:background="@color/quickcontact_tab_indicator" />
             <android.support.v4.view.ViewPager
                 android:id="@+id/item_list_pager"
-                android:background="@color/quickcontact_list_background"
+                android:background="@drawable/quickcontact_track_background"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent" />
         </LinearLayout>
diff --git a/res/layout-sw580dp/quickcontact_activity.xml b/res/layout-sw580dp/quickcontact_activity.xml
index 34e932d..229974b 100644
--- a/res/layout-sw580dp/quickcontact_activity.xml
+++ b/res/layout-sw580dp/quickcontact_activity.xml
@@ -42,7 +42,7 @@
             android:background="@color/quickcontact_tab_indicator" />
         <android.support.v4.view.ViewPager
             android:id="@+id/item_list_pager"
-            android:background="@color/quickcontact_list_background"
+            android:background="@drawable/quickcontact_track_background"
             android:layout_width="match_parent"
             android:layout_height="160dip" />
     </LinearLayout>
diff --git a/res/layout-sw680dp-land/quickcontact_activity.xml b/res/layout-sw680dp-land/quickcontact_activity.xml
index 5c38309..230c4e0 100644
--- a/res/layout-sw680dp-land/quickcontact_activity.xml
+++ b/res/layout-sw680dp-land/quickcontact_activity.xml
@@ -45,7 +45,7 @@
                 android:background="@color/quickcontact_tab_indicator" />
             <android.support.v4.view.ViewPager
                 android:id="@+id/item_list_pager"
-                android:background="@color/quickcontact_list_background"
+                android:background="@drawable/quickcontact_track_background"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent" />
         </LinearLayout>
diff --git a/res/layout/quickcontact_activity.xml b/res/layout/quickcontact_activity.xml
index 7f8ae03..61d356c 100644
--- a/res/layout/quickcontact_activity.xml
+++ b/res/layout/quickcontact_activity.xml
@@ -49,6 +49,6 @@
             android:id="@+id/item_list_pager"
             android:layout_width="match_parent"
             android:layout_height="156dip"
-            android:background="@color/quickcontact_list_background"/>
+            android:background="@drawable/quickcontact_track_background"/>
     </LinearLayout>
 </view>
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index d4d5fc7..0222a89 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -112,6 +112,7 @@
 import com.android.contacts.util.DateUtils;
 import com.android.contacts.util.PhoneCapabilityTester;
 import com.android.contacts.util.StructuredPostalUtils;
+import com.android.contacts.util.UiClosables;
 import com.android.internal.telephony.ITelephony;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Iterables;
@@ -1028,9 +1029,7 @@
     }
 
     private void dismissPopupIfShown() {
-        if (mPopup != null && mPopup.isShowing()) {
-            mPopup.dismiss();
-        }
+        UiClosables.closeQuietly(mPopup);
         mPopup = null;
     }
 
diff --git a/src/com/android/contacts/detail/PhotoSelectionHandler.java b/src/com/android/contacts/detail/PhotoSelectionHandler.java
index 3f913f2..9689acc 100644
--- a/src/com/android/contacts/detail/PhotoSelectionHandler.java
+++ b/src/com/android/contacts/detail/PhotoSelectionHandler.java
@@ -46,6 +46,7 @@
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.model.RawContactDeltaList;
 import com.android.contacts.util.ContactPhotoUtils;
+import com.android.contacts.util.UiClosables;
 
 import java.io.File;
 
@@ -79,9 +80,7 @@
     }
 
     public void destroy() {
-        if (mPopup != null) {
-            mPopup.dismiss();
-        }
+        UiClosables.closeQuietly(mPopup);
     }
 
     public abstract PhotoActionListener getListener();
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index bb63b2e..916ae70 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -85,6 +85,7 @@
 import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
 import com.android.contacts.util.ContactPhotoUtils;
 import com.android.contacts.util.HelpUtils;
+import com.android.contacts.util.UiClosables;
 import com.google.common.collect.ImmutableList;
 
 import java.io.File;
@@ -290,7 +291,7 @@
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             final AggregationSuggestionView suggestionView = (AggregationSuggestionView) view;
             suggestionView.handleItemClickEvent();
-            mAggregationSuggestionPopup.dismiss();
+            UiClosables.closeQuietly(mAggregationSuggestionPopup);
             mAggregationSuggestionPopup = null;
         }
     };
@@ -330,6 +331,9 @@
     @Override
     public void onStop() {
         super.onStop();
+
+        UiClosables.closeQuietly(mAggregationSuggestionPopup);
+
         // If anything was left unsaved, save it now but keep the editor open.
         if (!getActivity().isChangingConfigurations() && mStatus == Status.EDITING) {
             save(SaveMode.RELOAD);
@@ -915,7 +919,7 @@
                     @Override
                     public void onItemClick(AdapterView<?> parent, View view, int position,
                             long id) {
-                        popup.dismiss();
+                        UiClosables.closeQuietly(popup);
                         AccountWithDataSet newAccount = adapter.getItem(position);
                         if (!newAccount.equals(currentAccount)) {
                             rebindEditorsForNewContact(currentState, currentAccount, newAccount);
@@ -1435,9 +1439,7 @@
             return;
         }
 
-        if (mAggregationSuggestionPopup != null && mAggregationSuggestionPopup.isShowing()) {
-            mAggregationSuggestionPopup.dismiss();
-        }
+        UiClosables.closeQuietly(mAggregationSuggestionPopup);
 
         if (mAggregationSuggestionEngine.getSuggestedContactCount() == 0) {
             return;
diff --git a/src/com/android/contacts/editor/EventFieldEditorView.java b/src/com/android/contacts/editor/EventFieldEditorView.java
index 94846a0..0925add 100644
--- a/src/com/android/contacts/editor/EventFieldEditorView.java
+++ b/src/com/android/contacts/editor/EventFieldEditorView.java
@@ -121,7 +121,8 @@
     private void rebuildDateView() {
         final EditField editField = getKind().fieldList.get(0);
         final String column = editField.column;
-        String data = DateUtils.formatDate(getContext(), getEntry().getAsString(column));
+        String data = DateUtils.formatDate(getContext(), getEntry().getAsString(column),
+                false /*Use the short DateFormat to ensure that it fits inside the EditText*/);
         if (TextUtils.isEmpty(data)) {
             mDateView.setText(mNoDateString);
             mDateView.setTextColor(mSecondaryTextColor);
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index ab7895c..d150be8 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -43,6 +43,7 @@
 import com.android.contacts.model.RawContactDelta;
 import com.android.contacts.common.model.ValuesDelta;
 import com.android.contacts.model.RawContactModifier;
+import com.android.contacts.util.UiClosables;
 import com.google.common.base.Objects;
 
 import java.util.ArrayList;
@@ -280,8 +281,7 @@
 
     @Override
     public void onClick(View v) {
-        if (mPopup != null && mPopup.isShowing()) {
-            mPopup.dismiss();
+        if (UiClosables.closeQuietly(mPopup)) {
             return;
         }
 
@@ -329,10 +329,8 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mPopup != null) {
-            mPopup.dismiss();
-            mPopup = null;
-        }
+        UiClosables.closeQuietly(mPopup);
+        mPopup = null;
     }
 
     @Override
@@ -409,10 +407,8 @@
     }
 
     private void createNewGroup() {
-        if (mPopup != null) {
-            mPopup.dismiss();
-            mPopup = null;
-        }
+        UiClosables.closeQuietly(mPopup);
+        mPopup = null;
 
         GroupCreationDialogFragment.show(
                 ((Activity) getContext()).getFragmentManager(),
diff --git a/src/com/android/contacts/editor/PhotoActionPopup.java b/src/com/android/contacts/editor/PhotoActionPopup.java
index 4c7e7b7..9903880 100644
--- a/src/com/android/contacts/editor/PhotoActionPopup.java
+++ b/src/com/android/contacts/editor/PhotoActionPopup.java
@@ -26,6 +26,7 @@
 
 import com.android.contacts.R;
 import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contacts.util.UiClosables;
 
 import java.util.ArrayList;
 
@@ -122,7 +123,7 @@
                         break;
                 }
 
-                listPopupWindow.dismiss();
+                UiClosables.closeQuietly(listPopupWindow);
             }
         };
 
diff --git a/src/com/android/contacts/util/DateUtils.java b/src/com/android/contacts/util/DateUtils.java
index 72a53a0..dc9027c 100644
--- a/src/com/android/contacts/util/DateUtils.java
+++ b/src/com/android/contacts/util/DateUtils.java
@@ -52,20 +52,12 @@
         new SimpleDateFormat("yyyyMMdd'T'HHmm'Z'", Locale.US),
     };
 
-    private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_MONTH_FIRST =
-            new SimpleDateFormat("MMMM dd");
-
-    private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_DAY_FIRST =
-            new SimpleDateFormat("dd MMMM");
-
     static {
         for (SimpleDateFormat format : DATE_FORMATS) {
             format.setLenient(true);
             format.setTimeZone(UTC_TIMEZONE);
         }
         CommonDateUtils.NO_YEAR_DATE_FORMAT.setTimeZone(UTC_TIMEZONE);
-        FORMAT_WITHOUT_YEAR_MONTH_FIRST.setTimeZone(UTC_TIMEZONE);
-        FORMAT_WITHOUT_YEAR_DAY_FIRST.setTimeZone(UTC_TIMEZONE);
     }
 
     /**
@@ -96,11 +88,30 @@
     }
 
     /**
-     * Parses the supplied string to see if it looks like a date. If so,
-     * returns the same date in a cleaned-up format for the user.  Otherwise, returns
-     * the supplied string unchanged.
+     * Same as {@link #formatDate(Context context, String string, boolean longForm)}, with
+     * longForm set to {@code true} by default.
+     *
+     * @param context Valid context
+     * @param string String representation of a date to parse
+     * @return Returns the same date in a cleaned up format. If the supplied string does not look
+     * like a date, return it unchanged.
      */
+
     public static String formatDate(Context context, String string) {
+        return formatDate(context, string, true);
+    }
+
+    /**
+     * Parses the supplied string to see if it looks like a date.
+     *
+     * @param context Valid context
+     * @param string String representation of a date to parse
+     * @param longForm If true, return the date formatted into its long string representation.
+     * If false, return the date formatted using its short form representation (i.e. 12/11/2012)
+     * @return Returns the same date in a cleaned up format. If the supplied string does not look
+     * like a date, return it unchanged.
+     */
+    public static String formatDate(Context context, String string, boolean longForm) {
         if (string == null) {
             return null;
         }
@@ -127,10 +138,9 @@
         }
 
         if (noYearParsed) {
-            java.text.DateFormat outFormat = isMonthBeforeDay(context)
-                    ? FORMAT_WITHOUT_YEAR_MONTH_FIRST
-                    : FORMAT_WITHOUT_YEAR_DAY_FIRST;
+            final java.text.DateFormat outFormat = getLocalizedDateFormatWithoutYear(context);
             synchronized (outFormat) {
+                outFormat.setTimeZone(UTC_TIMEZONE);
                 return outFormat.format(date);
             }
         }
@@ -141,9 +151,11 @@
                 parsePosition.setIndex(0);
                 date = f.parse(string, parsePosition);
                 if (parsePosition.getIndex() == string.length()) {
-                    java.text.DateFormat outFormat = DateFormat.getDateFormat(context);
-                    outFormat.setTimeZone(UTC_TIMEZONE);
-                    return outFormat.format(date);
+                    final java.text.DateFormat outFormat =
+                            longForm ? DateFormat.getLongDateFormat(context) :
+                            DateFormat.getDateFormat(context);
+                        outFormat.setTimeZone(UTC_TIMEZONE);
+                        return outFormat.format(date);
                 }
             }
         }
@@ -170,7 +182,7 @@
      * determine whether the month field should be displayed before the day field, and returns
      * either "MMMM dd" or "dd MMMM" converted into a SimpleDateFormat.
      */
-    public static SimpleDateFormat getLocalizedDateFormatWithoutYear(Context context) {
+    public static java.text.DateFormat getLocalizedDateFormatWithoutYear(Context context) {
         final String pattern = ((SimpleDateFormat) SimpleDateFormat.getDateInstance(
                 java.text.DateFormat.LONG)).toPattern();
         // Determine the correct regex pattern for year.
@@ -181,9 +193,6 @@
          // Eliminate the substring in pattern that matches the format for that of year
             return new SimpleDateFormat(pattern.replaceAll(yearPattern, ""));
         } catch (IllegalArgumentException e) {
-            // In case the new pattern isn't handled by SimpleDateFormat, fall back to the original
-            // method of constructing the SimpleDateFormat, which may not be appropriate for all
-            // locales (i.e. Germany)
             return new SimpleDateFormat(
                     DateUtils.isMonthBeforeDay(context) ? "MMMM dd" : "dd MMMM");
         }
diff --git a/src/com/android/contacts/util/UiClosables.java b/src/com/android/contacts/util/UiClosables.java
new file mode 100644
index 0000000..3f960b1
--- /dev/null
+++ b/src/com/android/contacts/util/UiClosables.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 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.contacts.util;
+
+import android.widget.ListPopupWindow;
+
+/**
+ * Methods for closing various objects
+ */
+public class UiClosables {
+
+    /**
+     * Close a {@link ListPopupWindow}.
+     *
+     * @param popup The popup window to close.
+     * @return {@code true} if the popup was showing. {@code false} otherwise.
+     */
+    public static boolean closeQuietly(ListPopupWindow popup) {
+        if (popup != null && popup.isShowing()) {
+            popup.dismiss();
+            return true;
+        }
+        return false;
+    }
+}