DayPickerView.java revision 3e9818e0267619fecebd55095ab26c53eff92e93
13e9818e0267619fecebd55095ab26c53eff92e93James Kung/*
23e9818e0267619fecebd55095ab26c53eff92e93James Kung * Copyright (C) 2013 The Android Open Source Project
33e9818e0267619fecebd55095ab26c53eff92e93James Kung *
43e9818e0267619fecebd55095ab26c53eff92e93James Kung * Licensed under the Apache License, Version 2.0 (the "License");
53e9818e0267619fecebd55095ab26c53eff92e93James Kung * you may not use this file except in compliance with the License.
63e9818e0267619fecebd55095ab26c53eff92e93James Kung * You may obtain a copy of the License at
73e9818e0267619fecebd55095ab26c53eff92e93James Kung *
83e9818e0267619fecebd55095ab26c53eff92e93James Kung *      http://www.apache.org/licenses/LICENSE-2.0
93e9818e0267619fecebd55095ab26c53eff92e93James Kung *
103e9818e0267619fecebd55095ab26c53eff92e93James Kung * Unless required by applicable law or agreed to in writing, software
113e9818e0267619fecebd55095ab26c53eff92e93James Kung * distributed under the License is distributed on an "AS IS" BASIS,
123e9818e0267619fecebd55095ab26c53eff92e93James Kung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e9818e0267619fecebd55095ab26c53eff92e93James Kung * See the License for the specific language governing permissions and
143e9818e0267619fecebd55095ab26c53eff92e93James Kung * limitations under the License.
153e9818e0267619fecebd55095ab26c53eff92e93James Kung */
163e9818e0267619fecebd55095ab26c53eff92e93James Kung
173e9818e0267619fecebd55095ab26c53eff92e93James Kungpackage com.android.datetimepicker.date;
183e9818e0267619fecebd55095ab26c53eff92e93James Kung
193e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.content.Context;
203e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.database.DataSetObserver;
213e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.os.Handler;
223e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.text.format.Time;
233e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.util.Log;
243e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.view.View;
253e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.view.ViewConfiguration;
263e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.widget.AbsListView;
273e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.widget.AbsListView.OnScrollListener;
283e9818e0267619fecebd55095ab26c53eff92e93James Kungimport android.widget.ListView;
293e9818e0267619fecebd55095ab26c53eff92e93James Kung
303e9818e0267619fecebd55095ab26c53eff92e93James Kungimport com.android.datetimepicker.date.SimpleMonthAdapter.CalendarDay;
313e9818e0267619fecebd55095ab26c53eff92e93James Kung
323e9818e0267619fecebd55095ab26c53eff92e93James Kungimport java.util.Calendar;
333e9818e0267619fecebd55095ab26c53eff92e93James Kungimport java.util.Locale;
343e9818e0267619fecebd55095ab26c53eff92e93James Kung
353e9818e0267619fecebd55095ab26c53eff92e93James Kung/**
363e9818e0267619fecebd55095ab26c53eff92e93James Kung * This displays a list of months in a calendar format with selectable days.
373e9818e0267619fecebd55095ab26c53eff92e93James Kung */
383e9818e0267619fecebd55095ab26c53eff92e93James Kungpublic class DayPickerView extends ListView implements OnScrollListener {
393e9818e0267619fecebd55095ab26c53eff92e93James Kung
403e9818e0267619fecebd55095ab26c53eff92e93James Kung    private static final String TAG = "MonthFragment";
413e9818e0267619fecebd55095ab26c53eff92e93James Kung
423e9818e0267619fecebd55095ab26c53eff92e93James Kung    // Affects when the month selection will change while scrolling up
433e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected static final int SCROLL_HYST_WEEKS = 2;
443e9818e0267619fecebd55095ab26c53eff92e93James Kung    // How long the GoTo fling animation should last
453e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected static final int GOTO_SCROLL_DURATION = 250;
463e9818e0267619fecebd55095ab26c53eff92e93James Kung    // How long to wait after receiving an onScrollStateChanged notification
473e9818e0267619fecebd55095ab26c53eff92e93James Kung    // before acting on it
483e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected static final int SCROLL_CHANGE_DELAY = 40;
493e9818e0267619fecebd55095ab26c53eff92e93James Kung    // The number of days to display in each week
503e9818e0267619fecebd55095ab26c53eff92e93James Kung    public static final int DAYS_PER_WEEK = 7;
513e9818e0267619fecebd55095ab26c53eff92e93James Kung    public static int LIST_TOP_OFFSET = -1; // so that the top line will be
523e9818e0267619fecebd55095ab26c53eff92e93James Kung                                            // under the separator
533e9818e0267619fecebd55095ab26c53eff92e93James Kung    // You can override these numbers to get a different appearance
543e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected int mNumWeeks = 6;
553e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected boolean mShowWeekNumber = false;
563e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected int mDaysPerWeek = 7;
573e9818e0267619fecebd55095ab26c53eff92e93James Kung
583e9818e0267619fecebd55095ab26c53eff92e93James Kung    // These affect the scroll speed and feel
593e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected float mFriction = 1.0f;
603e9818e0267619fecebd55095ab26c53eff92e93James Kung
613e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected Context mContext;
623e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected Handler mHandler;
633e9818e0267619fecebd55095ab26c53eff92e93James Kung
643e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected float mMinimumFlingVelocity;
653e9818e0267619fecebd55095ab26c53eff92e93James Kung
663e9818e0267619fecebd55095ab26c53eff92e93James Kung    // highlighted time
673e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected CalendarDay mSelectedDay = new CalendarDay();
683e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected SimpleMonthAdapter mAdapter;
693e9818e0267619fecebd55095ab26c53eff92e93James Kung
703e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected CalendarDay mTempDay = new CalendarDay();
713e9818e0267619fecebd55095ab26c53eff92e93James Kung
723e9818e0267619fecebd55095ab26c53eff92e93James Kung    private static float mScale = 0;
733e9818e0267619fecebd55095ab26c53eff92e93James Kung    // When the week starts; numbered like Time.<WEEKDAY> (e.g. SUNDAY=0).
743e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected int mFirstDayOfWeek;
753e9818e0267619fecebd55095ab26c53eff92e93James Kung    // The first day that is visible in the view
763e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected Time mFirstVisibleDay = new Time();
773e9818e0267619fecebd55095ab26c53eff92e93James Kung    // The last name announced by accessibility
783e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected CharSequence mPrevMonthName;
793e9818e0267619fecebd55095ab26c53eff92e93James Kung    // which month should be displayed/highlighted [0-11]
803e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected int mCurrentMonthDisplayed;
813e9818e0267619fecebd55095ab26c53eff92e93James Kung    // used for tracking during a scroll
823e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected long mPreviousScrollPosition;
833e9818e0267619fecebd55095ab26c53eff92e93James Kung    // used for tracking what state listview is in
843e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
853e9818e0267619fecebd55095ab26c53eff92e93James Kung    // used for tracking what state listview is in
863e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
873e9818e0267619fecebd55095ab26c53eff92e93James Kung
883e9818e0267619fecebd55095ab26c53eff92e93James Kung    private final DatePickerController mController;
893e9818e0267619fecebd55095ab26c53eff92e93James Kung
903e9818e0267619fecebd55095ab26c53eff92e93James Kung    // This causes an update of the view at midnight
913e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected Runnable mTodayUpdater = new Runnable() {
923e9818e0267619fecebd55095ab26c53eff92e93James Kung        @Override
933e9818e0267619fecebd55095ab26c53eff92e93James Kung        public void run() {
943e9818e0267619fecebd55095ab26c53eff92e93James Kung            Time midnight = new Time(mFirstVisibleDay.timezone);
953e9818e0267619fecebd55095ab26c53eff92e93James Kung            midnight.setToNow();
963e9818e0267619fecebd55095ab26c53eff92e93James Kung            long currentMillis = midnight.toMillis(true);
973e9818e0267619fecebd55095ab26c53eff92e93James Kung
983e9818e0267619fecebd55095ab26c53eff92e93James Kung            midnight.hour = 0;
993e9818e0267619fecebd55095ab26c53eff92e93James Kung            midnight.minute = 0;
1003e9818e0267619fecebd55095ab26c53eff92e93James Kung            midnight.second = 0;
1013e9818e0267619fecebd55095ab26c53eff92e93James Kung            midnight.monthDay++;
1023e9818e0267619fecebd55095ab26c53eff92e93James Kung            long millisToMidnight = midnight.normalize(true) - currentMillis;
1033e9818e0267619fecebd55095ab26c53eff92e93James Kung            mHandler.postDelayed(this, millisToMidnight);
1043e9818e0267619fecebd55095ab26c53eff92e93James Kung
1053e9818e0267619fecebd55095ab26c53eff92e93James Kung            if (mAdapter != null) {
1063e9818e0267619fecebd55095ab26c53eff92e93James Kung                mAdapter.notifyDataSetChanged();
1073e9818e0267619fecebd55095ab26c53eff92e93James Kung            }
1083e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
1093e9818e0267619fecebd55095ab26c53eff92e93James Kung    };
1103e9818e0267619fecebd55095ab26c53eff92e93James Kung
1113e9818e0267619fecebd55095ab26c53eff92e93James Kung    // This allows us to update our position when a day is tapped
1123e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected DataSetObserver mObserver = new DataSetObserver() {
1133e9818e0267619fecebd55095ab26c53eff92e93James Kung        @Override
1143e9818e0267619fecebd55095ab26c53eff92e93James Kung        public void onChanged() {
1153e9818e0267619fecebd55095ab26c53eff92e93James Kung            CalendarDay day = mAdapter.getSelectedDay();
1163e9818e0267619fecebd55095ab26c53eff92e93James Kung            if (day.year != mSelectedDay.year || day.day != mSelectedDay.day) {
1173e9818e0267619fecebd55095ab26c53eff92e93James Kung                goTo(day, true, true, false);
1183e9818e0267619fecebd55095ab26c53eff92e93James Kung            }
1193e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
1203e9818e0267619fecebd55095ab26c53eff92e93James Kung    };
1213e9818e0267619fecebd55095ab26c53eff92e93James Kung
1223e9818e0267619fecebd55095ab26c53eff92e93James Kung    public DayPickerView(Context context, DatePickerController controller) {
1233e9818e0267619fecebd55095ab26c53eff92e93James Kung        super(context);
1243e9818e0267619fecebd55095ab26c53eff92e93James Kung        mHandler = new Handler();
1253e9818e0267619fecebd55095ab26c53eff92e93James Kung        mController = controller;
1263e9818e0267619fecebd55095ab26c53eff92e93James Kung        setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
1273e9818e0267619fecebd55095ab26c53eff92e93James Kung        setDrawSelectorOnTop(false);
1283e9818e0267619fecebd55095ab26c53eff92e93James Kung        init(context);
1293e9818e0267619fecebd55095ab26c53eff92e93James Kung        setCalendarDate(controller.getSelectedDay());
1303e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
1313e9818e0267619fecebd55095ab26c53eff92e93James Kung
1323e9818e0267619fecebd55095ab26c53eff92e93James Kung    public void setCalendarDate(CalendarDay day) {
1333e9818e0267619fecebd55095ab26c53eff92e93James Kung        goTo(day, false, true, true);
1343e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
1353e9818e0267619fecebd55095ab26c53eff92e93James Kung
1363e9818e0267619fecebd55095ab26c53eff92e93James Kung    public void init(Context context) {
1373e9818e0267619fecebd55095ab26c53eff92e93James Kung        mContext = context;
1383e9818e0267619fecebd55095ab26c53eff92e93James Kung        String tz = Time.getCurrentTimezone();
1393e9818e0267619fecebd55095ab26c53eff92e93James Kung        ViewConfiguration viewConfig = ViewConfiguration.get(context);
1403e9818e0267619fecebd55095ab26c53eff92e93James Kung        mMinimumFlingVelocity = viewConfig.getScaledMinimumFlingVelocity();
1413e9818e0267619fecebd55095ab26c53eff92e93James Kung
1423e9818e0267619fecebd55095ab26c53eff92e93James Kung        mFirstVisibleDay.timezone = tz;
1433e9818e0267619fecebd55095ab26c53eff92e93James Kung        mFirstVisibleDay.normalize(true);
1443e9818e0267619fecebd55095ab26c53eff92e93James Kung
1453e9818e0267619fecebd55095ab26c53eff92e93James Kung        setUpListView();
1463e9818e0267619fecebd55095ab26c53eff92e93James Kung        setUpAdapter();
1473e9818e0267619fecebd55095ab26c53eff92e93James Kung        setAdapter(mAdapter);
1483e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
1493e9818e0267619fecebd55095ab26c53eff92e93James Kung
1503e9818e0267619fecebd55095ab26c53eff92e93James Kung    public void onChange() {
1513e9818e0267619fecebd55095ab26c53eff92e93James Kung        setUpAdapter();
1523e9818e0267619fecebd55095ab26c53eff92e93James Kung        setAdapter(mAdapter);
1533e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
1543e9818e0267619fecebd55095ab26c53eff92e93James Kung
1553e9818e0267619fecebd55095ab26c53eff92e93James Kung    /**
1563e9818e0267619fecebd55095ab26c53eff92e93James Kung     * Creates a new adapter if necessary and sets up its parameters. Override
1573e9818e0267619fecebd55095ab26c53eff92e93James Kung     * this method to provide a custom adapter.
1583e9818e0267619fecebd55095ab26c53eff92e93James Kung     */
1593e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected void setUpAdapter() {
1603e9818e0267619fecebd55095ab26c53eff92e93James Kung        if (mAdapter == null) {
1613e9818e0267619fecebd55095ab26c53eff92e93James Kung            mAdapter = new SimpleMonthAdapter(getContext(), mController);
1623e9818e0267619fecebd55095ab26c53eff92e93James Kung            mAdapter.registerDataSetObserver(mObserver);
1633e9818e0267619fecebd55095ab26c53eff92e93James Kung        } else {
1643e9818e0267619fecebd55095ab26c53eff92e93James Kung            mAdapter.setSelectedDay(mSelectedDay);
1653e9818e0267619fecebd55095ab26c53eff92e93James Kung            mAdapter.notifyDataSetChanged();
1663e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
1673e9818e0267619fecebd55095ab26c53eff92e93James Kung        // refresh the view with the new parameters
1683e9818e0267619fecebd55095ab26c53eff92e93James Kung        mAdapter.notifyDataSetChanged();
1693e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
1703e9818e0267619fecebd55095ab26c53eff92e93James Kung
1713e9818e0267619fecebd55095ab26c53eff92e93James Kung    /*
1723e9818e0267619fecebd55095ab26c53eff92e93James Kung     * Sets all the required fields for the list view. Override this method to
1733e9818e0267619fecebd55095ab26c53eff92e93James Kung     * set a different list view behavior.
1743e9818e0267619fecebd55095ab26c53eff92e93James Kung     */
1753e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected void setUpListView() {
1763e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Transparent background on scroll
1773e9818e0267619fecebd55095ab26c53eff92e93James Kung        setCacheColorHint(0);
1783e9818e0267619fecebd55095ab26c53eff92e93James Kung        // No dividers
1793e9818e0267619fecebd55095ab26c53eff92e93James Kung        setDivider(null);
1803e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Items are clickable
1813e9818e0267619fecebd55095ab26c53eff92e93James Kung        setItemsCanFocus(true);
1823e9818e0267619fecebd55095ab26c53eff92e93James Kung        // The thumb gets in the way, so disable it
1833e9818e0267619fecebd55095ab26c53eff92e93James Kung        setFastScrollEnabled(false);
1843e9818e0267619fecebd55095ab26c53eff92e93James Kung        setVerticalScrollBarEnabled(false);
1853e9818e0267619fecebd55095ab26c53eff92e93James Kung        setOnScrollListener(this);
1863e9818e0267619fecebd55095ab26c53eff92e93James Kung        setFadingEdgeLength(0);
1873e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Make the scrolling behavior nicer
1883e9818e0267619fecebd55095ab26c53eff92e93James Kung        setFriction(ViewConfiguration.getScrollFriction() * mFriction);
1893e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
1903e9818e0267619fecebd55095ab26c53eff92e93James Kung
1913e9818e0267619fecebd55095ab26c53eff92e93James Kung    @Override
1923e9818e0267619fecebd55095ab26c53eff92e93James Kung    public void onAttachedToWindow() {
1933e9818e0267619fecebd55095ab26c53eff92e93James Kung        super.onAttachedToWindow();
1943e9818e0267619fecebd55095ab26c53eff92e93James Kung        setUpAdapter();
1953e9818e0267619fecebd55095ab26c53eff92e93James Kung        doResumeUpdates();
1963e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
1973e9818e0267619fecebd55095ab26c53eff92e93James Kung
1983e9818e0267619fecebd55095ab26c53eff92e93James Kung    @Override
1993e9818e0267619fecebd55095ab26c53eff92e93James Kung    public void onDetachedFromWindow() {
2003e9818e0267619fecebd55095ab26c53eff92e93James Kung        super.onDetachedFromWindow();
2013e9818e0267619fecebd55095ab26c53eff92e93James Kung        mHandler.removeCallbacks(mTodayUpdater);
2023e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
2033e9818e0267619fecebd55095ab26c53eff92e93James Kung
2043e9818e0267619fecebd55095ab26c53eff92e93James Kung    // @Override
2053e9818e0267619fecebd55095ab26c53eff92e93James Kung    // public void onSaveInstanceState(Bundle outState) {
2063e9818e0267619fecebd55095ab26c53eff92e93James Kung    // outState.putLong(KEY_CURRENT_TIME, mSelectedDay.toMillis(true));
2073e9818e0267619fecebd55095ab26c53eff92e93James Kung    // }
2083e9818e0267619fecebd55095ab26c53eff92e93James Kung
2093e9818e0267619fecebd55095ab26c53eff92e93James Kung    /**
2103e9818e0267619fecebd55095ab26c53eff92e93James Kung     * Updates the user preference fields. Override this to use a different
2113e9818e0267619fecebd55095ab26c53eff92e93James Kung     * preference space.
2123e9818e0267619fecebd55095ab26c53eff92e93James Kung     */
2133e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected void doResumeUpdates() {
2143e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Get default week start based on locale, subtracting one for use with
2153e9818e0267619fecebd55095ab26c53eff92e93James Kung        // android Time.
2163e9818e0267619fecebd55095ab26c53eff92e93James Kung        Calendar cal = Calendar.getInstance(Locale.getDefault());
2173e9818e0267619fecebd55095ab26c53eff92e93James Kung        mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1;
2183e9818e0267619fecebd55095ab26c53eff92e93James Kung
2193e9818e0267619fecebd55095ab26c53eff92e93James Kung        mShowWeekNumber = false;
2203e9818e0267619fecebd55095ab26c53eff92e93James Kung
2213e9818e0267619fecebd55095ab26c53eff92e93James Kung        goTo(mSelectedDay, false, false, false);
2223e9818e0267619fecebd55095ab26c53eff92e93James Kung        mAdapter.setSelectedDay(mSelectedDay);
2233e9818e0267619fecebd55095ab26c53eff92e93James Kung        mTodayUpdater.run();
2243e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
2253e9818e0267619fecebd55095ab26c53eff92e93James Kung
2263e9818e0267619fecebd55095ab26c53eff92e93James Kung    /**
2273e9818e0267619fecebd55095ab26c53eff92e93James Kung     * This moves to the specified time in the view. If the time is not already
2283e9818e0267619fecebd55095ab26c53eff92e93James Kung     * in range it will move the list so that the first of the month containing
2293e9818e0267619fecebd55095ab26c53eff92e93James Kung     * the time is at the top of the view. If the new time is already in view
2303e9818e0267619fecebd55095ab26c53eff92e93James Kung     * the list will not be scrolled unless forceScroll is true. This time may
2313e9818e0267619fecebd55095ab26c53eff92e93James Kung     * optionally be highlighted as selected as well.
2323e9818e0267619fecebd55095ab26c53eff92e93James Kung     *
2333e9818e0267619fecebd55095ab26c53eff92e93James Kung     * @param time The time to move to
2343e9818e0267619fecebd55095ab26c53eff92e93James Kung     * @param animate Whether to scroll to the given time or just redraw at the
2353e9818e0267619fecebd55095ab26c53eff92e93James Kung     *            new location
2363e9818e0267619fecebd55095ab26c53eff92e93James Kung     * @param setSelected Whether to set the given time as selected
2373e9818e0267619fecebd55095ab26c53eff92e93James Kung     * @param forceScroll Whether to recenter even if the time is already
2383e9818e0267619fecebd55095ab26c53eff92e93James Kung     *            visible
2393e9818e0267619fecebd55095ab26c53eff92e93James Kung     * @return Whether or not the view animated to the new location
2403e9818e0267619fecebd55095ab26c53eff92e93James Kung     */
2413e9818e0267619fecebd55095ab26c53eff92e93James Kung    public boolean goTo(CalendarDay day, boolean animate, boolean setSelected, boolean forceScroll) {
2423e9818e0267619fecebd55095ab26c53eff92e93James Kung
2433e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Set the selected day
2443e9818e0267619fecebd55095ab26c53eff92e93James Kung        if (setSelected) {
2453e9818e0267619fecebd55095ab26c53eff92e93James Kung            mSelectedDay.set(day);
2463e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
2473e9818e0267619fecebd55095ab26c53eff92e93James Kung
2483e9818e0267619fecebd55095ab26c53eff92e93James Kung        mTempDay.set(day);
2493e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Get the week we're going to
2503e9818e0267619fecebd55095ab26c53eff92e93James Kung        // TODO push Util function into Calendar public api.
2513e9818e0267619fecebd55095ab26c53eff92e93James Kung        int position = (day.year - mController.getMinYear())
2523e9818e0267619fecebd55095ab26c53eff92e93James Kung                * SimpleMonthAdapter.MONTHS_IN_YEAR + day.month;
2533e9818e0267619fecebd55095ab26c53eff92e93James Kung
2543e9818e0267619fecebd55095ab26c53eff92e93James Kung        View child;
2553e9818e0267619fecebd55095ab26c53eff92e93James Kung        int i = 0;
2563e9818e0267619fecebd55095ab26c53eff92e93James Kung        int top = 0;
2573e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Find a child that's completely in the view
2583e9818e0267619fecebd55095ab26c53eff92e93James Kung        do {
2593e9818e0267619fecebd55095ab26c53eff92e93James Kung            child = getChildAt(i++);
2603e9818e0267619fecebd55095ab26c53eff92e93James Kung            if (child == null) {
2613e9818e0267619fecebd55095ab26c53eff92e93James Kung                break;
2623e9818e0267619fecebd55095ab26c53eff92e93James Kung            }
2633e9818e0267619fecebd55095ab26c53eff92e93James Kung            top = child.getTop();
2643e9818e0267619fecebd55095ab26c53eff92e93James Kung            if (Log.isLoggable(TAG, Log.DEBUG)) {
2653e9818e0267619fecebd55095ab26c53eff92e93James Kung                Log.d(TAG, "child at " + (i - 1) + " has top " + top);
2663e9818e0267619fecebd55095ab26c53eff92e93James Kung            }
2673e9818e0267619fecebd55095ab26c53eff92e93James Kung        } while (top < 0);
2683e9818e0267619fecebd55095ab26c53eff92e93James Kung
2693e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Compute the first and last position visible
2703e9818e0267619fecebd55095ab26c53eff92e93James Kung        int selectedPosition;
2713e9818e0267619fecebd55095ab26c53eff92e93James Kung        if (child != null) {
2723e9818e0267619fecebd55095ab26c53eff92e93James Kung            selectedPosition = getPositionForView(child);
2733e9818e0267619fecebd55095ab26c53eff92e93James Kung        } else {
2743e9818e0267619fecebd55095ab26c53eff92e93James Kung            selectedPosition = 0;
2753e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
2763e9818e0267619fecebd55095ab26c53eff92e93James Kung
2773e9818e0267619fecebd55095ab26c53eff92e93James Kung        if (setSelected) {
2783e9818e0267619fecebd55095ab26c53eff92e93James Kung            mAdapter.setSelectedDay(mSelectedDay);
2793e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
2803e9818e0267619fecebd55095ab26c53eff92e93James Kung
2813e9818e0267619fecebd55095ab26c53eff92e93James Kung        if (Log.isLoggable(TAG, Log.DEBUG)) {
2823e9818e0267619fecebd55095ab26c53eff92e93James Kung            Log.d(TAG, "GoTo position " + position);
2833e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
2843e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Check if the selected day is now outside of our visible range
2853e9818e0267619fecebd55095ab26c53eff92e93James Kung        // and if so scroll to the month that contains it
2863e9818e0267619fecebd55095ab26c53eff92e93James Kung        if (position != selectedPosition || forceScroll) {
2873e9818e0267619fecebd55095ab26c53eff92e93James Kung            setMonthDisplayed(mTempDay);
2883e9818e0267619fecebd55095ab26c53eff92e93James Kung            mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
2893e9818e0267619fecebd55095ab26c53eff92e93James Kung            if (animate) {
2903e9818e0267619fecebd55095ab26c53eff92e93James Kung                smoothScrollToPositionFromTop(
2913e9818e0267619fecebd55095ab26c53eff92e93James Kung                        position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION);
2923e9818e0267619fecebd55095ab26c53eff92e93James Kung                return true;
2933e9818e0267619fecebd55095ab26c53eff92e93James Kung            } else {
2943e9818e0267619fecebd55095ab26c53eff92e93James Kung                setSelectionFromTop(position, LIST_TOP_OFFSET);
2953e9818e0267619fecebd55095ab26c53eff92e93James Kung                onScrollStateChanged(this, OnScrollListener.SCROLL_STATE_IDLE);
2963e9818e0267619fecebd55095ab26c53eff92e93James Kung            }
2973e9818e0267619fecebd55095ab26c53eff92e93James Kung        } else if (setSelected) {
2983e9818e0267619fecebd55095ab26c53eff92e93James Kung            setMonthDisplayed(mSelectedDay);
2993e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
3003e9818e0267619fecebd55095ab26c53eff92e93James Kung        return false;
3013e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
3023e9818e0267619fecebd55095ab26c53eff92e93James Kung
3033e9818e0267619fecebd55095ab26c53eff92e93James Kung    /**
3043e9818e0267619fecebd55095ab26c53eff92e93James Kung     * Updates the title and selected month if the view has moved to a new
3053e9818e0267619fecebd55095ab26c53eff92e93James Kung     * month.
3063e9818e0267619fecebd55095ab26c53eff92e93James Kung     */
3073e9818e0267619fecebd55095ab26c53eff92e93James Kung    @Override
3083e9818e0267619fecebd55095ab26c53eff92e93James Kung    public void onScroll(
3093e9818e0267619fecebd55095ab26c53eff92e93James Kung            AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
3103e9818e0267619fecebd55095ab26c53eff92e93James Kung        SimpleMonthView child = (SimpleMonthView) view.getChildAt(0);
3113e9818e0267619fecebd55095ab26c53eff92e93James Kung        if (child == null) {
3123e9818e0267619fecebd55095ab26c53eff92e93James Kung            return;
3133e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
3143e9818e0267619fecebd55095ab26c53eff92e93James Kung
3153e9818e0267619fecebd55095ab26c53eff92e93James Kung        // Figure out where we are
3163e9818e0267619fecebd55095ab26c53eff92e93James Kung        long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
3173e9818e0267619fecebd55095ab26c53eff92e93James Kung        mPreviousScrollPosition = currScroll;
3183e9818e0267619fecebd55095ab26c53eff92e93James Kung        mPreviousScrollState = mCurrentScrollState;
3193e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
3203e9818e0267619fecebd55095ab26c53eff92e93James Kung
3213e9818e0267619fecebd55095ab26c53eff92e93James Kung    /**
3223e9818e0267619fecebd55095ab26c53eff92e93James Kung     * Sets the month displayed at the top of this view based on time. Override
3233e9818e0267619fecebd55095ab26c53eff92e93James Kung     * to add custom events when the title is changed.
3243e9818e0267619fecebd55095ab26c53eff92e93James Kung     */
3253e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected void setMonthDisplayed(CalendarDay date) {
3263e9818e0267619fecebd55095ab26c53eff92e93James Kung        mCurrentMonthDisplayed = date.month;
3273e9818e0267619fecebd55095ab26c53eff92e93James Kung        invalidateViews();
3283e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
3293e9818e0267619fecebd55095ab26c53eff92e93James Kung
3303e9818e0267619fecebd55095ab26c53eff92e93James Kung    @Override
3313e9818e0267619fecebd55095ab26c53eff92e93James Kung    public void onScrollStateChanged(AbsListView view, int scrollState) {
3323e9818e0267619fecebd55095ab26c53eff92e93James Kung        // use a post to prevent re-entering onScrollStateChanged before it
3333e9818e0267619fecebd55095ab26c53eff92e93James Kung        // exits
3343e9818e0267619fecebd55095ab26c53eff92e93James Kung        mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
3353e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
3363e9818e0267619fecebd55095ab26c53eff92e93James Kung
3373e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
3383e9818e0267619fecebd55095ab26c53eff92e93James Kung
3393e9818e0267619fecebd55095ab26c53eff92e93James Kung    protected class ScrollStateRunnable implements Runnable {
3403e9818e0267619fecebd55095ab26c53eff92e93James Kung        private int mNewState;
3413e9818e0267619fecebd55095ab26c53eff92e93James Kung
3423e9818e0267619fecebd55095ab26c53eff92e93James Kung        /**
3433e9818e0267619fecebd55095ab26c53eff92e93James Kung         * Sets up the runnable with a short delay in case the scroll state
3443e9818e0267619fecebd55095ab26c53eff92e93James Kung         * immediately changes again.
3453e9818e0267619fecebd55095ab26c53eff92e93James Kung         *
3463e9818e0267619fecebd55095ab26c53eff92e93James Kung         * @param view The list view that changed state
3473e9818e0267619fecebd55095ab26c53eff92e93James Kung         * @param scrollState The new state it changed to
3483e9818e0267619fecebd55095ab26c53eff92e93James Kung         */
3493e9818e0267619fecebd55095ab26c53eff92e93James Kung        public void doScrollStateChange(AbsListView view, int scrollState) {
3503e9818e0267619fecebd55095ab26c53eff92e93James Kung            mHandler.removeCallbacks(this);
3513e9818e0267619fecebd55095ab26c53eff92e93James Kung            mNewState = scrollState;
3523e9818e0267619fecebd55095ab26c53eff92e93James Kung            mHandler.postDelayed(this, SCROLL_CHANGE_DELAY);
3533e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
3543e9818e0267619fecebd55095ab26c53eff92e93James Kung
3553e9818e0267619fecebd55095ab26c53eff92e93James Kung        @Override
3563e9818e0267619fecebd55095ab26c53eff92e93James Kung        public void run() {
3573e9818e0267619fecebd55095ab26c53eff92e93James Kung            mCurrentScrollState = mNewState;
3583e9818e0267619fecebd55095ab26c53eff92e93James Kung            if (Log.isLoggable(TAG, Log.DEBUG)) {
3593e9818e0267619fecebd55095ab26c53eff92e93James Kung                Log.d(TAG,
3603e9818e0267619fecebd55095ab26c53eff92e93James Kung                        "new scroll state: " + mNewState + " old state: " + mPreviousScrollState);
3613e9818e0267619fecebd55095ab26c53eff92e93James Kung            }
3623e9818e0267619fecebd55095ab26c53eff92e93James Kung            // Fix the position after a scroll or a fling ends
3633e9818e0267619fecebd55095ab26c53eff92e93James Kung            if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
3643e9818e0267619fecebd55095ab26c53eff92e93James Kung                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE
3653e9818e0267619fecebd55095ab26c53eff92e93James Kung                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
3663e9818e0267619fecebd55095ab26c53eff92e93James Kung                mPreviousScrollState = mNewState;
3673e9818e0267619fecebd55095ab26c53eff92e93James Kung                int i = 0;
3683e9818e0267619fecebd55095ab26c53eff92e93James Kung                View child = getChildAt(i);
3693e9818e0267619fecebd55095ab26c53eff92e93James Kung                while (child != null && child.getBottom() <= 0) {
3703e9818e0267619fecebd55095ab26c53eff92e93James Kung                    child = getChildAt(++i);
3713e9818e0267619fecebd55095ab26c53eff92e93James Kung                }
3723e9818e0267619fecebd55095ab26c53eff92e93James Kung                if (child == null) {
3733e9818e0267619fecebd55095ab26c53eff92e93James Kung                    // The view is no longer visible, just return
3743e9818e0267619fecebd55095ab26c53eff92e93James Kung                    return;
3753e9818e0267619fecebd55095ab26c53eff92e93James Kung                }
3763e9818e0267619fecebd55095ab26c53eff92e93James Kung                int firstPosition = getFirstVisiblePosition();
3773e9818e0267619fecebd55095ab26c53eff92e93James Kung                int lastPosition = getLastVisiblePosition();
3783e9818e0267619fecebd55095ab26c53eff92e93James Kung                boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1;
3793e9818e0267619fecebd55095ab26c53eff92e93James Kung                final int top = child.getTop();
3803e9818e0267619fecebd55095ab26c53eff92e93James Kung                final int bottom = child.getBottom();
3813e9818e0267619fecebd55095ab26c53eff92e93James Kung                final int midpoint = getHeight() / 2;
3823e9818e0267619fecebd55095ab26c53eff92e93James Kung                if (scroll && top < LIST_TOP_OFFSET) {
3833e9818e0267619fecebd55095ab26c53eff92e93James Kung                    if (bottom > midpoint) {
3843e9818e0267619fecebd55095ab26c53eff92e93James Kung                        smoothScrollBy(top, GOTO_SCROLL_DURATION);
3853e9818e0267619fecebd55095ab26c53eff92e93James Kung                    } else {
3863e9818e0267619fecebd55095ab26c53eff92e93James Kung                        smoothScrollBy(bottom, GOTO_SCROLL_DURATION);
3873e9818e0267619fecebd55095ab26c53eff92e93James Kung                    }
3883e9818e0267619fecebd55095ab26c53eff92e93James Kung                }
3893e9818e0267619fecebd55095ab26c53eff92e93James Kung            } else {
3903e9818e0267619fecebd55095ab26c53eff92e93James Kung                mPreviousScrollState = mNewState;
3913e9818e0267619fecebd55095ab26c53eff92e93James Kung            }
3923e9818e0267619fecebd55095ab26c53eff92e93James Kung        }
3933e9818e0267619fecebd55095ab26c53eff92e93James Kung    }
3943e9818e0267619fecebd55095ab26c53eff92e93James Kung}
395