All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.holoeverywhere.widget.DatePicker Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version

package org.holoeverywhere.widget;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

import org.holoeverywhere.LayoutInflater;
import org.holoeverywhere.R;
import org.holoeverywhere.internal.NumberPickerEditText;
import org.holoeverywhere.util.Arrays;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Build.VERSION;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;

public class DatePicker extends FrameLayout {
    private final class Callback implements NumberPicker.OnValueChangeListener,
            CalendarView.OnDateChangeListener {
        @Override
        public void onSelectedDayChange(CalendarView view, int year, int month,
                int monthDay) {
            setDate(year, month, monthDay);
            updateSpinners();
            notifyDateChanged();
        }

        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            updateInputState();
            tempDate.setTimeInMillis(currentDate.getTimeInMillis());
            if (picker == daySpinner) {
                int maxDayOfMonth = tempDate
                        .getActualMaximum(Calendar.DAY_OF_MONTH);
                if (oldVal == maxDayOfMonth && newVal == 1) {
                    tempDate.add(Calendar.DAY_OF_MONTH, 1);
                } else if (oldVal == 1 && newVal == maxDayOfMonth) {
                    tempDate.add(Calendar.DAY_OF_MONTH, -1);
                } else {
                    tempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
                }
            } else if (picker == monthSpinner) {
                if (oldVal == 11 && newVal == 0) {
                    tempDate.add(Calendar.MONTH, 1);
                } else if (oldVal == 0 && newVal == 11) {
                    tempDate.add(Calendar.MONTH, -1);
                } else {
                    tempDate.add(Calendar.MONTH, newVal - oldVal);
                }
            } else if (picker == yearSpinner) {
                tempDate.set(Calendar.YEAR, newVal);
            } else {
                return;
            }
            setDate(tempDate.get(Calendar.YEAR), tempDate.get(Calendar.MONTH),
                    tempDate.get(Calendar.DAY_OF_MONTH));
            updateSpinners();
            updateCalendarView();
            notifyDateChanged();
        }

    }

    public interface OnDateChangedListener {
        void onDateChanged(DatePicker view, int year, int monthOfYear,
                int dayOfMonth);
    }

    private static class SavedState extends BaseSavedState {
        @SuppressWarnings("all")
        public static final Creator CREATOR = new Creator() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };

        private final int year, month, day;

        private SavedState(Parcel in) {
            super(in);
            year = in.readInt();
            month = in.readInt();
            day = in.readInt();
        }

        private SavedState(Parcelable superState, int year, int month, int day) {
            super(superState);
            this.year = year;
            this.month = month;
            this.day = day;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(year);
            dest.writeInt(month);
            dest.writeInt(day);
        }
    }

    private static final String DATE_FORMAT = "MM/dd/yyyy";
    private static final String LOG_TAG = DatePicker.class.getSimpleName();

    private static 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;
        }
    }

    private static void setContentDescription(View parent, int childId,
            int textId) {
        if (parent == null) {
            return;
        }
        View child = parent.findViewById(childId);
        if (child != null) {
            child.setContentDescription(parent.getContext().getText(textId));
        }
    }

    private final Callback callback = new Callback();
    private final java.text.DateFormat dateFormat = new SimpleDateFormat(
            DatePicker.DATE_FORMAT);
    private final NumberPicker daySpinner, monthSpinner, yearSpinner;
    private final InputMethodManager inputMethodManager;
    private Locale locale;
    private final CalendarView mCalendarView;
    private int numberOfMonths;
    private OnDateChangedListener onDateChangedListener;
    private String[] shortMonths;
    private final LinearLayout spinners;
    private Calendar tempDate, minDate, maxDate, currentDate;

    public DatePicker(Context context) {
        this(context, null);
    }

    public DatePicker(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.datePickerStyle);
    }

    public DatePicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.DatePicker, defStyle, R.style.Holo_DatePicker);
        boolean spinnersShown = a.getBoolean(
                R.styleable.DatePicker_spinnersShown, true);
        boolean calendarViewShown = a.getBoolean(
                R.styleable.DatePicker_calendarViewShown, true);
        boolean forceShownState = a.getBoolean(
                R.styleable.DatePicker_forceShownState, false);
        int startYear = a.getInt(R.styleable.DatePicker_startYear, 1900);
        int endYear = a.getInt(R.styleable.DatePicker_endYear, 2100);
        String minDate = a.getString(R.styleable.DatePicker_minDate);
        String maxDate = a.getString(R.styleable.DatePicker_maxDate);
        int layoutResourceId = a.getResourceId(
                R.styleable.DatePicker_internalLayout,
                R.layout.date_picker_holo);
        a.recycle();
        inputMethodManager = (InputMethodManager) context
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        setLocale(Locale.getDefault());
        LayoutInflater.inflate(context, layoutResourceId, this, true);
        spinners = (LinearLayout) findViewById(R.id.pickers);
        mCalendarView = (CalendarView) findViewById(R.id.calendar_view);
        daySpinner = (NumberPicker) findViewById(R.id.day);
        monthSpinner = (NumberPicker) findViewById(R.id.month);
        yearSpinner = (NumberPicker) findViewById(R.id.year);
        if (((AccessibilityManager) getContext().getSystemService(
                Context.ACCESSIBILITY_SERVICE)).isEnabled()) {
            setContentDescriptions();
        }
        mCalendarView.setOnDateChangeListener(callback);
        daySpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
        daySpinner.setOnLongPressUpdateInterval(100);
        daySpinner.setOnValueChangedListener(callback);
        monthSpinner.setMinValue(0);
        monthSpinner.setMaxValue(numberOfMonths - 1);
        monthSpinner.setDisplayedValues(shortMonths);
        monthSpinner.setOnLongPressUpdateInterval(200);
        monthSpinner.setOnValueChangedListener(callback);
        yearSpinner.setOnLongPressUpdateInterval(100);
        yearSpinner.setOnValueChangedListener(callback);
        if (spinnersShown || calendarViewShown || forceShownState) {
            setSpinnersShown(spinnersShown);
            setCalendarViewShown(calendarViewShown);
        } else {
            setSpinnersShown(true);
            setCalendarViewShown(false);
        }
        tempDate.clear();
        if (TextUtils.isEmpty(minDate) || !parseDate(minDate, tempDate)) {
            tempDate.set(startYear, 0, 1);
        }
        setMinDate(tempDate.getTimeInMillis());
        tempDate.clear();
        if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, tempDate)) {
            tempDate.set(endYear, 11, 31);
        }
        setMaxDate(tempDate.getTimeInMillis());
        currentDate.setTimeInMillis(System.currentTimeMillis());
        init(currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH),
                currentDate.get(Calendar.DAY_OF_MONTH), null);
        // TODO
        // Reordering a spinners leads them stop, temporarily disabled.
        // reorderSpinners();
    }

    private void checkInputState(NumberPicker... spinners) {
        for (NumberPicker spinner : spinners) {
            NumberPickerEditText input = spinner.getInputField();
            if (inputMethodManager.isActive(input)) {
                input.clearFocus();
                inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
            }
        }
    }

    @SuppressLint("NewApi")
    @Override
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
        onPopulateAccessibilityEvent(event);
        return true;
    }

    @Override
    protected void dispatchRestoreInstanceState(
            SparseArray container) {
        dispatchThawSelfOnly(container);
    }

    public CalendarView getCalendarView() {
        return mCalendarView;
    }

    public boolean getCalendarViewShown() {
        return mCalendarView.isShown();
    }

    public int getDayOfMonth() {
        return currentDate.get(Calendar.DAY_OF_MONTH);
    }

    public long getMaxDate() {
        return mCalendarView.getMaxDate();
    }

    public long getMinDate() {
        return mCalendarView.getMinDate();
    }

    public int getMonth() {
        return currentDate.get(Calendar.MONTH);
    }

    public OnDateChangedListener getOnDateChangedListener() {
        return onDateChangedListener;
    }

    public boolean getSpinnersShown() {
        return spinners.isShown();
    }

    public int getYear() {
        return currentDate.get(Calendar.YEAR);
    }

    public void init(int year, int monthOfYear, int dayOfMonth,
            OnDateChangedListener onDateChangedListener) {
        setOnDateChangedListener(onDateChangedListener);
        setDate(year, monthOfYear, dayOfMonth);
        updateSpinners();
        updateCalendarView();
    }

    private boolean isNewDate(int year, int month, int dayOfMonth) {
        return currentDate.get(Calendar.YEAR) != year
                || currentDate.get(Calendar.MONTH) != dayOfMonth
                || currentDate.get(Calendar.DAY_OF_MONTH) != month;
    }

    private void notifyDateChanged() {
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
        if (onDateChangedListener != null) {
            onDateChangedListener.onDateChanged(this, getYear(), getMonth(),
                    getDayOfMonth());
        }
    }

    @Override
    @SuppressLint("NewApi")
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setLocale(newConfig.locale);
    }

    @SuppressLint("NewApi")
    @Override
    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
        if (VERSION.SDK_INT >= 14) {
            super.onPopulateAccessibilityEvent(event);
        }
        final int flags = DateUtils.FORMAT_SHOW_DATE
                | DateUtils.FORMAT_SHOW_YEAR;
        String selectedDateUtterance = DateUtils.formatDateTime(getContext(),
                currentDate.getTimeInMillis(), flags);
        event.getText().add(selectedDateUtterance);
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        setDate(ss.year, ss.month, ss.day);
        updateSpinners();
        updateCalendarView();
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        return new SavedState(super.onSaveInstanceState(), getYear(),
                getMonth(), getDayOfMonth());
    }

    private boolean parseDate(String date, Calendar outDate) {
        try {
            outDate.setTime(dateFormat.parse(date));
            return true;
        } catch (ParseException e) {
            Log.w(DatePicker.LOG_TAG, "Date: " + date + " not in format: "
                    + DatePicker.DATE_FORMAT);
            return false;
        }
    }

    private void pushSpinner(NumberPicker spinner, int spinnerCount, int i) {
        if (spinner.getParent() != null
                && spinner.getParent() instanceof ViewGroup) {
            ViewGroup parent = (ViewGroup) spinner.getParent();
            if (parent.getChildAt(i) != spinner) {
                parent.removeView(spinner);
                parent.addView(spinner);
                setImeOptions(spinner, spinnerCount, i);
            }
        }
    }

    @SuppressWarnings("unused")
    private void reorderSpinners() {
        char[] order = DateFormat.getDateFormatOrder(getContext());
        final int spinnerCount = order.length;
        for (int i = 0; i < spinnerCount; i++) {
            switch (order[i]) {
                case DateFormat.DATE:
                    pushSpinner(daySpinner, spinnerCount, i);
                    break;
                case DateFormat.MONTH:
                    pushSpinner(monthSpinner, spinnerCount, i);
                    break;
                case DateFormat.YEAR:
                    pushSpinner(yearSpinner, spinnerCount, i);
                    break;
            }
        }
    }

    public void setCalendarViewShown(boolean shown) {
        mCalendarView.setVisibility(shown ? View.VISIBLE : View.GONE);
    }

    private void setContentDescriptions() {
        DatePicker.setContentDescription(daySpinner, R.id.increment,
                R.string.date_picker_increment_day_button);
        DatePicker.setContentDescription(daySpinner, R.id.decrement,
                R.string.date_picker_decrement_day_button);
        DatePicker.setContentDescription(monthSpinner, R.id.increment,
                R.string.date_picker_increment_month_button);
        DatePicker.setContentDescription(monthSpinner, R.id.decrement,
                R.string.date_picker_decrement_month_button);
        DatePicker.setContentDescription(yearSpinner, R.id.increment,
                R.string.date_picker_increment_year_button);
        DatePicker.setContentDescription(yearSpinner, R.id.decrement,
                R.string.date_picker_decrement_year_button);
    }

    private void setDate(int year, int month, int dayOfMonth) {
        currentDate.set(year, month, dayOfMonth);
        if (currentDate.before(minDate)) {
            currentDate.setTimeInMillis(minDate.getTimeInMillis());
        } else if (currentDate.after(maxDate)) {
            currentDate.setTimeInMillis(maxDate.getTimeInMillis());
        }
    }

    @Override
    public void setEnabled(boolean enabled) {
        if (isEnabled() == enabled) {
            return;
        }
        super.setEnabled(enabled);
        daySpinner.setEnabled(enabled);
        monthSpinner.setEnabled(enabled);
        yearSpinner.setEnabled(enabled);
        mCalendarView.setEnabled(enabled);
    }

    private void setImeOptions(NumberPicker spinner, int spinnerCount,
            int spinnerIndex) {
        final int imeOptions;
        if (spinnerIndex < spinnerCount - 1) {
            imeOptions = EditorInfo.IME_ACTION_NEXT;
        } else {
            imeOptions = EditorInfo.IME_ACTION_DONE;
        }
        spinner.getInputField().setImeOptions(imeOptions);
    }

    public void setLocale(Locale locale) {
        if (locale == null || locale.equals(this.locale)) {
            return;
        }
        this.locale = locale;
        tempDate = DatePicker.getCalendarForLocale(tempDate, locale);
        minDate = DatePicker.getCalendarForLocale(minDate, locale);
        maxDate = DatePicker.getCalendarForLocale(maxDate, locale);
        currentDate = DatePicker.getCalendarForLocale(currentDate, locale);
        numberOfMonths = tempDate.getActualMaximum(Calendar.MONTH) + 1;
        shortMonths = new String[numberOfMonths];
        for (int i = 0; i < numberOfMonths; i++) {
            shortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i,
                    DateUtils.LENGTH_MEDIUM);
        }
    }

    public void setMaxDate(long maxDateL) {
        tempDate.setTimeInMillis(maxDateL);
        if (tempDate.get(Calendar.YEAR) == maxDate.get(Calendar.YEAR)
                && tempDate.get(Calendar.DAY_OF_YEAR) == maxDate
                        .get(Calendar.DAY_OF_YEAR)) {
            return;
        }
        maxDate.setTimeInMillis(maxDateL);
        mCalendarView.setMaxDate(maxDateL);
        if (currentDate.after(maxDate)) {
            currentDate.setTimeInMillis(maxDate.getTimeInMillis());
            updateCalendarView();
        }
        updateSpinners();
    }

    public void setMinDate(long minDateL) {
        tempDate.setTimeInMillis(minDateL);
        if (tempDate.get(Calendar.YEAR) == minDate.get(Calendar.YEAR)
                && tempDate.get(Calendar.DAY_OF_YEAR) == minDate
                        .get(Calendar.DAY_OF_YEAR)) {
            return;
        }
        minDate.setTimeInMillis(minDateL);
        mCalendarView.setMinDate(minDateL);
        if (currentDate.before(minDate)) {
            currentDate.setTimeInMillis(minDate.getTimeInMillis());
            updateCalendarView();
        }
        updateSpinners();
    }

    public void setOnDateChangedListener(
            OnDateChangedListener onDateChangedListener) {
        this.onDateChangedListener = onDateChangedListener;
    }

    public void setSpinnersShown(boolean shown) {
        spinners.setVisibility(shown ? View.VISIBLE : View.GONE);
    }

    private void updateCalendarView() {
        mCalendarView.setDate(currentDate.getTimeInMillis(), false, false);
    }

    public void updateDate(int year, int month, int dayOfMonth) {
        if (!isNewDate(year, month, dayOfMonth)) {
            return;
        }
        setDate(year, month, dayOfMonth);
        updateSpinners();
        updateCalendarView();
        notifyDateChanged();
    }

    private void updateInputState() {
        if (inputMethodManager != null) {
            checkInputState(yearSpinner, monthSpinner, daySpinner);
        }
    }

    private void updateSpinners() {
        monthSpinner.setDisplayedValues(null);
        if (currentDate.equals(minDate)) {
            daySpinner.setMinValue(currentDate.get(Calendar.DAY_OF_MONTH));
            daySpinner.setMaxValue(currentDate
                    .getActualMaximum(Calendar.DAY_OF_MONTH));
            daySpinner.setWrapSelectorWheel(false);
            monthSpinner.setMinValue(currentDate.get(Calendar.MONTH));
            monthSpinner.setMaxValue(currentDate
                    .getActualMaximum(Calendar.MONTH));
            monthSpinner.setWrapSelectorWheel(false);
        } else if (currentDate.equals(maxDate)) {
            daySpinner.setMinValue(currentDate
                    .getActualMinimum(Calendar.DAY_OF_MONTH));
            daySpinner.setMaxValue(currentDate.get(Calendar.DAY_OF_MONTH));
            daySpinner.setWrapSelectorWheel(false);
            monthSpinner.setMinValue(currentDate
                    .getActualMinimum(Calendar.MONTH));
            monthSpinner.setMaxValue(currentDate.get(Calendar.MONTH));
            monthSpinner.setWrapSelectorWheel(false);
        } else {
            daySpinner.setMinValue(1);
            daySpinner.setMaxValue(currentDate
                    .getActualMaximum(Calendar.DAY_OF_MONTH));
            daySpinner.setWrapSelectorWheel(true);
            monthSpinner.setMinValue(0);
            monthSpinner.setMaxValue(11);
            monthSpinner.setWrapSelectorWheel(true);
        }
        String[] displayedValues = Arrays.copyOfRange(shortMonths,
                monthSpinner.getMinValue(), monthSpinner.getMaxValue() + 1);
        monthSpinner.setDisplayedValues(displayedValues);
        yearSpinner.setMinValue(minDate.get(Calendar.YEAR));
        yearSpinner.setMaxValue(maxDate.get(Calendar.YEAR));
        yearSpinner.setWrapSelectorWheel(false);
        yearSpinner.setValue(currentDate.get(Calendar.YEAR));
        monthSpinner.setValue(currentDate.get(Calendar.MONTH));
        daySpinner.setValue(currentDate.get(Calendar.DAY_OF_MONTH));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy