am 27d4616f: am b8f69516: Merge "Fix support for birthday on February 29th with year omitted" into jb-mr1-dev

* commit '27d4616f291e323dc1a20ebec935f2ac64de414e':
  Fix support for birthday on February 29th with year omitted
diff --git a/src/com/android/contacts/datepicker/DatePicker.java b/src/com/android/contacts/datepicker/DatePicker.java
index ef91c95..39bbd8d 100644
--- a/src/com/android/contacts/datepicker/DatePicker.java
+++ b/src/com/android/contacts/datepicker/DatePicker.java
@@ -54,6 +54,8 @@
  */
 @Widget
 public class DatePicker extends FrameLayout {
+    /** Magic year that represents "no year" */
+    public static int NO_YEAR = 0;
 
     private static final int DEFAULT_START_YEAR = 1900;
     private static final int DEFAULT_END_YEAR = 2100;
@@ -83,7 +85,7 @@
 
         /**
          * @param view The view associated with this listener.
-         * @param year The year that was set.
+         * @param year The year that was set or {@link DatePicker#NO_YEAR} if no year was set
          * @param monthOfYear The month that was set (0-11) for compatibility
          *  with {@link java.util.Calendar}.
          * @param dayOfMonth The day of the month that was set.
@@ -279,7 +281,7 @@
 
     public void updateDate(int year, int monthOfYear, int dayOfMonth) {
         if (mYear != year || mMonth != monthOfYear || mDay != dayOfMonth) {
-            mYear = (mYearOptional && year == 0) ? getCurrentYear() : year;
+            mYear = (mYearOptional && year == NO_YEAR) ? getCurrentYear() : year;
             mMonth = monthOfYear;
             mDay = dayOfMonth;
             updateSpinners();
@@ -413,7 +415,7 @@
 
     /**
      * Initialize the state.
-     * @param year The initial year or 0 if no year has been specified
+     * @param year The initial year or {@link #NO_YEAR} if no year has been specified
      * @param monthOfYear The initial month.
      * @param dayOfMonth The initial day of the month.
      * @param yearOptional True if the user can toggle the year
@@ -421,11 +423,11 @@
      */
     public void init(int year, int monthOfYear, int dayOfMonth, boolean yearOptional,
             OnDateChangedListener onDateChangedListener) {
-        mYear = (yearOptional && year == 0) ? getCurrentYear() : year;
+        mYear = (yearOptional && year == NO_YEAR) ? getCurrentYear() : year;
         mMonth = monthOfYear;
         mDay = dayOfMonth;
         mYearOptional = yearOptional;
-        mHasYear = yearOptional ? (year != 0) : true;
+        mHasYear = yearOptional ? (year != NO_YEAR) : true;
         mOnDateChangedListener = onDateChangedListener;
         updateSpinners();
     }
@@ -454,7 +456,7 @@
     }
 
     public int getYear() {
-        return (mYearOptional && !mHasYear) ? 0 : mYear;
+        return (mYearOptional && !mHasYear) ? NO_YEAR : mYear;
     }
 
     public boolean isYearOptional() {
@@ -482,7 +484,7 @@
 
     private void notifyDateChanged() {
         if (mOnDateChangedListener != null) {
-            int year = (mYearOptional && !mHasYear) ? 0 : mYear;
+            int year = (mYearOptional && !mHasYear) ? NO_YEAR : mYear;
             mOnDateChangedListener.onDateChanged(DatePicker.this, year, mMonth, mDay);
         }
     }
diff --git a/src/com/android/contacts/datepicker/DatePickerDialog.java b/src/com/android/contacts/datepicker/DatePickerDialog.java
index 9c7cee6..14ebd3b 100644
--- a/src/com/android/contacts/datepicker/DatePickerDialog.java
+++ b/src/com/android/contacts/datepicker/DatePickerDialog.java
@@ -47,6 +47,9 @@
 public class DatePickerDialog extends AlertDialog implements OnClickListener,
         OnDateChangedListener {
 
+    /** Magic year that represents "no year" */
+    public static int NO_YEAR = DatePicker.NO_YEAR;
+
     private static final String YEAR = "year";
     private static final String MONTH = "month";
     private static final String DAY = "day";
@@ -54,7 +57,6 @@
 
     private final DatePicker mDatePicker;
     private final OnDateSetListener mCallBack;
-    private final Calendar mCalendar;
     private final DateFormat mTitleDateFormat;
     private final DateFormat mTitleNoYearDateFormat;
 
@@ -68,7 +70,8 @@
     public interface OnDateSetListener {
         /**
          * @param view The view associated with this listener.
-         * @param year The year that was set or 0 if the user has not specified a year
+         * @param year The year that was set or {@link DatePickerDialog#NO_YEAR} if the user has
+         *  not specified a year
          * @param monthOfYear The month that was set (0-11) for compatibility
          *  with {@link java.util.Calendar}.
          * @param dayOfMonth The day of the month that was set.
@@ -94,7 +97,8 @@
     /**
      * @param context The context the dialog is to run in.
      * @param callBack How the parent is notified that the date is set.
-     * @param year The initial year of the dialog or 0 if no year has been specified
+     * @param year The initial year of the dialog or {@link DatePickerDialog#NO_YEAR} if no year
+     *  has been specified
      * @param monthOfYear The initial month of the dialog.
      * @param dayOfMonth The initial day of the dialog.
      * @param yearOptional Whether the year can be toggled by the user
@@ -115,7 +119,8 @@
      * @param context The context the dialog is to run in.
      * @param theme the theme to apply to this dialog
      * @param callBack How the parent is notified that the date is set.
-     * @param year The initial year of the dialog or 0 if no year has been specified
+     * @param year The initial year of the dialog or {@link DatePickerDialog#NO_YEAR} if no year
+     *  has been specified
      * @param monthOfYear The initial month of the dialog.
      * @param dayOfMonth The initial day of the dialog.
      */
@@ -132,7 +137,8 @@
      * @param context The context the dialog is to run in.
      * @param theme the theme to apply to this dialog
      * @param callBack How the parent is notified that the date is set.
-     * @param year The initial year of the dialog.
+     * @param year The initial year of the dialog or {@link DatePickerDialog#NO_YEAR} if no
+     *  year has been specified.
      * @param monthOfYear The initial month of the dialog.
      * @param dayOfMonth The initial day of the dialog.
      * @param yearOptional Whether the year can be toggled by the user
@@ -154,7 +160,6 @@
         mTitleDateFormat = DateFormat.getDateInstance(DateFormat.FULL);
         mTitleNoYearDateFormat = new SimpleDateFormat(
                 DateUtils.isMonthBeforeDay(getContext()) ? "MMMM dd" : "dd MMMM");
-        mCalendar = Calendar.getInstance();
         updateTitle(mInitialYear, mInitialMonth, mInitialDay);
 
         setButton(BUTTON_POSITIVE, context.getText(com.android.internal.R.string.date_time_set),
@@ -205,12 +210,13 @@
     }
 
     private void updateTitle(int year, int month, int day) {
-        mCalendar.set(Calendar.YEAR, year);
-        mCalendar.set(Calendar.MONTH, month);
-        mCalendar.set(Calendar.DAY_OF_MONTH, day);
+        final Calendar calendar = Calendar.getInstance();
+        calendar.set(Calendar.YEAR, year);
+        calendar.set(Calendar.MONTH, month);
+        calendar.set(Calendar.DAY_OF_MONTH, day);
         final DateFormat dateFormat =
-                year == 0 ? mTitleNoYearDateFormat : mTitleDateFormat;
-        setTitle(dateFormat.format(mCalendar.getTime()));
+                year == NO_YEAR ? mTitleNoYearDateFormat : mTitleDateFormat;
+        setTitle(dateFormat.format(calendar.getTime()));
     }
 
     @Override
diff --git a/src/com/android/contacts/editor/EventFieldEditorView.java b/src/com/android/contacts/editor/EventFieldEditorView.java
index ca1bf64..77f2010 100644
--- a/src/com/android/contacts/editor/EventFieldEditorView.java
+++ b/src/com/android/contacts/editor/EventFieldEditorView.java
@@ -203,7 +203,7 @@
 
         final int oldYear, oldMonth, oldDay;
         if (TextUtils.isEmpty(oldValue)) {
-            // Default to January first, 30 years ago
+            // Default to January first of this year
             oldYear = defaultYear;
             oldMonth = 0;
             oldDay = 1;
@@ -221,13 +221,25 @@
                 oldMonth = calendar.get(Calendar.MONTH);
                 oldDay = calendar.get(Calendar.DAY_OF_MONTH);
             } else {
-                final Date date2 = kind.dateFormatWithoutYear.parse(oldValue, position);
-                // Don't understand the date, lets not change it
-                if (date2 == null) return null;
-                calendar.setTime(date2);
-                oldYear = isYearOptional ? 0 : defaultYear;
-                oldMonth = calendar.get(Calendar.MONTH);
-                oldDay = calendar.get(Calendar.DAY_OF_MONTH);
+                // Unfortunately, we need a one-off hack for February 29th, because
+                // the parse functions assume 1970, which wasn't a leap year
+                // Caveat here: This won't catch AccountTypes that allow omitting the year but
+                // require a time of the day. But as we don't have any of those at the moment,
+                // this shouldn't be an issue
+                if (DateUtils.NO_YEAR_DATE_FEB29TH.equals(oldValue)) {
+                    oldYear = isYearOptional ? DatePickerDialog.NO_YEAR : defaultYear;
+                    oldMonth = Calendar.FEBRUARY;
+                    oldDay = 29;
+                } else {
+                    final Date date2 = kind.dateFormatWithoutYear.parse(oldValue, position);
+                    // Don't understand the date? Let's not show any dialog
+                    if (date2 == null) return null;
+
+                    calendar.setTime(date2);
+                    oldYear = isYearOptional ? DatePickerDialog.NO_YEAR : defaultYear;
+                    oldMonth = calendar.get(Calendar.MONTH);
+                    oldDay = calendar.get(Calendar.DAY_OF_MONTH);
+                }
             }
         }
         final OnDateSetListener callBack = new OnDateSetListener() {
@@ -241,8 +253,8 @@
                 // The format string will ignore that year.
                 // For formats other than Exchange, the time of the day is ignored
                 outCalendar.clear();
-                outCalendar.set(year == 0 ? 2000 : year, monthOfYear, dayOfMonth,
-                        DEFAULT_HOUR, 0, 0);
+                outCalendar.set(year == DatePickerDialog.NO_YEAR ? 2000 : year, monthOfYear,
+                        dayOfMonth, DEFAULT_HOUR, 0, 0);
 
                 final String resultString;
                 if (year == 0) {
diff --git a/src/com/android/contacts/util/DateUtils.java b/src/com/android/contacts/util/DateUtils.java
index d0bb68f..89127c9 100644
--- a/src/com/android/contacts/util/DateUtils.java
+++ b/src/com/android/contacts/util/DateUtils.java
@@ -21,6 +21,7 @@
 
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
@@ -34,6 +35,11 @@
     // All the SimpleDateFormats in this class use the UTC timezone
     public static final SimpleDateFormat NO_YEAR_DATE_FORMAT =
             new SimpleDateFormat("--MM-dd", Locale.US);
+    /**
+     * When parsing a date without a year, the system assumes 1970, which wasn't a leap-year.
+     * Let's add a one-off hack for that day of the year
+     */
+    public static final String NO_YEAR_DATE_FEB29TH = "--02-29";
     public static final SimpleDateFormat FULL_DATE_FORMAT =
             new SimpleDateFormat("yyyy-MM-dd", Locale.US);
     public static final SimpleDateFormat DATE_AND_TIME_FORMAT =
@@ -88,6 +94,14 @@
         return null;
     }
 
+    private static final Date getUtcDate(int year, int month, int dayOfMonth) {
+        final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE, Locale.US);
+        calendar.set(Calendar.YEAR, year);
+        calendar.set(Calendar.MONTH, month);
+        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+        return calendar.getTime();
+    }
+
     /**
      * 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
@@ -105,13 +119,21 @@
 
         ParsePosition parsePosition = new ParsePosition(0);
 
+        final boolean noYearParsed;
         Date date;
 
-        synchronized (NO_YEAR_DATE_FORMAT) {
-            date = NO_YEAR_DATE_FORMAT.parse(string, parsePosition);
+        // Unfortunately, we can't parse Feb 29th correctly, so let's handle this day seperately
+        if (NO_YEAR_DATE_FEB29TH.equals(string)) {
+            date = getUtcDate(0, Calendar.FEBRUARY, 29);
+            noYearParsed = true;
+        } else {
+            synchronized (NO_YEAR_DATE_FORMAT) {
+                date = NO_YEAR_DATE_FORMAT.parse(string, parsePosition);
+            }
+            noYearParsed = parsePosition.getIndex() == string.length();
         }
 
-        if (parsePosition.getIndex() == string.length()) {
+        if (noYearParsed) {
             java.text.DateFormat outFormat = isMonthBeforeDay(context)
                     ? FORMAT_WITHOUT_YEAR_MONTH_FIRST
                     : FORMAT_WITHOUT_YEAR_DAY_FIRST;