Merge "Import translations. DO NOT MERGE"
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 0222a89..ba502c7 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -32,6 +32,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -118,7 +119,9 @@
import com.google.common.collect.Iterables;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -700,6 +703,15 @@
// secondary=false for this field, and tweak the weight
// of its DataKind.)
} else if (dataItem instanceof EventDataItem && hasData) {
+ final Calendar cal = DateUtils.parseDate(entry.data, false);
+ if (cal != null) {
+ final Date nextAnniversary =
+ DateUtils.getNextAnnualDate(cal);
+ final Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
+ builder.appendPath("time");
+ ContentUris.appendId(builder, nextAnniversary.getTime());
+ entry.intent = new Intent(Intent.ACTION_VIEW).setData(builder.build());
+ }
entry.data = DateUtils.formatDate(mContext, entry.data);
entry.uri = null;
mEventEntries.add(entry);
@@ -1680,6 +1692,12 @@
views.data.setText(entry.data);
setMaxLines(views.data, entry.maxLines);
+ // Gray out the data item if it does not perform an action when clicked
+ if (entry.intent == null) {
+ ((TextView) view.findViewById(R.id.data)).setTextColor(
+ getResources().getColor(R.color.secondary_text_color));
+ }
+
// Set the default contact method
views.primaryIndicator.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
diff --git a/src/com/android/contacts/editor/EventFieldEditorView.java b/src/com/android/contacts/editor/EventFieldEditorView.java
index 0925add..3589c89 100644
--- a/src/com/android/contacts/editor/EventFieldEditorView.java
+++ b/src/com/android/contacts/editor/EventFieldEditorView.java
@@ -203,44 +203,26 @@
final boolean isYearOptional = getType().isYearOptional();
final int oldYear, oldMonth, oldDay;
+
if (TextUtils.isEmpty(oldValue)) {
// Default to the current date
oldYear = defaultYear;
oldMonth = calendar.get(Calendar.MONTH);
oldDay = calendar.get(Calendar.DAY_OF_MONTH);
} else {
- final ParsePosition position = new ParsePosition(0);
// Try parsing with year
- Date date1 = kind.dateFormatWithYear.parse(oldValue, position);
- if (date1 == null) {
- // If that format does not fit, try guessing the right format
- date1 = DateUtils.parseDate(oldValue);
- }
- if (date1 != null) {
- calendar.setTime(date1);
- oldYear = calendar.get(Calendar.YEAR);
- oldMonth = calendar.get(Calendar.MONTH);
- oldDay = calendar.get(Calendar.DAY_OF_MONTH);
- } else {
- // 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;
+ Calendar cal = DateUtils.parseDate(oldValue, false);
+ if (cal != null) {
+ if (DateUtils.isYearSet(cal)) {
+ oldYear = cal.get(Calendar.YEAR);
} 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);
+ //cal.set(Calendar.YEAR, 0);
oldYear = isYearOptional ? DatePickerDialog.NO_YEAR : defaultYear;
- oldMonth = calendar.get(Calendar.MONTH);
- oldDay = calendar.get(Calendar.DAY_OF_MONTH);
}
+ oldMonth = cal.get(Calendar.MONTH);
+ oldDay = cal.get(Calendar.DAY_OF_MONTH);
+ } else {
+ return null;
}
}
final OnDateSetListener callBack = new OnDateSetListener() {
@@ -263,7 +245,6 @@
} else {
resultString = kind.dateFormatWithYear.format(outCalendar.getTime());
}
-
onFieldChanged(column, resultString);
rebuildDateView();
}
diff --git a/src/com/android/contacts/util/DateUtils.java b/src/com/android/contacts/util/DateUtils.java
index dc9027c..e20aadf 100644
--- a/src/com/android/contacts/util/DateUtils.java
+++ b/src/com/android/contacts/util/DateUtils.java
@@ -25,6 +25,7 @@
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
+import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
@@ -61,30 +62,69 @@
}
/**
- * Parses the supplied string to see if it looks like a date. If so,
- * returns the date. Otherwise, returns null.
+ * Parses the supplied string to see if it looks like a date.
+ *
+ * @param string The string representation of the provided date
+ * @param mustContainYear If true, the string is parsed as a date containing a year. If false,
+ * the string is parsed into a valid date even if the year field is missing.
+ * @return A Calendar object corresponding to the date if the string is successfully parsed.
+ * If not, null is returned.
*/
- public static Date parseDate(String string) {
+ public static Calendar parseDate(String string, boolean mustContainYear) {
ParsePosition parsePosition = new ParsePosition(0);
+ Date date;
+ if (!mustContainYear) {
+ final boolean noYearParsed;
+ // Unfortunately, we can't parse Feb 29th correctly, so let's handle this day seperately
+ if (NO_YEAR_DATE_FEB29TH.equals(string)) {
+ return getUtcDate(0, Calendar.FEBRUARY, 29);
+ } else {
+ synchronized (CommonDateUtils.NO_YEAR_DATE_FORMAT) {
+ date = CommonDateUtils.NO_YEAR_DATE_FORMAT.parse(string, parsePosition);
+ }
+ noYearParsed = parsePosition.getIndex() == string.length();
+ }
+
+ if (noYearParsed) {
+ return getUtcDate(date, true);
+ }
+ }
for (int i = 0; i < DATE_FORMATS.length; i++) {
SimpleDateFormat f = DATE_FORMATS[i];
synchronized (f) {
parsePosition.setIndex(0);
- Date date = f.parse(string, parsePosition);
+ date = f.parse(string, parsePosition);
if (parsePosition.getIndex() == string.length()) {
- return date;
+ return getUtcDate(date, false);
}
}
}
return null;
}
- private static final Date getUtcDate(int year, int month, int dayOfMonth) {
+ private static final Calendar getUtcDate(Date date, boolean noYear) {
final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE, Locale.US);
+ calendar.setTime(date);
+ if (noYear) {
+ calendar.set(Calendar.YEAR, 0);
+ }
+ return calendar;
+ }
+
+ private static final Calendar getUtcDate(int year, int month, int dayOfMonth) {
+ final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE, Locale.US);
+ calendar.clear();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
- return calendar.getTime();
+ return calendar;
+ }
+
+ public static boolean isYearSet(Calendar cal) {
+ // use the Calendar.YEAR field to track whether or not the year is set instead of
+ // Calendar.isSet() because doing Calendar.get() causes Calendar.isSet() to become
+ // true irregardless of what the previous value was
+ return cal.get(Calendar.YEAR) > 1;
}
/**
@@ -120,46 +160,26 @@
if (string.length() == 0) {
return string;
}
+ final Calendar cal = parseDate(string, false);
- ParsePosition parsePosition = new ParsePosition(0);
+ // we weren't able to parse the string successfully so just return it unchanged
+ if (cal == null) {
+ return string;
+ }
- final boolean noYearParsed;
- Date date;
-
- // 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;
+ final boolean isYearSet = isYearSet(cal);
+ final java.text.DateFormat outFormat;
+ if (!isYearSet) {
+ outFormat = getLocalizedDateFormatWithoutYear(context);
} else {
- synchronized (CommonDateUtils.NO_YEAR_DATE_FORMAT) {
- date = CommonDateUtils.NO_YEAR_DATE_FORMAT.parse(string, parsePosition);
- }
- noYearParsed = parsePosition.getIndex() == string.length();
+ outFormat =
+ longForm ? DateFormat.getLongDateFormat(context) :
+ DateFormat.getDateFormat(context);
}
-
- if (noYearParsed) {
- final java.text.DateFormat outFormat = getLocalizedDateFormatWithoutYear(context);
- synchronized (outFormat) {
- outFormat.setTimeZone(UTC_TIMEZONE);
- return outFormat.format(date);
- }
+ synchronized (outFormat) {
+ outFormat.setTimeZone(UTC_TIMEZONE);
+ return outFormat.format(cal.getTime());
}
-
- for (int i = 0; i < DATE_FORMATS.length; i++) {
- SimpleDateFormat f = DATE_FORMATS[i];
- synchronized (f) {
- parsePosition.setIndex(0);
- date = f.parse(string, parsePosition);
- if (parsePosition.getIndex() == string.length()) {
- final java.text.DateFormat outFormat =
- longForm ? DateFormat.getLongDateFormat(context) :
- DateFormat.getDateFormat(context);
- outFormat.setTimeZone(UTC_TIMEZONE);
- return outFormat.format(date);
- }
- }
- }
- return string;
}
public static boolean isMonthBeforeDay(Context context) {
@@ -197,4 +217,56 @@
DateUtils.isMonthBeforeDay(context) ? "MMMM dd" : "dd MMMM");
}
}
+
+ /**
+ * Given a calendar (possibly containing only a day of the year), returns the earliest possible
+ * anniversary of the date that is equal to or after the current point in time if the date
+ * does not contain a year, or the date converted to the local time zone (if the date contains
+ * a year.
+ *
+ * @param target The date we wish to convert(in the UTC time zone).
+ * @return If date does not contain a year (year < 1900), returns the next earliest anniversary
+ * that is after the current point in time (in the local time zone). Otherwise, returns the
+ * adjusted Date in the local time zone.
+ */
+ public static Date getNextAnnualDate(Calendar target) {
+ final Calendar today = Calendar.getInstance();
+ today.setTime(new Date());
+
+ // Round the current time to the exact start of today so that when we compare
+ // today against the target date, both dates are set to exactly 0000H.
+ today.set(Calendar.HOUR_OF_DAY, 0);
+ today.set(Calendar.MINUTE, 0);
+ today.set(Calendar.SECOND, 0);
+ today.set(Calendar.MILLISECOND, 0);
+
+ final boolean isYearSet = isYearSet(target);
+ final int targetYear = target.get(Calendar.YEAR);
+ final int targetMonth = target.get(Calendar.MONTH);
+ final int targetDay = target.get(Calendar.DAY_OF_MONTH);
+ final boolean isFeb29 = (targetMonth == Calendar.FEBRUARY && targetDay == 29);
+ final GregorianCalendar anniversary = new GregorianCalendar();
+ // Convert from the UTC date to the local date. Set the year to today's year if the
+ // there is no provided year (targetYear < 1900)
+ anniversary.set(!isYearSet ? today.get(Calendar.YEAR) : targetYear,
+ targetMonth, targetDay);
+ // If the anniversary's date is before the start of today and there is no year set,
+ // increment the year by 1 so that the returned date is always equal to or greater than
+ // today. If the day is a leap year, keep going until we get the next leap year anniversary
+ // Otherwise if there is already a year set, simply return the exact date.
+ if (!isYearSet) {
+ int anniversaryYear = today.get(Calendar.YEAR);
+ if (anniversary.before(today) ||
+ (isFeb29 && !anniversary.isLeapYear(anniversaryYear))) {
+ // If the target date is not Feb 29, then set the anniversary to the next year.
+ // Otherwise, keep going until we find the next leap year (this is not guaranteed
+ // to be in 4 years time).
+ do {
+ anniversaryYear +=1;
+ } while (isFeb29 && !anniversary.isLeapYear(anniversaryYear));
+ anniversary.set(anniversaryYear, targetMonth, targetDay);
+ }
+ }
+ return anniversary.getTime();
+ }
}