Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5926962 authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

Date/time pickers and calendar view not handling locale change.

1. The data/time pickers and calendar view do not handle locale
   change properly since they use cached Calendar instances to
   limit the GC but fail to update them when the local changes.

Change-Id: I2a92d7f4e0f2798422843123e5aa483f8044bbed
parent f81673d7
Loading
Loading
Loading
Loading
+62 −12
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import com.android.internal.R;
import android.annotation.Widget;
import android.app.Service;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
@@ -238,21 +239,11 @@ public class CalendarView extends FrameLayout {
     */
    private String[] mDayLabels;

    /**
     * Temporary instance to avoid multiple instantiations.
     */
    private Calendar mTempDate = Calendar.getInstance();

    /**
     * The first day of the week.
     */
    private int mFirstDayOfWeek;

    /**
     * The first day of the focused month.
     */
    private Calendar mFirstDayOfMonth = Calendar.getInstance();

    /**
     * Which month should be displayed/highlighted [0-11].
     */
@@ -288,21 +279,36 @@ public class CalendarView extends FrameLayout {
     */
    private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();

    /**
     * Temporary instance to avoid multiple instantiations.
     */
    private Calendar mTempDate;

    /**
     * The first day of the focused month.
     */
    private Calendar mFirstDayOfMonth;

    /**
     * The start date of the range supported by this picker.
     */
    private Calendar mMinDate = Calendar.getInstance();
    private Calendar mMinDate;

    /**
     * The end date of the range supported by this picker.
     */
    private Calendar mMaxDate = Calendar.getInstance();
    private Calendar mMaxDate;

    /**
     * Date format for parsing dates.
     */
    private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);

    /**
     * The current locale.
     */
    private Locale mCurrentLocale;

    /**
     * The callback used to indicate the user changes the date.
     */
@@ -330,6 +336,9 @@ public class CalendarView extends FrameLayout {
    public CalendarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, 0);

        // initialization based on locale
        setCurrentLocale(Locale.getDefault());

        TypedValue calendarViewStyle = new TypedValue();
        context.getTheme().resolveAttribute(R.attr.calendarViewStyle, calendarViewStyle, true);
        TypedArray attributesArray = context.obtainStyledAttributes(calendarViewStyle.resourceId,
@@ -413,6 +422,12 @@ public class CalendarView extends FrameLayout {
        return mListView.isEnabled();
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setCurrentLocale(newConfig.locale);
    }

    /**
     * Gets the minimal date supported by this {@link CalendarView} in milliseconds
     * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
@@ -623,6 +638,41 @@ public class CalendarView extends FrameLayout {
        goTo(mTempDate, animate, true, center);
    }

    /**
     * Sets the current locale.
     *
     * @param locale The current locale.
     */
    private void setCurrentLocale(Locale locale) {
        if (locale.equals(mCurrentLocale)) {
            return;
        }

        mCurrentLocale = locale;

        mTempDate = getCalendarForLocale(mTempDate, locale);
        mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
        mMinDate = getCalendarForLocale(mMinDate, locale);
        mMaxDate = getCalendarForLocale(mMaxDate, locale);
    }

    /**
     * Gets a calendar for locale bootstrapped with the value of a given calendar.
     *
     * @param oldCalendar The old calendar.
     * @param locale The locale.
     */
    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
        if (oldCalendar == null) {
            return Calendar.getInstance(locale);
        } else {
            final long currentTimeMillis = oldCalendar.getTimeInMillis();
            Calendar newCalendar = Calendar.getInstance(locale);
            newCalendar.setTimeInMillis(currentTimeMillis);
            return newCalendar;
        }
    }

    /**
     * @return True if the <code>firstDate</code> is the same as the <code>
     * secondDate</code>.
+65 −30
Original line number Diff line number Diff line
@@ -16,10 +16,9 @@

package android.widget;

import com.android.internal.R;

import android.annotation.Widget;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +32,8 @@ import android.view.LayoutInflater;
import android.view.accessibility.AccessibilityEvent;
import android.widget.NumberPicker.OnValueChangeListener;

import com.android.internal.R;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@@ -89,23 +90,23 @@ public class DatePicker extends FrameLayout {

    private final CalendarView mCalendarView;

    private OnDateChangedListener mOnDateChangedListener;
    private Locale mCurrentLocale;

    private Locale mMonthLocale;
    private OnDateChangedListener mOnDateChangedListener;

    private final Calendar mTempDate = Calendar.getInstance();
    private String[] mShortMonths;

    private final int mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
    private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);

    private final String[] mShortMonths = new String[mNumberOfMonths];
    private int mNumberOfMonths;

    private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
    private Calendar mTempDate;

    private final Calendar mMinDate = Calendar.getInstance();
    private Calendar mMinDate;

    private final Calendar mMaxDate = Calendar.getInstance();
    private Calendar mMaxDate;

    private final Calendar mCurrentDate = Calendar.getInstance();
    private Calendar mCurrentDate;

    private boolean mIsEnabled = DEFAULT_ENABLED_STATE;

@@ -137,6 +138,9 @@ public class DatePicker extends FrameLayout {
    public DatePicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // initialization based on locale
        setCurrentLocale(Locale.getDefault());

        TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
                defStyle, 0);
        boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown,
@@ -213,7 +217,7 @@ public class DatePicker extends FrameLayout {
        mMonthSpinner = (NumberPicker) findViewById(R.id.month);
        mMonthSpinner.setMinValue(0);
        mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
        mMonthSpinner.setDisplayedValues(getShortMonths());
        mMonthSpinner.setDisplayedValues(mShortMonths);
        mMonthSpinner.setOnLongPressUpdateInterval(200);
        mMonthSpinner.setOnValueChangedListener(onChangeListener);

@@ -363,6 +367,12 @@ public class DatePicker extends FrameLayout {
        event.getText().add(selectedDateUtterance);
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setCurrentLocale(newConfig.locale);
    }

    /**
     * Gets whether the {@link CalendarView} is shown.
     *
@@ -410,6 +420,48 @@ public class DatePicker extends FrameLayout {
        mSpinners.setVisibility(shown ? VISIBLE : GONE);
    }

    /**
     * Sets the current locale.
     *
     * @param locale The current locale.
     */
    private void setCurrentLocale(Locale locale) {
        if (locale.equals(mCurrentLocale)) {
            return;
        }

        mCurrentLocale = locale;

        mTempDate = getCalendarForLocale(mTempDate, locale);
        mMinDate = getCalendarForLocale(mMinDate, locale);
        mMaxDate = getCalendarForLocale(mMaxDate, locale);
        mCurrentDate = getCalendarForLocale(mCurrentDate, locale);

        mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
        mShortMonths = new String[mNumberOfMonths];
        for (int i = 0; i < mNumberOfMonths; i++) {
            mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i,
                    DateUtils.LENGTH_MEDIUM);
        }
    }

    /**
     * Gets a calendar for locale bootstrapped with the value of a given calendar.
     *
     * @param oldCalendar The old calendar.
     * @param locale The locale.
     */
    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
        if (oldCalendar == null) {
            return Calendar.getInstance(locale);
        } else {
            final long currentTimeMillis = oldCalendar.getTimeInMillis();
            Calendar newCalendar = Calendar.getInstance(locale);
            newCalendar.setTimeInMillis(currentTimeMillis);
            return newCalendar;
        }
    }

    /**
     * Reorders the spinners according to the date format that is
     * explicitly set by the user and if no such is set fall back
@@ -507,23 +559,6 @@ public class DatePicker extends FrameLayout {
        }
    }

    /**
     * @return The short month abbreviations.
     */
    private String[] getShortMonths() {
        final Locale currentLocale = Locale.getDefault();
        if (currentLocale.equals(mMonthLocale)) {
            return mShortMonths;
        } else {
            for (int i = 0; i < mNumberOfMonths; i++) {
                mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i,
                        DateUtils.LENGTH_MEDIUM);
            }
            mMonthLocale = currentLocale;
            return mShortMonths;
        }
    }

    private boolean isNewDate(int year, int month, int dayOfMonth) {
        return (mCurrentDate.get(Calendar.YEAR) != year
                || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
@@ -569,7 +604,7 @@ public class DatePicker extends FrameLayout {

        // make sure the month names are a zero based array
        // with the months in the month spinner
        String[] displayedValues = Arrays.copyOfRange(getShortMonths(),
        String[] displayedValues = Arrays.copyOfRange(mShortMonths,
                mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
        mMonthSpinner.setDisplayedValues(displayedValues);

+26 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import com.android.internal.R;

import android.annotation.Widget;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +33,7 @@ import android.widget.NumberPicker.OnValueChangeListener;

import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Locale;

/**
 * A view for selecting the time of day, in either 24 hour or AM/PM mode. The
@@ -92,6 +94,8 @@ public class TimePicker extends FrameLayout {

    private Calendar mTempCalendar;

    private Locale mCurrentLocale;

    /**
     * The callback interface used to indicate the time has been adjusted.
     */
@@ -116,6 +120,9 @@ public class TimePicker extends FrameLayout {
    public TimePicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // initialization based on locale
        setCurrentLocale(Locale.getDefault());

        // process style attributes
        TypedArray attributesArray = context.obtainStyledAttributes(
                attrs, R.styleable.TimePicker, defStyle, 0);
@@ -211,8 +218,6 @@ public class TimePicker extends FrameLayout {
        updateHourControl();
        updateAmPmControl();

        // initialize to current time
        mTempCalendar = Calendar.getInstance();
        setOnTimeChangedListener(NO_OP_CHANGE_LISTENER);

        // set to current time
@@ -248,6 +253,25 @@ public class TimePicker extends FrameLayout {
        return mIsEnabled;
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setCurrentLocale(newConfig.locale);
    }

    /**
     * Sets the current locale.
     *
     * @param locale The current locale.
     */
    private void setCurrentLocale(Locale locale) {
        if (locale.equals(mCurrentLocale)) {
            return;
        }
        mCurrentLocale = locale;
        mTempCalendar = Calendar.getInstance(locale);
    }

    /**
     * Used to save / restore state of time picker
     */