14612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette/*
24612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * Copyright (C) 2014 The Android Open Source Project
34612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette *
44612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
54612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * you may not use this file except in compliance with the License.
64612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * You may obtain a copy of the License at
74612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette *
84612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
94612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette *
104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * Unless required by applicable law or agreed to in writing, software
114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * See the License for the specific language governing permissions and
144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * limitations under the License.
154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette */
164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverettepackage android.widget;
184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport com.android.internal.R;
204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.app.Service;
224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.content.Context;
234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.content.res.Configuration;
244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.content.res.TypedArray;
254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.database.DataSetObserver;
264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.graphics.Canvas;
274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.graphics.Paint;
284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.graphics.Rect;
294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.graphics.drawable.Drawable;
304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.text.TextUtils;
314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.text.format.DateUtils;
324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.util.AttributeSet;
334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.util.DisplayMetrics;
344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.util.TypedValue;
354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.view.GestureDetector;
364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.view.LayoutInflater;
374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.view.MotionEvent;
384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.view.View;
394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport android.view.ViewGroup;
404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport java.util.Calendar;
424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport java.util.Locale;
434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteimport libcore.icu.LocaleData;
454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette/**
474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette * A delegate implementing the legacy CalendarView
484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette */
494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viveretteclass CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelegate {
504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Default value whether to show week number.
524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The number of milliseconds in a day.e
574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final long MILLIS_IN_DAY = 86400000L;
594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The number of day in a week.
624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int DAYS_PER_WEEK = 7;
644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The number of milliseconds in a week.
674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Affects when the month selection will change while scrolling upe
724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int SCROLL_HYST_WEEKS = 2;
744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * How long the GoTo fling animation should last.
774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int GOTO_SCROLL_DURATION = 1000;
794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The duration of the adjustment upon a user scroll in milliseconds.
824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int ADJUSTMENT_SCROLL_DURATION = 500;
844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * How long to wait after receiving an onScrollStateChanged notification
874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * before acting on it.
884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int SCROLL_CHANGE_DELAY = 40;
904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int DEFAULT_DATE_TEXT_SIZE = 14;
944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
1004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int UNSCALED_BOTTOM_BUFFER = 20;
1024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
1044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
1064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private final int mWeekSeperatorLineWidth;
1084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mDateTextSize;
1104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private Drawable mSelectedDateVerticalBar;
1124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private final int mSelectedDateVerticalBarWidth;
1144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mSelectedWeekBackgroundColor;
1164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mFocusedMonthDateColor;
1184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mUnfocusedMonthDateColor;
1204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mWeekSeparatorLineColor;
1224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mWeekNumberColor;
1244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mWeekDayTextAppearanceResId;
1264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mDateTextAppearanceResId;
1284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The top offset of the weeks list.
1314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mListScrollTopOffset = 2;
1334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The visible height of a week view.
1364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mWeekMinVisibleHeight = 12;
1384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The visible height of a week view.
1414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mBottomBuffer = 20;
1434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The number of shown weeks.
1464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mShownWeekCount;
1484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Flag whether to show the week number.
1514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private boolean mShowWeekNumber;
1534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The number of day per week to be shown.
1564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mDaysPerWeek = 7;
1584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The friction of the week list while flinging.
1614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private float mFriction = .05f;
1634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Scale for adjusting velocity of the week list while flinging.
1664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private float mVelocityScale = 0.333f;
1684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The adapter for the weeks list.
1714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private WeeksAdapter mAdapter;
1734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The weeks list.
1764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private ListView mListView;
1784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The name of the month to display.
1814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private TextView mMonthName;
1834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The header with week day names.
1864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private ViewGroup mDayNamesHeader;
1884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Cached abbreviations for day of week names.
1914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private String[] mDayNamesShort;
1934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
1954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Cached full-length day of week names.
1964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
1974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private String[] mDayNamesLong;
1984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
1994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The first day of the week.
2014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mFirstDayOfWeek;
2034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Which month should be displayed/highlighted [0-11].
2064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mCurrentMonthDisplayed = -1;
2084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Used for tracking during a scroll.
2114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private long mPreviousScrollPosition;
2134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Used for tracking which direction the view is scrolling.
2164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private boolean mIsScrollingUp = false;
2184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The previous scroll state of the weeks ListView.
2214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mPreviousScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
2234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The current scroll state of the weeks ListView.
2264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int mCurrentScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
2284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Listener for changes in the selected day.
2314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private CalendarView.OnDateChangeListener mOnDateChangeListener;
2334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Command for adjusting the position after a scroll/fling.
2364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
2384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Temporary instance to avoid multiple instantiations.
2414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private Calendar mTempDate;
2434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The first day of the focused month.
2464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private Calendar mFirstDayOfMonth;
2484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The start date of the range supported by this picker.
2514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private Calendar mMinDate;
2534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
2554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * The end date of the range supported by this picker.
2564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
2574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private Calendar mMaxDate;
2584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    CalendarViewLegacyDelegate(CalendarView delegator, Context context, AttributeSet attrs,
2604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int defStyleAttr, int defStyleRes) {
2614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        super(delegator, context);
2624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        final TypedArray a = context.obtainStyledAttributes(attrs,
2644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.CalendarView, defStyleAttr, defStyleRes);
2654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mShowWeekNumber = a.getBoolean(R.styleable.CalendarView_showWeekNumber,
2664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                DEFAULT_SHOW_WEEK_NUMBER);
2674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
2684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                LocaleData.get(Locale.getDefault()).firstDayOfWeek);
2694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        final String minDate = a.getString(R.styleable.CalendarView_minDate);
2704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
2714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            parseDate(DEFAULT_MIN_DATE, mMinDate);
2724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
2734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
2744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
2754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            parseDate(DEFAULT_MAX_DATE, mMaxDate);
2764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
2774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mMaxDate.before(mMinDate)) {
2784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            throw new IllegalArgumentException("Max date cannot be before min date.");
2794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
2804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mShownWeekCount = a.getInt(R.styleable.CalendarView_shownWeekCount,
2814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                DEFAULT_SHOWN_WEEK_COUNT);
2824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mSelectedWeekBackgroundColor = a.getColor(
2834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
2844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mFocusedMonthDateColor = a.getColor(
2854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.CalendarView_focusedMonthDateColor, 0);
2864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mUnfocusedMonthDateColor = a.getColor(
2874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.CalendarView_unfocusedMonthDateColor, 0);
2884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mWeekSeparatorLineColor = a.getColor(
2894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.CalendarView_weekSeparatorLineColor, 0);
2904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mWeekNumberColor = a.getColor(R.styleable.CalendarView_weekNumberColor, 0);
2914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mSelectedDateVerticalBar = a.getDrawable(
2924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.CalendarView_selectedDateVerticalBar);
2934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mDateTextAppearanceResId = a.getResourceId(
2954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
2964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        updateDateTextSize();
2974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
2984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mWeekDayTextAppearanceResId = a.getResourceId(
2994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.CalendarView_weekDayTextAppearance,
3004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
3014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        a.recycle();
3024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        DisplayMetrics displayMetrics = mDelegator.getResources().getDisplayMetrics();
3044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
3054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
3064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
3074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
3084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
3094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                UNSCALED_BOTTOM_BUFFER, displayMetrics);
3104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
3114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
3124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
3134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
3144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        LayoutInflater layoutInflater = (LayoutInflater) mContext
3164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
3174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
3184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mDelegator.addView(content);
3194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mListView = (ListView) mDelegator.findViewById(R.id.list);
3214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mDayNamesHeader = (ViewGroup) content.findViewById(R.id.day_names);
3224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mMonthName = (TextView) content.findViewById(R.id.month_name);
3234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        setUpHeader();
3254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        setUpListView();
3264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        setUpAdapter();
3274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // go to today or whichever is close to today min or max date
3294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mTempDate.setTimeInMillis(System.currentTimeMillis());
3304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mTempDate.before(mMinDate)) {
3314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            goTo(mMinDate, false, true, true);
3324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else if (mMaxDate.before(mTempDate)) {
3334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            goTo(mMaxDate, false, true, true);
3344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else {
3354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            goTo(mTempDate, false, true, true);
3364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
3374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mDelegator.invalidate();
3394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
3404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
3424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setShownWeekCount(int count) {
3434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mShownWeekCount != count) {
3444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mShownWeekCount = count;
3454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDelegator.invalidate();
3464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
3474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
3484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
3504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getShownWeekCount() {
3514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mShownWeekCount;
3524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
3534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
3554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setSelectedWeekBackgroundColor(int color) {
3564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mSelectedWeekBackgroundColor != color) {
3574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedWeekBackgroundColor = color;
3584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final int childCount = mListView.getChildCount();
3594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            for (int i = 0; i < childCount; i++) {
3604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                WeekView weekView = (WeekView) mListView.getChildAt(i);
3614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (weekView.mHasSelectedDay) {
3624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    weekView.invalidate();
3634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
3644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
3654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
3664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
3674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
3694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getSelectedWeekBackgroundColor() {
3704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mSelectedWeekBackgroundColor;
3714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
3724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
3744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setFocusedMonthDateColor(int color) {
3754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mFocusedMonthDateColor != color) {
3764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mFocusedMonthDateColor = color;
3774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final int childCount = mListView.getChildCount();
3784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            for (int i = 0; i < childCount; i++) {
3794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                WeekView weekView = (WeekView) mListView.getChildAt(i);
3804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (weekView.mHasFocusedDay) {
3814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    weekView.invalidate();
3824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
3834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
3844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
3854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
3864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
3884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getFocusedMonthDateColor() {
3894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mFocusedMonthDateColor;
3904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
3914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
3924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
3934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setUnfocusedMonthDateColor(int color) {
3944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mUnfocusedMonthDateColor != color) {
3954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mUnfocusedMonthDateColor = color;
3964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final int childCount = mListView.getChildCount();
3974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            for (int i = 0; i < childCount; i++) {
3984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                WeekView weekView = (WeekView) mListView.getChildAt(i);
3994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (weekView.mHasUnfocusedDay) {
4004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    weekView.invalidate();
4014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
4024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
4034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
4044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getUnfocusedMonthDateColor() {
4084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mFocusedMonthDateColor;
4094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setWeekNumberColor(int color) {
4134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mWeekNumberColor != color) {
4144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mWeekNumberColor = color;
4154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mShowWeekNumber) {
4164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                invalidateAllWeekViews();
4174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
4184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
4194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getWeekNumberColor() {
4234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mWeekNumberColor;
4244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setWeekSeparatorLineColor(int color) {
4284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mWeekSeparatorLineColor != color) {
4294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mWeekSeparatorLineColor = color;
4304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            invalidateAllWeekViews();
4314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
4324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getWeekSeparatorLineColor() {
4364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mWeekSeparatorLineColor;
4374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setSelectedDateVerticalBar(int resourceId) {
4414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        Drawable drawable = mDelegator.getContext().getDrawable(resourceId);
4424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        setSelectedDateVerticalBar(drawable);
4434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setSelectedDateVerticalBar(Drawable drawable) {
4474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mSelectedDateVerticalBar != drawable) {
4484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedDateVerticalBar = drawable;
4494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final int childCount = mListView.getChildCount();
4504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            for (int i = 0; i < childCount; i++) {
4514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                WeekView weekView = (WeekView) mListView.getChildAt(i);
4524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (weekView.mHasSelectedDay) {
4534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    weekView.invalidate();
4544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
4554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
4564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
4574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public Drawable getSelectedDateVerticalBar() {
4614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mSelectedDateVerticalBar;
4624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setWeekDayTextAppearance(int resourceId) {
4664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mWeekDayTextAppearanceResId != resourceId) {
4674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mWeekDayTextAppearanceResId = resourceId;
4684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            setUpHeader();
4694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
4704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getWeekDayTextAppearance() {
4744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mWeekDayTextAppearanceResId;
4754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setDateTextAppearance(int resourceId) {
4794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mDateTextAppearanceResId != resourceId) {
4804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDateTextAppearanceResId = resourceId;
4814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            updateDateTextSize();
4824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            invalidateAllWeekViews();
4834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
4844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getDateTextAppearance() {
4884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mDateTextAppearanceResId;
4894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
4904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
4914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
4924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setMinDate(long minDate) {
4934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mTempDate.setTimeInMillis(minDate);
4944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (isSameDate(mTempDate, mMinDate)) {
4954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return;
4964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
4974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mMinDate.setTimeInMillis(minDate);
4984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // make sure the current date is not earlier than
4994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // the new min date since the latter is used for
5004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // calculating the indices in the adapter thus
5014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // avoiding out of bounds error
5024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        Calendar date = mAdapter.mSelectedDate;
5034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (date.before(mMinDate)) {
5044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mAdapter.setSelectedDay(mMinDate);
5054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
5064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // reinitialize the adapter since its range depends on min date
5074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mAdapter.init();
5084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (date.before(mMinDate)) {
5094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            setDate(mTempDate.getTimeInMillis());
5104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else {
5114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // we go to the current date to force the ListView to query its
5124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // adapter for the shown views since we have changed the adapter
5134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // range and the base from which the later calculates item indices
5144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // note that calling setDate will not work since the date is the same
5154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            goTo(date, false, true, false);
5164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
5174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public long getMinDate() {
5214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mMinDate.getTimeInMillis();
5224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setMaxDate(long maxDate) {
5264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mTempDate.setTimeInMillis(maxDate);
5274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (isSameDate(mTempDate, mMaxDate)) {
5284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return;
5294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
5304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mMaxDate.setTimeInMillis(maxDate);
5314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // reinitialize the adapter since its range depends on max date
5324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mAdapter.init();
5334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        Calendar date = mAdapter.mSelectedDate;
5344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (date.after(mMaxDate)) {
5354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            setDate(mMaxDate.getTimeInMillis());
5364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else {
5374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // we go to the current date to force the ListView to query its
5384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // adapter for the shown views since we have changed the adapter
5394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // range and the base from which the later calculates item indices
5404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // note that calling setDate will not work since the date is the same
5414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            goTo(date, false, true, false);
5424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
5434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public long getMaxDate() {
5474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mMaxDate.getTimeInMillis();
5484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setShowWeekNumber(boolean showWeekNumber) {
5524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mShowWeekNumber == showWeekNumber) {
5534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return;
5544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
5554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mShowWeekNumber = showWeekNumber;
5564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mAdapter.notifyDataSetChanged();
5574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        setUpHeader();
5584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public boolean getShowWeekNumber() {
5624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mShowWeekNumber;
5634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setFirstDayOfWeek(int firstDayOfWeek) {
5674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mFirstDayOfWeek == firstDayOfWeek) {
5684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return;
5694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
5704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mFirstDayOfWeek = firstDayOfWeek;
5714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mAdapter.init();
5724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mAdapter.notifyDataSetChanged();
5734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        setUpHeader();
5744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public int getFirstDayOfWeek() {
5784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mFirstDayOfWeek;
5794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setDate(long date) {
5834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        setDate(date, false, false);
5844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setDate(long date, boolean animate, boolean center) {
5884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mTempDate.setTimeInMillis(date);
5894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
5904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return;
5914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
5924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        goTo(mTempDate, animate, true, center);
5934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
5954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
5964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public long getDate() {
5974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return mAdapter.mSelectedDate.getTimeInMillis();
5984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
5994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
6014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void setOnDateChangeListener(CalendarView.OnDateChangeListener listener) {
6024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mOnDateChangeListener = listener;
6034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
6044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
6064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    public void onConfigurationChanged(Configuration newConfig) {
6074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        setCurrentLocale(newConfig.locale);
6084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
6094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
6114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Sets the current locale.
6124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *
6134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @param locale The current locale.
6144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
6154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    @Override
6164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    protected void setCurrentLocale(Locale locale) {
6174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        super.setCurrentLocale(locale);
6184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mTempDate = getCalendarForLocale(mTempDate, locale);
6204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
6214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mMinDate = getCalendarForLocale(mMinDate, locale);
6224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mMaxDate = getCalendarForLocale(mMaxDate, locale);
6234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
6244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void updateDateTextSize() {
6254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        TypedArray dateTextAppearance = mDelegator.getContext().obtainStyledAttributes(
6264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mDateTextAppearanceResId, R.styleable.TextAppearance);
6274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mDateTextSize = dateTextAppearance.getDimensionPixelSize(
6284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
6294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        dateTextAppearance.recycle();
6304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
6314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
6334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Invalidates all week views.
6344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
6354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void invalidateAllWeekViews() {
6364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        final int childCount = mListView.getChildCount();
6374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        for (int i = 0; i < childCount; i++) {
6384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            View view = mListView.getChildAt(i);
6394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            view.invalidate();
6404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
6414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
6424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
6444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Gets a calendar for locale bootstrapped with the value of a given calendar.
6454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *
6464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @param oldCalendar The old calendar.
6474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @param locale The locale.
6484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
6494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
6504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (oldCalendar == null) {
6514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return Calendar.getInstance(locale);
6524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else {
6534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final long currentTimeMillis = oldCalendar.getTimeInMillis();
6544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            Calendar newCalendar = Calendar.getInstance(locale);
6554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            newCalendar.setTimeInMillis(currentTimeMillis);
6564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return newCalendar;
6574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
6584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
6594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
6614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @return True if the <code>firstDate</code> is the same as the <code>
6624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * secondDate</code>.
6634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
6644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private static boolean isSameDate(Calendar firstDate, Calendar secondDate) {
6654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
6664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
6674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
6684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
6704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Creates a new adapter if necessary and sets up its parameters.
6714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
6724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void setUpAdapter() {
6734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mAdapter == null) {
6744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mAdapter = new WeeksAdapter(mContext);
6754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mAdapter.registerDataSetObserver(new DataSetObserver() {
6764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                @Override
6774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                public void onChanged() {
6784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    if (mOnDateChangeListener != null) {
6794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                        Calendar selectedDay = mAdapter.getSelectedDay();
6804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                        mOnDateChangeListener.onSelectedDayChange(mDelegator,
6814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                                selectedDay.get(Calendar.YEAR),
6824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                                selectedDay.get(Calendar.MONTH),
6834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                                selectedDay.get(Calendar.DAY_OF_MONTH));
6844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    }
6854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
6864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            });
6874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mListView.setAdapter(mAdapter);
6884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
6894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // refresh the view with the new parameters
6914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mAdapter.notifyDataSetChanged();
6924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
6934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
6944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
6954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Sets up the strings to be used by the header.
6964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
6974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void setUpHeader() {
6984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mDayNamesShort = new String[mDaysPerWeek];
6994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mDayNamesLong = new String[mDaysPerWeek];
7004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        for (int i = mFirstDayOfWeek, count = mFirstDayOfWeek + mDaysPerWeek; i < count; i++) {
7014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int calendarDay = (i > Calendar.SATURDAY) ? i - Calendar.SATURDAY : i;
7024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDayNamesShort[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
7034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    DateUtils.LENGTH_SHORTEST);
7044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDayNamesLong[i - mFirstDayOfWeek] = DateUtils.getDayOfWeekString(calendarDay,
7054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    DateUtils.LENGTH_LONG);
7064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
7074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
7084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        TextView label = (TextView) mDayNamesHeader.getChildAt(0);
7094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mShowWeekNumber) {
7104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            label.setVisibility(View.VISIBLE);
7114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else {
7124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            label.setVisibility(View.GONE);
7134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
7144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        for (int i = 1, count = mDayNamesHeader.getChildCount(); i < count; i++) {
7154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            label = (TextView) mDayNamesHeader.getChildAt(i);
7164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mWeekDayTextAppearanceResId > -1) {
7174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
7184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
7194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (i < mDaysPerWeek + 1) {
7204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                label.setText(mDayNamesShort[i - 1]);
7214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                label.setContentDescription(mDayNamesLong[i - 1]);
7224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                label.setVisibility(View.VISIBLE);
7234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
7244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                label.setVisibility(View.GONE);
7254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
7264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
7274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mDayNamesHeader.invalidate();
7284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
7294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
7304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
7314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Sets all the required fields for the list view.
7324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
7334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void setUpListView() {
7344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Configure the listview
7354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mListView.setDivider(null);
7364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mListView.setItemsCanFocus(true);
7374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mListView.setVerticalScrollBarEnabled(false);
7384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
7394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            public void onScrollStateChanged(AbsListView view, int scrollState) {
7404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                CalendarViewLegacyDelegate.this.onScrollStateChanged(view, scrollState);
7414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
7424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
7434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            public void onScroll(
7444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    AbsListView view, int firstVisibleItem, int visibleItemCount,
7454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    int totalItemCount) {
7464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                CalendarViewLegacyDelegate.this.onScroll(view, firstVisibleItem,
7474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                        visibleItemCount, totalItemCount);
7484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
7494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        });
7504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Make the scrolling behavior nicer
7514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mListView.setFriction(mFriction);
7524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mListView.setVelocityScale(mVelocityScale);
7534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
7544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
7554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
7564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * This moves to the specified time in the view. If the time is not already
7574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * in range it will move the list so that the first of the month containing
7584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * the time is at the top of the view. If the new time is already in view
7594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * the list will not be scrolled unless forceScroll is true. This time may
7604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * optionally be highlighted as selected as well.
7614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *
7624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @param date The time to move to.
7634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @param animate Whether to scroll to the given time or just redraw at the
7644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *            new location.
7654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @param setSelected Whether to set the given time as selected.
7664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @param forceScroll Whether to recenter even if the time is already
7674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *            visible.
7684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *
7694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @throws IllegalArgumentException of the provided date is before the
7704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *        range start of after the range end.
7714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
7724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void goTo(Calendar date, boolean animate, boolean setSelected,
7734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            boolean forceScroll) {
7744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (date.before(mMinDate) || date.after(mMaxDate)) {
7754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
7764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    + " and " + mMaxDate.getTime());
7774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
7784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Find the first and last entirely visible weeks
7794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
7804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        View firstChild = mListView.getChildAt(0);
7814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (firstChild != null && firstChild.getTop() < 0) {
7824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            firstFullyVisiblePosition++;
7834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
7844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
7854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
7864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            lastFullyVisiblePosition--;
7874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
7884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (setSelected) {
7894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mAdapter.setSelectedDay(date);
7904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
7914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Get the week we're going to
7924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        int position = getWeeksSinceMinDate(date);
7934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
7944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Check if the selected day is now outside of our visible range
7954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // and if so scroll to the month that contains it
7964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
7974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                || forceScroll) {
7984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
7994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
8004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            setMonthDisplayed(mFirstDayOfMonth);
8024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // the earliest time we can scroll to is the min date
8044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mFirstDayOfMonth.before(mMinDate)) {
8054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                position = 0;
8064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
8074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                position = getWeeksSinceMinDate(mFirstDayOfMonth);
8084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
8094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mPreviousScrollState = AbsListView.OnScrollListener.SCROLL_STATE_FLING;
8114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (animate) {
8124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
8134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                        GOTO_SCROLL_DURATION);
8144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
8154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mListView.setSelectionFromTop(position, mListScrollTopOffset);
8164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                // Perform any after scroll operations that are needed
8174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                onScrollStateChanged(mListView, AbsListView.OnScrollListener.SCROLL_STATE_IDLE);
8184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
8194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else if (setSelected) {
8204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // Otherwise just set the selection
8214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            setMonthDisplayed(date);
8224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
8234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
8244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
8264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Called when a <code>view</code> transitions to a new <code>scrollState
8274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * </code>.
8284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
8294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void onScrollStateChanged(AbsListView view, int scrollState) {
8304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
8314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
8324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
8344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Updates the title and selected month if the <code>view</code> has moved to a new
8354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * month.
8364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
8374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
8384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                          int totalItemCount) {
8394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        WeekView child = (WeekView) view.getChildAt(0);
8404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (child == null) {
8414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return;
8424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
8434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Figure out where we are
8454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        long currScroll =
8464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
8474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // If we have moved since our last call update the direction
8494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (currScroll < mPreviousScrollPosition) {
8504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mIsScrollingUp = true;
8514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else if (currScroll > mPreviousScrollPosition) {
8524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mIsScrollingUp = false;
8534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else {
8544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return;
8554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
8564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Use some hysteresis for checking which month to highlight. This
8584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // causes the month to transition when two full weeks of a month are
8594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // visible when scrolling up, and when the first day in a month reaches
8604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // the top of the screen when scrolling down.
8614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
8624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (mIsScrollingUp) {
8634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
8644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        } else if (offset != 0) {
8654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            child = (WeekView) view.getChildAt(offset);
8664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
8674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (child != null) {
8694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // Find out which month we're moving into
8704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int month;
8714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mIsScrollingUp) {
8724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                month = child.getMonthOfFirstWeekDay();
8734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
8744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                month = child.getMonthOfLastWeekDay();
8754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
8764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // And how it relates to our current highlighted month
8784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int monthDiff;
8794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mCurrentMonthDisplayed == 11 && month == 0) {
8804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                monthDiff = 1;
8814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else if (mCurrentMonthDisplayed == 0 && month == 11) {
8824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                monthDiff = -1;
8834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
8844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                monthDiff = month - mCurrentMonthDisplayed;
8854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
8864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
8874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // Only switch months if we're scrolling away from the currently
8884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // selected month
8894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
8904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                Calendar firstDay = child.getFirstDay();
8914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (mIsScrollingUp) {
8924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
8934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                } else {
8944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
8954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
8964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                setMonthDisplayed(firstDay);
8974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
8984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
8994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mPreviousScrollPosition = currScroll;
9004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mPreviousScrollState = mCurrentScrollState;
9014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
9024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
9044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Sets the month displayed at the top of this view based on time. Override
9054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * to add custom events when the title is changed.
9064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *
9074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @param calendar A day in the new focus month.
9084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
9094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private void setMonthDisplayed(Calendar calendar) {
9104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
9114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mAdapter.setFocusMonth(mCurrentMonthDisplayed);
9124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
9134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                | DateUtils.FORMAT_SHOW_YEAR;
9144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        final long millis = calendar.getTimeInMillis();
9154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
9164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mMonthName.setText(newMonthName);
9174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        mMonthName.invalidate();
9184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
9194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
9214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * @return Returns the number of weeks between the current <code>date</code>
9224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     *         and the <code>mMinDate</code>.
9234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
9244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private int getWeeksSinceMinDate(Calendar date) {
9254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        if (date.before(mMinDate)) {
9264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
9274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    + " does not precede toDate: " + date.getTime());
9284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
9294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        long endTimeMillis = date.getTimeInMillis()
9304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                + date.getTimeZone().getOffset(date.getTimeInMillis());
9314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        long startTimeMillis = mMinDate.getTimeInMillis()
9324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                + mMinDate.getTimeZone().getOffset(mMinDate.getTimeInMillis());
9334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        long dayOffsetMillis = (mMinDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek)
9344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                * MILLIS_IN_DAY;
9354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        return (int) ((endTimeMillis - startTimeMillis + dayOffsetMillis) / MILLIS_IN_WEEK);
9364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
9374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
9394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * Command responsible for acting upon scroll state changes.
9404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
9414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private class ScrollStateRunnable implements Runnable {
9424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private AbsListView mView;
9434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mNewState;
9454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
9474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Sets up the runnable with a short delay in case the scroll state
9484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * immediately changes again.
9494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
9504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param view The list view that changed state
9514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param scrollState The new state it changed to
9524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
9534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public void doScrollStateChange(AbsListView view, int scrollState) {
9544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mView = view;
9554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mNewState = scrollState;
9564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDelegator.removeCallbacks(this);
9574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDelegator.postDelayed(this, SCROLL_CHANGE_DELAY);
9584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
9594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public void run() {
9614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mCurrentScrollState = mNewState;
9624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // Fix the position after a scroll or a fling ends
9634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mNewState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE
9644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    && mPreviousScrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
9654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                View child = mView.getChildAt(0);
9664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (child == null) {
9674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    // The view is no longer visible, just return
9684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    return;
9694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
9704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                int dist = child.getBottom() - mListScrollTopOffset;
9714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (dist > mListScrollTopOffset) {
9724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    if (mIsScrollingUp) {
9734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                        mView.smoothScrollBy(dist - child.getHeight(),
9744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                                ADJUSTMENT_SCROLL_DURATION);
9754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    } else {
9764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                        mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
9774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    }
9784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
9794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
9804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mPreviousScrollState = mNewState;
9814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
9824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
9834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
9854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * <p>
9864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * This is a specialized adapter for creating a list of weeks with
9874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * selectable days. It can be configured to display the week number, start
9884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * the week on a given day, show a reduced number of days, or display an
9894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * arbitrary number of weeks at a time.
9904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * </p>
9914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
9924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private class WeeksAdapter extends BaseAdapter implements View.OnTouchListener {
9934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mSelectedWeek;
9954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private GestureDetector mGestureDetector;
9974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
9984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mFocusedMonth;
9994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private final Calendar mSelectedDate = Calendar.getInstance();
10014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mTotalWeekCount;
10034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public WeeksAdapter(Context context) {
10054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mContext = context;
10064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mGestureDetector = new GestureDetector(mContext, new WeeksAdapter.CalendarGestureListener());
10074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            init();
10084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
10114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Set up the gesture detector and selected time
10124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
10134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private void init() {
10144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
10154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
10164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
10174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
10184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTotalWeekCount++;
10194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
10204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            notifyDataSetChanged();
10214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
10244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Updates the selected day and related parameters.
10254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
10264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param selectedDay The time to highlight
10274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
10284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public void setSelectedDay(Calendar selectedDay) {
10294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
10304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
10314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                return;
10324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
10334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
10344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
10354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
10364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            notifyDataSetChanged();
10374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
10404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @return The selected day of month.
10414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
10424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public Calendar getSelectedDay() {
10434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return mSelectedDate;
10444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        @Override
10474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public int getCount() {
10484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return mTotalWeekCount;
10494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        @Override
10524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public Object getItem(int position) {
10534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return null;
10544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        @Override
10574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public long getItemId(int position) {
10584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return position;
10594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        @Override
10624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public View getView(int position, View convertView, ViewGroup parent) {
10634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            WeekView weekView = null;
10644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (convertView != null) {
10654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                weekView = (WeekView) convertView;
10664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
10674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                weekView = new WeekView(mContext);
10684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                AbsListView.LayoutParams params =
10694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                        new AbsListView.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
10704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                                FrameLayout.LayoutParams.WRAP_CONTENT);
10714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                weekView.setLayoutParams(params);
10724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                weekView.setClickable(true);
10734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                weekView.setOnTouchListener(this);
10744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
10754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
10774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    Calendar.DAY_OF_WEEK) : -1;
10784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            weekView.init(position, selectedWeekDay, mFocusedMonth);
10794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return weekView;
10814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
10844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Changes which month is in focus and updates the view.
10854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
10864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param month The month to show as in focus [0-11]
10874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
10884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public void setFocusMonth(int month) {
10894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mFocusedMonth == month) {
10904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                return;
10914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
10924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mFocusedMonth = month;
10934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            notifyDataSetChanged();
10944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
10954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
10964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        @Override
10974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public boolean onTouch(View v, MotionEvent event) {
10984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
10994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                WeekView weekView = (WeekView) v;
11004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                // if we cannot find a day for the given location we are done
11014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (!weekView.getDayFromLocation(event.getX(), mTempDate)) {
11024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    return true;
11034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
11044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                // it is possible that the touched day is outside the valid range
11054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                // we draw whole weeks but range end can fall not on the week end
11064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
11074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    return true;
11084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
11094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                onDateTapped(mTempDate);
11104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                return true;
11114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
11124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return false;
11134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
11144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
11164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Maintains the same hour/min/sec but moves the day to the tapped day.
11174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
11184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param day The day that was tapped
11194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
11204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private void onDateTapped(Calendar day) {
11214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            setSelectedDay(day);
11224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            setMonthDisplayed(day);
11234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
11244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
11264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * This is here so we can identify single tap events and set the
11274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * selected day correctly
11284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
11294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
11304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            @Override
11314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            public boolean onSingleTapUp(MotionEvent e) {
11324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                return true;
11334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
11344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
11354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
11364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    /**
11384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * <p>
11394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * This is a dynamic view for drawing a single week. It can be configured to
11404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * display the week number, start the week on a given day, or show a reduced
11414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * number of days. It is intended for use as a single view within a
11424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * ListView. See {@link WeeksAdapter} for usage.
11434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     * </p>
11444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette     */
11454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    private class WeekView extends View {
11464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private final Rect mTempRect = new Rect();
11484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private final Paint mDrawPaint = new Paint();
11504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private final Paint mMonthNumDrawPaint = new Paint();
11524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Cache the number strings so we don't have to recompute them each time
11544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private String[] mDayNumbers;
11554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Quick lookup for checking which days are in the focus month
11574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private boolean[] mFocusDay;
11584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Whether this view has a focused day.
11604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private boolean mHasFocusedDay;
11614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Whether this view has only focused days.
11634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private boolean mHasUnfocusedDay;
11644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // The first day displayed by this item
11664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private Calendar mFirstDay;
11674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // The month of the first day in this week
11694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mMonthOfFirstWeekDay = -1;
11704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // The month of the last day in this week
11724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mLastWeekDayMonth = -1;
11734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // The position of this week, equivalent to weeks since the week of Jan
11754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // 1st, 1900
11764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mWeek = -1;
11774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Quick reference to the width of this view, matches parent
11794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mWidth;
11804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // The height this view should draw at in pixels, set by height param
11824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mHeight;
11834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // If this view contains the selected day
11854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private boolean mHasSelectedDay = false;
11864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // Which day is selected [0-6] or -1 if no day is selected
11884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mSelectedDay = -1;
11894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // The number of days + a spot for week number if it is displayed
11914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mNumCells;
11924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // The left edge of the selected day
11944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mSelectedLeft = -1;
11954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        // The right edge of the selected day
11974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private int mSelectedRight = -1;
11984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
11994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public WeekView(Context context) {
12004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            super(context);
12014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // Sets up any standard paints that will be used
12034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            initilaizePaints();
12044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
12054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
12074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Initializes this week view.
12084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
12094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param weekNumber The number of the week this view represents. The
12104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *            week number is a zero based index of the weeks since
12114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *            {@link android.widget.CalendarView#getMinDate()}.
12124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
12134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *            selected day.
12144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param focusedMonth The month that is currently in focus i.e.
12154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *            highlighted.
12164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
12174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
12184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedDay = selectedWeekDay;
12194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mHasSelectedDay = mSelectedDay != -1;
12204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
12214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mWeek = weekNumber;
12224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
12234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
12254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
12264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // Allocate space for caching the day numbers and focus values
12284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDayNumbers = new String[mNumCells];
12294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mFocusDay = new boolean[mNumCells];
12304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // If we're showing the week number calculate it based on Monday
12324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int i = 0;
12334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mShowWeekNumber) {
12344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mDayNumbers[0] = String.format(Locale.getDefault(), "%d",
12354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                        mTempDate.get(Calendar.WEEK_OF_YEAR));
12364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                i++;
12374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
12384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // Now adjust our starting day based on the start day of the week
12404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
12414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mTempDate.add(Calendar.DAY_OF_MONTH, diff);
12424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mFirstDay = (Calendar) mTempDate.clone();
12444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
12454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mHasUnfocusedDay = true;
12474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            for (; i < mNumCells; i++) {
12484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
12494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mFocusDay[i] = isFocusedDay;
12504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mHasFocusedDay |= isFocusedDay;
12514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mHasUnfocusedDay &= !isFocusedDay;
12524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                // do not draw dates outside the valid range to avoid user confusion
12534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
12544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mDayNumbers[i] = "";
12554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                } else {
12564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mDayNumbers[i] = String.format(Locale.getDefault(), "%d",
12574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                            mTempDate.get(Calendar.DAY_OF_MONTH));
12584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
12594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempDate.add(Calendar.DAY_OF_MONTH, 1);
12604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
12614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // We do one extra add at the end of the loop, if that pushed us to
12624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // new month undo it
12634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
12644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempDate.add(Calendar.DAY_OF_MONTH, -1);
12654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
12664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
12674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            updateSelectionPositions();
12694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
12704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
12724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Initialize the paint instances.
12734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
12744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private void initilaizePaints() {
12754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDrawPaint.setFakeBoldText(false);
12764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDrawPaint.setAntiAlias(true);
12774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDrawPaint.setStyle(Paint.Style.FILL);
12784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mMonthNumDrawPaint.setFakeBoldText(true);
12804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mMonthNumDrawPaint.setAntiAlias(true);
12814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mMonthNumDrawPaint.setStyle(Paint.Style.FILL);
12824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mMonthNumDrawPaint.setTextAlign(Paint.Align.CENTER);
12834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mMonthNumDrawPaint.setTextSize(mDateTextSize);
12844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
12854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
12874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Returns the month of the first day in this week.
12884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
12894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @return The month the first day of this view is in.
12904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
12914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public int getMonthOfFirstWeekDay() {
12924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return mMonthOfFirstWeekDay;
12934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
12944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
12954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
12964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Returns the month of the last day in this week
12974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
12984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @return The month the last day of this view is in
12994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
13004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public int getMonthOfLastWeekDay() {
13014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return mLastWeekDayMonth;
13024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
13034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
13054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Returns the first day in this view.
13064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
13074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @return The first day in the view.
13084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
13094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public Calendar getFirstDay() {
13104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return mFirstDay;
13114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
13124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
13144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Calculates the day that the given x position is in, accounting for
13154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * week number.
13164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
13174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param x The x position of the touch event.
13184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @return True if a day was found for the given location.
13194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
13204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        public boolean getDayFromLocation(float x, Calendar outCalendar) {
13214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final boolean isLayoutRtl = isLayoutRtl();
13224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int start;
13244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int end;
13254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (isLayoutRtl) {
13274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                start = 0;
13284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                end = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
13294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
13304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                start = mShowWeekNumber ? mWidth / mNumCells : 0;
13314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                end = mWidth;
13324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
13334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (x < start || x > end) {
13354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                outCalendar.clear();
13364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                return false;
13374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
13384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // Selection is (x - start) / (pixels/day) which is (x - start) * day / pixels
13404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int dayPosition = (int) ((x - start) * mDaysPerWeek / (end - start));
13414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (isLayoutRtl) {
13434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                dayPosition = mDaysPerWeek - 1 - dayPosition;
13444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
13454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
13474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
13484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            return true;
13504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
13514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        @Override
13534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        protected void onDraw(Canvas canvas) {
13544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            drawBackground(canvas);
13554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            drawWeekNumbersAndDates(canvas);
13564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            drawWeekSeparators(canvas);
13574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            drawSelectedDateVerticalBars(canvas);
13584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
13594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
13614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * This draws the selection highlight if a day is selected in this week.
13624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
13634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param canvas The canvas to draw on
13644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
13654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private void drawBackground(Canvas canvas) {
13664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (!mHasSelectedDay) {
13674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                return;
13684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
13694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDrawPaint.setColor(mSelectedWeekBackgroundColor);
13704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mTempRect.top = mWeekSeperatorLineWidth;
13724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mTempRect.bottom = mHeight;
13734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final boolean isLayoutRtl = isLayoutRtl();
13754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (isLayoutRtl) {
13774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempRect.left = 0;
13784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempRect.right = mSelectedLeft - 2;
13794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
13804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
13814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempRect.right = mSelectedLeft - 2;
13824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
13834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            canvas.drawRect(mTempRect, mDrawPaint);
13844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (isLayoutRtl) {
13864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempRect.left = mSelectedRight + 3;
13874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempRect.right = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
13884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
13894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempRect.left = mSelectedRight + 3;
13904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mTempRect.right = mWidth;
13914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
13924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            canvas.drawRect(mTempRect, mDrawPaint);
13934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
13944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
13954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
13964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Draws the week and month day numbers for this week.
13974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
13984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param canvas The canvas to draw on
13994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
14004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private void drawWeekNumbersAndDates(Canvas canvas) {
14014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final float textHeight = mDrawPaint.getTextSize();
14024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
14034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final int nDays = mNumCells;
14044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            final int divisor = 2 * nDays;
14054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
14064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDrawPaint.setTextAlign(Paint.Align.CENTER);
14074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDrawPaint.setTextSize(mDateTextSize);
14084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
14094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int i = 0;
14104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
14114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (isLayoutRtl()) {
14124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                for (; i < nDays - 1; i++) {
14134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
14144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                            : mUnfocusedMonthDateColor);
14154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    int x = (2 * i + 1) * mWidth / divisor;
14164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    canvas.drawText(mDayNumbers[nDays - 1 - i], x, y, mMonthNumDrawPaint);
14174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
14184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (mShowWeekNumber) {
14194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mDrawPaint.setColor(mWeekNumberColor);
14204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    int x = mWidth - mWidth / divisor;
14214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
14224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
14234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
14244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (mShowWeekNumber) {
14254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mDrawPaint.setColor(mWeekNumberColor);
14264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    int x = mWidth / divisor;
14274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
14284612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    i++;
14294612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
14304612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                for (; i < nDays; i++) {
14314612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
14324612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                            : mUnfocusedMonthDateColor);
14334612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    int x = (2 * i + 1) * mWidth / divisor;
14344612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
14354612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
14364612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
14374612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
14384612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
14394612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
14404612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Draws a horizontal line for separating the weeks.
14414612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
14424612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param canvas The canvas to draw on.
14434612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
14444612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private void drawWeekSeparators(Canvas canvas) {
14454612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            // If it is the topmost fully visible child do not draw separator line
14464612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
14474612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mListView.getChildAt(0).getTop() < 0) {
14484612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                firstFullyVisiblePosition++;
14494612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
14504612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (firstFullyVisiblePosition == mWeek) {
14514612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                return;
14524612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
14534612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDrawPaint.setColor(mWeekSeparatorLineColor);
14544612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
14554612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            float startX;
14564612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            float stopX;
14574612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (isLayoutRtl()) {
14584612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                startX = 0;
14594612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                stopX = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
14604612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            } else {
14614612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                startX = mShowWeekNumber ? mWidth / mNumCells : 0;
14624612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                stopX = mWidth;
14634612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
14644612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            canvas.drawLine(startX, 0, stopX, 0, mDrawPaint);
14654612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
14664612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
14674612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
14684612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * Draws the selected date bars if this week has a selected day.
14694612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         *
14704612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * @param canvas The canvas to draw on
14714612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
14724612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private void drawSelectedDateVerticalBars(Canvas canvas) {
14734612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (!mHasSelectedDay) {
14744612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                return;
14754612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
14764612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedDateVerticalBar.setBounds(
14774612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
14784612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mWeekSeperatorLineWidth,
14794612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mSelectedLeft + mSelectedDateVerticalBarWidth / 2,
14804612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mHeight);
14814612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedDateVerticalBar.draw(canvas);
14824612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedDateVerticalBar.setBounds(
14834612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mSelectedRight - mSelectedDateVerticalBarWidth / 2,
14844612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mWeekSeperatorLineWidth,
14854612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mSelectedRight + mSelectedDateVerticalBarWidth / 2,
14864612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mHeight);
14874612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mSelectedDateVerticalBar.draw(canvas);
14884612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
14894612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
14904612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        @Override
14914612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
14924612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mWidth = w;
14934612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            updateSelectionPositions();
14944612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
14954612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
14964612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        /**
14974612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         * This calculates the positions for the selected day lines.
14984612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette         */
14994612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        private void updateSelectionPositions() {
15004612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            if (mHasSelectedDay) {
15014612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                final boolean isLayoutRtl = isLayoutRtl();
15024612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                int selectedPosition = mSelectedDay - mFirstDayOfWeek;
15034612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (selectedPosition < 0) {
15044612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    selectedPosition += 7;
15054612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
15064612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (mShowWeekNumber && !isLayoutRtl) {
15074612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    selectedPosition++;
15084612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
15094612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                if (isLayoutRtl) {
15104612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mSelectedLeft = (mDaysPerWeek - 1 - selectedPosition) * mWidth / mNumCells;
15114612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
15124612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                } else {
15134612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    mSelectedLeft = selectedPosition * mWidth / mNumCells;
15144612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                }
15154612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                mSelectedRight = mSelectedLeft + mWidth / mNumCells;
15164612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            }
15174612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
15184612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
15194612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        @Override
15204612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
15214612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
15224612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette                    .getPaddingBottom()) / mShownWeekCount;
15234612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette            setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
15244612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette        }
15254612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette    }
15264612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette
15274612740ddc76b3518dc6d189d5f8b5b7f60e9d64Alan Viverette}
1528