From 628bb61c30a8912452ea533926a74466c0674a8f Mon Sep 17 00:00:00 2001 From: David Ogutu Date: Mon, 29 Jan 2018 12:56:40 -0500 Subject: [PATCH] Fix TimePicker separator l10n issue. This was surfacing in the android clock with certain locales e.g. fr_CA. Fixed for all unqiue locale patterns. Bug: 71572309 Test: manual - changed locale to Fr_CA and made sure clock seperator WAI Test: atest CtsWidgetTestCases:TextViewTest CtsWidgetTestCases:EditTextTest CtsWidgetTestCases:TextViewFadingEdgeTest FrameworksCoreTests:TextViewFallbackLineSpacingTest FrameworksCoreTests:TextViewTest Change-Id: Ie43bf9428e8c5ef2fe2e9545cb5a6dada25d6e52 --- core/java/android/text/format/DateFormat.java | 40 ++++++++----- .../widget/TimePickerClockDelegate.java | 57 +++++++++++++++---- 2 files changed, 73 insertions(+), 24 deletions(-) diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index de2dcce5bc5df..413cd1035618e 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -431,7 +431,7 @@ public class DateFormat { int c = s.charAt(i); if (c == QUOTE) { - count = appendQuotedText(s, i, len); + count = appendQuotedText(s, i); len = s.length(); continue; } @@ -574,36 +574,48 @@ public class DateFormat { : String.format(Locale.getDefault(), "%d", year); } - private static int appendQuotedText(SpannableStringBuilder s, int i, int len) { - if (i + 1 < len && s.charAt(i + 1) == QUOTE) { - s.delete(i, i + 1); + + /** + * Strips quotation marks from the {@code formatString} and appends the result back to the + * {@code formatString}. + * + * @param formatString the format string, as described in + * {@link android.text.format.DateFormat}, to be modified + * @param index index of the first quote + * @return the length of the quoted text that was appended. + * @hide + */ + public static int appendQuotedText(SpannableStringBuilder formatString, int index) { + int length = formatString.length(); + if (index + 1 < length && formatString.charAt(index + 1) == QUOTE) { + formatString.delete(index, index + 1); return 1; } int count = 0; // delete leading quote - s.delete(i, i + 1); - len--; + formatString.delete(index, index + 1); + length--; - while (i < len) { - char c = s.charAt(i); + while (index < length) { + char c = formatString.charAt(index); if (c == QUOTE) { // QUOTEQUOTE -> QUOTE - if (i + 1 < len && s.charAt(i + 1) == QUOTE) { + if (index + 1 < length && formatString.charAt(index + 1) == QUOTE) { - s.delete(i, i + 1); - len--; + formatString.delete(index, index + 1); + length--; count++; - i++; + index++; } else { // Closing QUOTE ends quoted text copying - s.delete(i, i + 1); + formatString.delete(index, index + 1); break; } } else { - i++; + index++; count++; } } diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index 706b0ce225dc4..77670b35a1eaf 100644 --- a/core/java/android/widget/TimePickerClockDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -50,6 +50,7 @@ import com.android.internal.R; import com.android.internal.widget.NumericTextView; import com.android.internal.widget.NumericTextView.OnValueChangedListener; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Calendar; @@ -804,20 +805,56 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate { private void updateHeaderSeparator() { final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale, (mIs24Hour) ? "Hm" : "hm"); - final String separatorText; - // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats - final char[] hourFormats = {'H', 'h', 'K', 'k'}; - int hIndex = lastIndexOfAny(bestDateTimePattern, hourFormats); - if (hIndex == -1) { - // Default case - separatorText = ":"; - } else { - separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1)); - } + final String separatorText = getHourMinSeparatorFromPattern(bestDateTimePattern); mSeparatorView.setText(separatorText); mTextInputPickerView.updateSeparator(separatorText); } + /** + * This helper method extracts the time separator from the {@code datetimePattern}. + * + * The time separator is defined in the Unicode CLDR and cannot be supposed to be ":". + * + * See http://unicode.org/cldr/trac/browser/trunk/common/main + * + * @return Separator string. This is the character or set of quoted characters just after the + * hour marker in {@code dateTimePattern}. Returns a colon (:) if it can't locate the + * separator. + * + * @hide + */ + private static String getHourMinSeparatorFromPattern(String dateTimePattern) { + final String defaultSeparator = ":"; + boolean foundHourPattern = false; + for (int i = 0; i < dateTimePattern.length(); i++) { + switch (dateTimePattern.charAt(i)) { + // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats. + case 'H': + case 'h': + case 'K': + case 'k': + foundHourPattern = true; + continue; + case ' ': // skip spaces + continue; + case '\'': + if (!foundHourPattern) { + continue; + } + SpannableStringBuilder quotedSubstring = new SpannableStringBuilder( + dateTimePattern.substring(i)); + int quotedTextLength = DateFormat.appendQuotedText(quotedSubstring, 0); + return quotedSubstring.subSequence(0, quotedTextLength).toString(); + default: + if (!foundHourPattern) { + continue; + } + return Character.toString(dateTimePattern.charAt(i)); + } + } + return defaultSeparator; + } + static private int lastIndexOfAny(String str, char[] any) { final int lengthAny = any.length; if (lengthAny > 0) {