150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov/*
250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * Copyright (C) 2010 The Android Open Source Project
350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov *
450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * Licensed under the Apache License, Version 2.0 (the "License");
550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * you may not use this file except in compliance with the License.
650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * You may obtain a copy of the License at
750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov *
850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov *      http://www.apache.org/licenses/LICENSE-2.0
950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov *
1050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * Unless required by applicable law or agreed to in writing, software
1150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * distributed under the License is distributed on an "AS IS" BASIS,
1250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * See the License for the specific language governing permissions and
1450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov * limitations under the License.
1550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov */
1650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovpackage android.widget;
1850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
19e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.annotation.Widget;
2050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.app.Service;
2150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.content.Context;
22f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganovimport android.content.res.Configuration;
2350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.content.res.TypedArray;
2450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.database.DataSetObserver;
2550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.graphics.Canvas;
2650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.graphics.Paint;
2750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.graphics.Paint.Align;
2850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.graphics.Paint.Style;
2950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.graphics.Rect;
3050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.graphics.drawable.Drawable;
31e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.text.TextUtils;
3250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.text.format.DateUtils;
3350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.util.AttributeSet;
34e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.util.DisplayMetrics;
35e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport android.util.Log;
3650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.util.TypedValue;
3750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.view.GestureDetector;
3850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.view.LayoutInflater;
3950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.view.MotionEvent;
4050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.view.View;
4150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.view.ViewGroup;
428a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
438a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
4450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport android.widget.AbsListView.OnScrollListener;
4550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
461442da620c117910e7628b36efc32ff962be02b2Svetoslav Ganovimport com.android.internal.R;
471442da620c117910e7628b36efc32ff962be02b2Svetoslav Ganov
48e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport java.text.ParseException;
49e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport java.text.SimpleDateFormat;
5050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport java.util.Calendar;
5150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport java.util.Locale;
52e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovimport java.util.TimeZone;
5350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
5450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganovimport libcore.icu.LocaleData;
5550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
5650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov/**
57e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov * This class is a calendar widget for displaying and selecting dates. The range
58e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov * of dates supported by this calendar is configurable. A user can select a date
59e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov * by taping on it and can scroll and fling the calendar to a desired date.
60e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov *
61e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_showWeekNumber
62e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_firstDayOfWeek
63e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_minDate
64e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_maxDate
65e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_shownWeekCount
66e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
67e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
68e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
69e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_weekNumberColor
70e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
71e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
72e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
73e51f94cfe9f97e13dad0e564dfc92b22e70c296dSvetoslav Ganov * @attr ref android.R.styleable#CalendarView_dateTextAppearance
7450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov */
75e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov@Widget
76e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganovpublic class CalendarView extends FrameLayout {
7750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
7850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
79e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Tag for logging.
80e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
81e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final String LOG_TAG = CalendarView.class.getSimpleName();
82e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
83e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
84e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Default value whether to show week number.
8550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
86e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final boolean DEFAULT_SHOW_WEEK_NUMBER = true;
87e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
88e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
89e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The number of milliseconds in a day.e
90e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
91e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final long MILLIS_IN_DAY = 86400000L;
9250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
9350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
9450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The number of day in a week.
9550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
96e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int DAYS_PER_WEEK = 7;
9750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
9850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
9950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The number of milliseconds in a week.
10050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
101e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final long MILLIS_IN_WEEK = DAYS_PER_WEEK * MILLIS_IN_DAY;
10250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
10350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
104e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Affects when the month selection will change while scrolling upe
105e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
106e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int SCROLL_HYST_WEEKS = 2;
10750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
10850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
10950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * How long the GoTo fling animation should last.
11050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
111e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int GOTO_SCROLL_DURATION = 1000;
11250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
11350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
11450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The duration of the adjustment upon a user scroll in milliseconds.
11550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
116e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int ADJUSTMENT_SCROLL_DURATION = 500;
11750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
11850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
11950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * How long to wait after receiving an onScrollStateChanged notification
12050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * before acting on it.
12150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
122e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int SCROLL_CHANGE_DELAY = 40;
12350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
12450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
125e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * String for parsing dates.
12650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
127e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final String DATE_FORMAT = "MM/dd/yyyy";
128e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
129e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
130e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default minimal date.
131e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
132e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final String DEFAULT_MIN_DATE = "01/01/1900";
133e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
134e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
135e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The default maximal date.
136e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
137e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final String DEFAULT_MAX_DATE = "01/01/2100";
138e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
139e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int DEFAULT_SHOWN_WEEK_COUNT = 6;
140e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
141e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int DEFAULT_DATE_TEXT_SIZE = 14;
142e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
143e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH = 6;
144e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
145e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int UNSCALED_WEEK_MIN_VISIBLE_HEIGHT = 12;
146e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
147e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int UNSCALED_LIST_SCROLL_TOP_OFFSET = 2;
148e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
149e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int UNSCALED_BOTTOM_BUFFER = 20;
150e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
151e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int UNSCALED_WEEK_SEPARATOR_LINE_WIDTH = 1;
152e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
153e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private static final int DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID = -1;
154e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
155e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private final int mWeekSeperatorLineWidth;
156e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
157ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private int mDateTextSize;
158e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
159ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private Drawable mSelectedDateVerticalBar;
160e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
161e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private final int mSelectedDateVerticalBarWidth;
162e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
163ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private int mSelectedWeekBackgroundColor;
164e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
165ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private int mFocusedMonthDateColor;
166e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
167ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private int mUnfocusedMonthDateColor;
168e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
169ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private int mWeekSeparatorLineColor;
170e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
171ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private int mWeekNumberColor;
172ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
173ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private int mWeekDayTextAppearanceResId;
174ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
175ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private int mDateTextAppearanceResId;
17650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
17750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
17850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The top offset of the weeks list.
17950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
180e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mListScrollTopOffset = 2;
18150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
18250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
18350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The visible height of a week view.
18450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
185e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mWeekMinVisibleHeight = 12;
18650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
18750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
18850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The visible height of a week view.
18950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
190e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mBottomBuffer = 20;
19150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
19250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
19350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The number of shown weeks.
19450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
195e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mShownWeekCount;
19650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
19750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
19850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Flag whether to show the week number.
19950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
200e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean mShowWeekNumber;
20150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
20250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
203e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The number of day per week to be shown.
20450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
205e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mDaysPerWeek = 7;
20650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
20750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
20850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The friction of the week list while flinging.
20950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
210e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private float mFriction = .05f;
21150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
21250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
21350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Scale for adjusting velocity of the week list while flinging.
21450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
215e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private float mVelocityScale = 0.333f;
21650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
21750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
21850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The adapter for the weeks list.
21950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
220e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private WeeksAdapter mAdapter;
22150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
22250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
22350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The weeks list.
22450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
225e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private ListView mListView;
22650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
22750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
22850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The name of the month to display.
22950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
230e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private TextView mMonthName;
23150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
23250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
23350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The header with week day names.
23450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
235e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private ViewGroup mDayNamesHeader;
23650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
23750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
23850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Cached labels for the week names header.
23950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
240e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private String[] mDayLabels;
24150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
24250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
243e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * The first day of the week.
24450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
245e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mFirstDayOfWeek;
24650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
24750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
248e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Which month should be displayed/highlighted [0-11].
24950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
250f537c9b5f15cb5d5ad8197c61beb471ecdc5f1fbKi-Hwan Lee    private int mCurrentMonthDisplayed = -1;
25150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
25250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
25350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Used for tracking during a scroll.
25450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
255e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private long mPreviousScrollPosition;
25650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
25750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
25850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Used for tracking which direction the view is scrolling.
25950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
260e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean mIsScrollingUp = false;
26150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
26250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
26350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The previous scroll state of the weeks ListView.
26450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
265e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
26650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
26750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
26850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The current scroll state of the weeks ListView.
26950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
270e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
27150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
27250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
27350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Listener for changes in the selected day.
27450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
275e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private OnDateChangeListener mOnDateChangeListener;
27650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
27750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
27850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Command for adjusting the position after a scroll/fling.
27950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
280e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable();
28150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
28250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
283f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     * Temporary instance to avoid multiple instantiations.
284f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     */
285f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    private Calendar mTempDate;
286f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
287f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    /**
288f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     * The first day of the focused month.
289f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     */
290f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    private Calendar mFirstDayOfMonth;
291f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
292f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    /**
29350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The start date of the range supported by this picker.
29450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
295f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    private Calendar mMinDate;
29650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
29750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
29850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The end date of the range supported by this picker.
29950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
300f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    private Calendar mMaxDate;
30150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
30250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
303e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Date format for parsing dates.
30450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
305e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
30650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
30750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
308f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     * The current locale.
309f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     */
310f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    private Locale mCurrentLocale;
311f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
312f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    /**
31350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * The callback used to indicate the user changes the date.
31450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
315e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public interface OnDateChangeListener {
31650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
31750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
31850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Called upon change of the selected day.
31950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
32050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param view The view associated with this listener.
32150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param year The year that was set.
32250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param month The month that was set [0-11].
32350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param dayOfMonth The day of the month that was set.
32450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
325e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        public void onSelectedDayChange(CalendarView view, int year, int month, int dayOfMonth);
32650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
32750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
328e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public CalendarView(Context context) {
32950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        this(context, null);
33050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
33150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
332e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public CalendarView(Context context, AttributeSet attrs) {
33350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        this(context, attrs, 0);
33450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
33550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
336e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public CalendarView(Context context, AttributeSet attrs, int defStyle) {
33750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        super(context, attrs, 0);
33850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
339f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        // initialization based on locale
340f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        setCurrentLocale(Locale.getDefault());
341f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
342f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov        TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.CalendarView,
343f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov                R.attr.calendarViewStyle, 0);
344e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mShowWeekNumber = attributesArray.getBoolean(R.styleable.CalendarView_showWeekNumber,
345e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                DEFAULT_SHOW_WEEK_NUMBER);
346e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mFirstDayOfWeek = attributesArray.getInt(R.styleable.CalendarView_firstDayOfWeek,
347e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                LocaleData.get(Locale.getDefault()).firstDayOfWeek);
348e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        String minDate = attributesArray.getString(R.styleable.CalendarView_minDate);
349e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
350e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            parseDate(DEFAULT_MIN_DATE, mMinDate);
351e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
352e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        String maxDate = attributesArray.getString(R.styleable.CalendarView_maxDate);
353e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
354e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            parseDate(DEFAULT_MAX_DATE, mMaxDate);
355e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
356f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov        if (mMaxDate.before(mMinDate)) {
357f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov            throw new IllegalArgumentException("Max date cannot be before min date.");
358f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov        }
359e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mShownWeekCount = attributesArray.getInt(R.styleable.CalendarView_shownWeekCount,
360e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                DEFAULT_SHOWN_WEEK_COUNT);
361e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mSelectedWeekBackgroundColor = attributesArray.getColor(
362e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                R.styleable.CalendarView_selectedWeekBackgroundColor, 0);
363e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mFocusedMonthDateColor = attributesArray.getColor(
364e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                R.styleable.CalendarView_focusedMonthDateColor, 0);
365e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mUnfocusedMonthDateColor = attributesArray.getColor(
366e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                R.styleable.CalendarView_unfocusedMonthDateColor, 0);
367e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mWeekSeparatorLineColor = attributesArray.getColor(
368e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                R.styleable.CalendarView_weekSeparatorLineColor, 0);
369e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mWeekNumberColor = attributesArray.getColor(R.styleable.CalendarView_weekNumberColor, 0);
370e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mSelectedDateVerticalBar = attributesArray.getDrawable(
371e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                R.styleable.CalendarView_selectedDateVerticalBar);
372e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
373ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        mDateTextAppearanceResId = attributesArray.getResourceId(
374e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                R.styleable.CalendarView_dateTextAppearance, R.style.TextAppearance_Small);
375ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        updateDateTextSize();
376e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
377ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        mWeekDayTextAppearanceResId = attributesArray.getResourceId(
378e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                R.styleable.CalendarView_weekDayTextAppearance,
379e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                DEFAULT_WEEK_DAY_TEXT_APPEARANCE_RES_ID);
380e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        attributesArray.recycle();
381e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
382e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
383e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mWeekMinVisibleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
384e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                UNSCALED_WEEK_MIN_VISIBLE_HEIGHT, displayMetrics);
385e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mListScrollTopOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
386e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                UNSCALED_LIST_SCROLL_TOP_OFFSET, displayMetrics);
387e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mBottomBuffer = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
388e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                UNSCALED_BOTTOM_BUFFER, displayMetrics);
389e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mSelectedDateVerticalBarWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
390e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                UNSCALED_SELECTED_DATE_VERTICAL_BAR_WIDTH, displayMetrics);
391e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mWeekSeperatorLineWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
392e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                UNSCALED_WEEK_SEPARATOR_LINE_WIDTH, displayMetrics);
393e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
39422c1c2cd103ef8beadaf0e78c12b22b60ce9cfc3Alan Viverette        LayoutInflater layoutInflater = (LayoutInflater) context
39550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                .getSystemService(Service.LAYOUT_INFLATER_SERVICE);
396e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
39750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        addView(content);
39850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
39950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mListView = (ListView) findViewById(R.id.list);
40050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mDayNamesHeader = (ViewGroup) content.findViewById(com.android.internal.R.id.day_names);
40150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mMonthName = (TextView) content.findViewById(com.android.internal.R.id.month_name);
40250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
403ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        setUpHeader();
40450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        setUpListView();
40550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        setUpAdapter();
40650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
407f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov        // go to today or whichever is close to today min or max date
408e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mTempDate.setTimeInMillis(System.currentTimeMillis());
409f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov        if (mTempDate.before(mMinDate)) {
410f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov            goTo(mMinDate, false, true, true);
411f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov        } else if (mMaxDate.before(mTempDate)) {
412f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov            goTo(mMaxDate, false, true, true);
413f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov        } else {
414f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov            goTo(mTempDate, false, true, true);
415f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov        }
416f1189e9a462dcc85918607159c433d9cd23de0bfSvetoslav Ganov
41750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        invalidate();
41850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
41950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
420ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
421ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the number of weeks to be shown.
422ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
423ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param count The shown week count.
424414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
425414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_shownWeekCount
426ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
427ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setShownWeekCount(int count) {
428ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mShownWeekCount != count) {
429ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mShownWeekCount = count;
430ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            invalidate();
431ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
432ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
433ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
434ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
435ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the number of weeks to be shown.
436ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
437ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return The shown week count.
438414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
439414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_shownWeekCount
440ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
441ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public int getShownWeekCount() {
442ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mShownWeekCount;
443ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
444ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
445ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
446ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the background color for the selected week.
447ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
448ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param color The week background color.
449414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
450414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
451ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
452ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setSelectedWeekBackgroundColor(int color) {
453ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mSelectedWeekBackgroundColor != color) {
454ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mSelectedWeekBackgroundColor = color;
455ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            final int childCount = mListView.getChildCount();
456ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            for (int i = 0; i < childCount; i++) {
457ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                WeekView weekView = (WeekView) mListView.getChildAt(i);
458ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                if (weekView.mHasSelectedDay) {
459ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                    weekView.invalidate();
460ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                }
461ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            }
462ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
463ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
464ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
465ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
466ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the background color for the selected week.
467ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
468ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return The week background color.
469414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
470414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
471ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
472ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public int getSelectedWeekBackgroundColor() {
473ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mSelectedWeekBackgroundColor;
474ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
475ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
476ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
477ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the color for the dates of the focused month.
478ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
479ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param color The focused month date color.
480414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
481414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
482ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
483ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setFocusedMonthDateColor(int color) {
484ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mFocusedMonthDateColor != color) {
485ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mFocusedMonthDateColor = color;
486ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            final int childCount = mListView.getChildCount();
487ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            for (int i = 0; i < childCount; i++) {
488ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                WeekView weekView = (WeekView) mListView.getChildAt(i);
489ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                if (weekView.mHasFocusedDay) {
490ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                    weekView.invalidate();
491ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                }
492ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            }
493ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
494ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
495ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
496ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
497ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the color for the dates in the focused month.
498ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
499ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return The focused month date color.
500414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
501414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
502ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
503ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public int getFocusedMonthDateColor() {
504ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mFocusedMonthDateColor;
505ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
506ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
507ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
508ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the color for the dates of a not focused month.
509ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
510ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param color A not focused month date color.
511414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
512414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
513ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
514ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setUnfocusedMonthDateColor(int color) {
515ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mUnfocusedMonthDateColor != color) {
516ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mUnfocusedMonthDateColor = color;
517ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            final int childCount = mListView.getChildCount();
518ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            for (int i = 0; i < childCount; i++) {
519ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                WeekView weekView = (WeekView) mListView.getChildAt(i);
520ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                if (weekView.mHasUnfocusedDay) {
521ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                    weekView.invalidate();
522ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                }
523ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            }
524ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
525ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
526ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
527ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
528ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the color for the dates in a not focused month.
529ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
530ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return A not focused month date color.
531414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
532414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
533ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
534ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public int getUnfocusedMonthDateColor() {
535ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mFocusedMonthDateColor;
536ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
537ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
538ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
539ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the color for the week numbers.
540ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
541ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param color The week number color.
542414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
543414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_weekNumberColor
544ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
545ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setWeekNumberColor(int color) {
546ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mWeekNumberColor != color) {
547ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mWeekNumberColor = color;
548ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            if (mShowWeekNumber) {
549ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                invalidateAllWeekViews();
550ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            }
551ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
552ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
553ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
554ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
555ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the color for the week numbers.
556ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
557ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return The week number color.
558414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
559414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_weekNumberColor
560ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
561ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public int getWeekNumberColor() {
562ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mWeekNumberColor;
563ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
564ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
565ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
566ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the color for the separator line between weeks.
567ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
568ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param color The week separator color.
569414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
570414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
571ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
572ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setWeekSeparatorLineColor(int color) {
573ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mWeekSeparatorLineColor != color) {
574ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mWeekSeparatorLineColor = color;
575ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            invalidateAllWeekViews();
576ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
577ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
578ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
579ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
580ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the color for the separator line between weeks.
581ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
582ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return The week separator color.
583414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
584414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
585ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
586ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public int getWeekSeparatorLineColor() {
587ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mWeekSeparatorLineColor;
588ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
589ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
590ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
591ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the drawable for the vertical bar shown at the beginning and at
592ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * the end of the selected date.
593ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
594ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param resourceId The vertical bar drawable resource id.
595414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
596414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
597ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
598ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setSelectedDateVerticalBar(int resourceId) {
599ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        Drawable drawable = getResources().getDrawable(resourceId);
600ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        setSelectedDateVerticalBar(drawable);
601ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
602ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
603ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
604ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the drawable for the vertical bar shown at the beginning and at
605ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * the end of the selected date.
606ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
607ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param drawable The vertical bar drawable.
608414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
609414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
610ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
611ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setSelectedDateVerticalBar(Drawable drawable) {
612ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mSelectedDateVerticalBar != drawable) {
613ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mSelectedDateVerticalBar = drawable;
614ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            final int childCount = mListView.getChildCount();
615ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            for (int i = 0; i < childCount; i++) {
616ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                WeekView weekView = (WeekView) mListView.getChildAt(i);
617ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                if (weekView.mHasSelectedDay) {
618ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                    weekView.invalidate();
619ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                }
620ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            }
621ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
622ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
623ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
624ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
625ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the drawable for the vertical bar shown at the beginning and at
626ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * the end of the selected date.
627ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
628ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return The vertical bar drawable.
629ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
630ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public Drawable getSelectedDateVerticalBar() {
631ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mSelectedDateVerticalBar;
632ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
633ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
634ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
635ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the text appearance for the week day abbreviation of the calendar header.
636ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
637ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param resourceId The text appearance resource id.
638414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
639414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
640ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
641ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setWeekDayTextAppearance(int resourceId) {
642ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mWeekDayTextAppearanceResId != resourceId) {
643ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mWeekDayTextAppearanceResId = resourceId;
644ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            setUpHeader();
645ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
646ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
647ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
648ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
649ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the text appearance for the week day abbreviation of the calendar header.
650ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
651ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return The text appearance resource id.
652414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
653414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
654ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
655ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public int getWeekDayTextAppearance() {
656ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mWeekDayTextAppearanceResId;
657ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
658ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
659ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
660ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Sets the text appearance for the calendar dates.
661ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
662ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @param resourceId The text appearance resource id.
663414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
664414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_dateTextAppearance
665ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
666ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public void setDateTextAppearance(int resourceId) {
667ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        if (mDateTextAppearanceResId != resourceId) {
668ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mDateTextAppearanceResId = resourceId;
669ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            updateDateTextSize();
670ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            invalidateAllWeekViews();
671ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
672ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
673ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
674ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
675ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Gets the text appearance for the calendar dates.
676ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     *
677ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * @return The text appearance resource id.
678414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
679414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_dateTextAppearance
680ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
681ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    public int getDateTextAppearance() {
682ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        return mDateTextAppearanceResId;
683ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
684ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
68551c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov    @Override
68651c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov    public void setEnabled(boolean enabled) {
68751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        mListView.setEnabled(enabled);
68851c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov    }
68951c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov
69051c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov    @Override
69151c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov    public boolean isEnabled() {
69251c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        return mListView.isEnabled();
69351c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov    }
69451c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov
695f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    @Override
696f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    protected void onConfigurationChanged(Configuration newConfig) {
697f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        super.onConfigurationChanged(newConfig);
698f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        setCurrentLocale(newConfig.locale);
699f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    }
700f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
7018a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
7028a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
7038a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
7048a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        event.setClassName(CalendarView.class.getName());
7058a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
7068a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
7078a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
7088a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
7098a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityNodeInfo(info);
7108a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        info.setClassName(CalendarView.class.getName());
7118a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
7128a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
71350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
714e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the minimal date supported by this {@link CalendarView} in milliseconds
715e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
716e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * zone.
717e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
718e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Note: The default minimal date is 01/01/1900.
719e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
720e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
721e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The minimal supported date.
722414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
723414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_minDate
724e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
725e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public long getMinDate() {
726e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMinDate.getTimeInMillis();
727e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
728e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
729e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
730e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the minimal date supported by this {@link CalendarView} in milliseconds
731e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
732e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * zone.
73350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
734e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param minDate The minimal supported date.
735414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
736414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_minDate
737e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
738e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMinDate(long minDate) {
739e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mTempDate.setTimeInMillis(minDate);
740e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (isSameDate(mTempDate, mMinDate)) {
741e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
74250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
743e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMinDate.setTimeInMillis(minDate);
744156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov        // make sure the current date is not earlier than
745156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov        // the new min date since the latter is used for
746156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov        // calculating the indices in the adapter thus
747156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov        // avoiding out of bounds error
748156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov        Calendar date = mAdapter.mSelectedDate;
749156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov        if (date.before(mMinDate)) {
750156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            mAdapter.setSelectedDay(mMinDate);
751156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov        }
752e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        // reinitialize the adapter since its range depends on min date
753e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mAdapter.init();
754e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (date.before(mMinDate)) {
755156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            setDate(mTempDate.getTimeInMillis());
756e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else {
757e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // we go to the current date to force the ListView to query its
758e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // adapter for the shown views since we have changed the adapter
759e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // range and the base from which the later calculates item indices
760e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // note that calling setDate will not work since the date is the same
761e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            goTo(date, false, true, false);
76250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
763e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
764e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
765e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
766e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the maximal date supported by this {@link CalendarView} in milliseconds
767e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
768e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * zone.
769e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
770e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Note: The default maximal date is 01/01/2100.
771e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * <p>
772e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
773e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The maximal supported date.
774414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
775414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_maxDate
776e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
777e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public long getMaxDate() {
778e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mMaxDate.getTimeInMillis();
779e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
78028104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov
781e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
782e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the maximal date supported by this {@link CalendarView} in milliseconds
783e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
784e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * zone.
785e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
786e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param maxDate The maximal supported date.
787414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
788414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_maxDate
789e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
790e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setMaxDate(long maxDate) {
791e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mTempDate.setTimeInMillis(maxDate);
792e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (isSameDate(mTempDate, mMaxDate)) {
79328104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov            return;
79428104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov        }
795e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mMaxDate.setTimeInMillis(maxDate);
796e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        // reinitialize the adapter since its range depends on max date
797e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mAdapter.init();
798e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        Calendar date = mAdapter.mSelectedDate;
799e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (date.after(mMaxDate)) {
800e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            setDate(mMaxDate.getTimeInMillis());
801e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } else {
802e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // we go to the current date to force the ListView to query its
803e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // adapter for the shown views since we have changed the adapter
804e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // range and the base from which the later calculates item indices
805e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            // note that calling setDate will not work since the date is the same
806e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            goTo(date, false, true, false);
807e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
808e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
80928104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov
810e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
811e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets whether to show the week number.
812e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
813e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param showWeekNumber True to show the week number.
814414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
815414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_showWeekNumber
816e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
817e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setShowWeekNumber(boolean showWeekNumber) {
818e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mShowWeekNumber == showWeekNumber) {
819e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
820e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
821e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mShowWeekNumber = showWeekNumber;
822e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mAdapter.notifyDataSetChanged();
823ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        setUpHeader();
824e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
82528104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov
826e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
827e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets whether to show the week number.
828e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
829e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return True if showing the week number.
830414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
831414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_showWeekNumber
832e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
833e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public boolean getShowWeekNumber() {
834e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mShowWeekNumber;
835e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
836e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
837e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
838e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the first day of week.
839e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
840e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The first day of the week conforming to the {@link CalendarView}
841e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *         APIs.
842e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#MONDAY
843e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#TUESDAY
844e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#WEDNESDAY
845e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#THURSDAY
846e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#FRIDAY
847e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#SATURDAY
848e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#SUNDAY
849414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
850414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_firstDayOfWeek
851e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
852e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public int getFirstDayOfWeek() {
853e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mFirstDayOfWeek;
854e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
855e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
856e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
857e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the first day of week.
858e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
859e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param firstDayOfWeek The first day of the week conforming to the
860e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *            {@link CalendarView} APIs.
861e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#MONDAY
862e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#TUESDAY
863e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#WEDNESDAY
864e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#THURSDAY
865e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#FRIDAY
866e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#SATURDAY
867e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see Calendar#SUNDAY
868414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     *
869414efc3b5f50e2784f4f7c2be3f2c096aef0c6b4Svetoslav Ganov     * @attr ref android.R.styleable#CalendarView_firstDayOfWeek
870e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
871e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setFirstDayOfWeek(int firstDayOfWeek) {
872e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (mFirstDayOfWeek == firstDayOfWeek) {
873e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
87450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
875e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mFirstDayOfWeek = firstDayOfWeek;
876e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mAdapter.init();
877ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        setUpHeader();
87850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
87950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
88050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
881e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the listener to be notified upon selected date change.
88250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
883e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param listener The listener to be notified.
88450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
885e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setOnDateChangeListener(OnDateChangeListener listener) {
886e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mOnDateChangeListener = listener;
88750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
88850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
88950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
890e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Gets the selected date in milliseconds since January 1, 1970 00:00:00 in
891e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link TimeZone#getDefault()} time zone.
89250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
893e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return The selected date.
89450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
895e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public long getDate() {
896e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return mAdapter.mSelectedDate.getTimeInMillis();
89750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
89850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
89950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
900e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the selected date in milliseconds since January 1, 1970 00:00:00 in
901e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link TimeZone#getDefault()} time zone.
902e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
903e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param date The selected date.
904e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
905e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @throws IllegalArgumentException of the provided date is before the
906e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *        minimal or after the maximal date.
90750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
908e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @see #setDate(long, boolean, boolean)
90951c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov     * @see #setMinDate(long)
91051c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov     * @see #setMaxDate(long)
91150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
912e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setDate(long date) {
913e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        setDate(date, false, false);
91450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
91550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
91650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
917e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets the selected date in milliseconds since January 1, 1970 00:00:00 in
918e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * {@link TimeZone#getDefault()} time zone.
91950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
920e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param date The date.
921e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param animate Whether to animate the scroll to the current date.
922e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @param center Whether to center the current date even if it is already visible.
923e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
924e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @throws IllegalArgumentException of the provided date is before the
925e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *        minimal or after the maximal date.
926e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
92751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov     * @see #setMinDate(long)
92851c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov     * @see #setMaxDate(long)
929e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
930e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    public void setDate(long date, boolean animate, boolean center) {
931e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mTempDate.setTimeInMillis(date);
932e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (isSameDate(mTempDate, mAdapter.mSelectedDate)) {
933e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return;
934e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
935e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        goTo(mTempDate, animate, true, center);
936e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
937e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
938ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private void updateDateTextSize() {
93922c1c2cd103ef8beadaf0e78c12b22b60ce9cfc3Alan Viverette        TypedArray dateTextAppearance = mContext.obtainStyledAttributes(
940ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                mDateTextAppearanceResId, R.styleable.TextAppearance);
941ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        mDateTextSize = dateTextAppearance.getDimensionPixelSize(
942ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                R.styleable.TextAppearance_textSize, DEFAULT_DATE_TEXT_SIZE);
943ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        dateTextAppearance.recycle();
944ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
945ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
946ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    /**
947ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     * Invalidates all week views.
948ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov     */
949ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private void invalidateAllWeekViews() {
950ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        final int childCount = mListView.getChildCount();
951ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        for (int i = 0; i < childCount; i++) {
952ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            View view = mListView.getChildAt(i);
953ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            view.invalidate();
954ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        }
955ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    }
956ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
957e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
958f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     * Sets the current locale.
959f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     *
960f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     * @param locale The current locale.
961f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     */
962f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    private void setCurrentLocale(Locale locale) {
963f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        if (locale.equals(mCurrentLocale)) {
964f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov            return;
965f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        }
966f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
967f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        mCurrentLocale = locale;
968f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
969f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        mTempDate = getCalendarForLocale(mTempDate, locale);
970f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        mFirstDayOfMonth = getCalendarForLocale(mFirstDayOfMonth, locale);
971f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        mMinDate = getCalendarForLocale(mMinDate, locale);
972f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        mMaxDate = getCalendarForLocale(mMaxDate, locale);
973f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    }
974f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
975f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    /**
976f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     * Gets a calendar for locale bootstrapped with the value of a given calendar.
977f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     *
978f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     * @param oldCalendar The old calendar.
979f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     * @param locale The locale.
980f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov     */
981f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
982f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        if (oldCalendar == null) {
983f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov            return Calendar.getInstance(locale);
984f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        } else {
985f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov            final long currentTimeMillis = oldCalendar.getTimeInMillis();
986f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov            Calendar newCalendar = Calendar.getInstance(locale);
987f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov            newCalendar.setTimeInMillis(currentTimeMillis);
988f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov            return newCalendar;
989f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov        }
990f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    }
991f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov
992f5926962cc665d4a2e6464f9ba9e3e9788496a6fSvetoslav Ganov    /**
993e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return True if the <code>firstDate</code> is the same as the <code>
994e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * secondDate</code>.
99550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
996e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean isSameDate(Calendar firstDate, Calendar secondDate) {
997e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        return (firstDate.get(Calendar.DAY_OF_YEAR) == secondDate.get(Calendar.DAY_OF_YEAR)
998e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                && firstDate.get(Calendar.YEAR) == secondDate.get(Calendar.YEAR));
999e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1000e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1001e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
1002e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Creates a new adapter if necessary and sets up its parameters.
1003e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1004e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private void setUpAdapter() {
100550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (mAdapter == null) {
100622c1c2cd103ef8beadaf0e78c12b22b60ce9cfc3Alan Viverette            mAdapter = new WeeksAdapter();
100750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mAdapter.registerDataSetObserver(new DataSetObserver() {
100850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                @Override
100950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                public void onChanged() {
1010e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    if (mOnDateChangeListener != null) {
101150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        Calendar selectedDay = mAdapter.getSelectedDay();
1012e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                        mOnDateChangeListener.onSelectedDayChange(CalendarView.this,
101350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                                selectedDay.get(Calendar.YEAR),
101450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                                selectedDay.get(Calendar.MONTH),
101550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                                selectedDay.get(Calendar.DAY_OF_MONTH));
101650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    }
101750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                }
101850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            });
101950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mListView.setAdapter(mAdapter);
102050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
102150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
102250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // refresh the view with the new parameters
102350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mAdapter.notifyDataSetChanged();
102450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
102550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
102650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1027e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets up the strings to be used by the header.
102850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1029ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov    private void setUpHeader() {
1030b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio        final String[] tinyWeekdayNames = LocaleData.get(Locale.getDefault()).tinyWeekdayNames;
103150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mDayLabels = new String[mDaysPerWeek];
1032b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio        for (int i = 0; i < mDaysPerWeek; i++) {
1033b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio            final int j = i + mFirstDayOfWeek;
1034b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio            final int calendarDay = (j > Calendar.SATURDAY) ? j - Calendar.SATURDAY : j;
1035b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio            mDayLabels[i] = tinyWeekdayNames[calendarDay];
103650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
1037b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio        // Deal with week number
103850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        TextView label = (TextView) mDayNamesHeader.getChildAt(0);
103950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (mShowWeekNumber) {
104050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            label.setVisibility(View.VISIBLE);
104150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        } else {
104250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            label.setVisibility(View.GONE);
104350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
1044b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio        // Deal with day labels
1045b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio        final int count = mDayNamesHeader.getChildCount();
1046b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio        for (int i = 0; i < count - 1; i++) {
1047b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio            label = (TextView) mDayNamesHeader.getChildAt(i + 1);
1048ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            if (mWeekDayTextAppearanceResId > -1) {
1049ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                label.setTextAppearance(mContext, mWeekDayTextAppearanceResId);
1050e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            }
1051b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio            if (i < mDaysPerWeek) {
1052b08e5788a79127df2e2e452f5fb1adab417cb503Fabrice Di Meglio                label.setText(mDayLabels[i]);
105350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                label.setVisibility(View.VISIBLE);
105450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            } else {
105550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                label.setVisibility(View.GONE);
105650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
105750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
105850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mDayNamesHeader.invalidate();
105950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
106050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
106150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1062e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Sets all the required fields for the list view.
106350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1064e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private void setUpListView() {
1065e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        // Configure the listview
1066e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mListView.setDivider(null);
1067e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mListView.setItemsCanFocus(true);
1068e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mListView.setVerticalScrollBarEnabled(false);
1069e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mListView.setOnScrollListener(new OnScrollListener() {
1070e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            public void onScrollStateChanged(AbsListView view, int scrollState) {
1071e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                CalendarView.this.onScrollStateChanged(view, scrollState);
1072e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            }
1073e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1074e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            public void onScroll(
1075e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    AbsListView view, int firstVisibleItem, int visibleItemCount,
1076e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    int totalItemCount) {
1077e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                CalendarView.this.onScroll(view, firstVisibleItem, visibleItemCount,
1078e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                        totalItemCount);
1079e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            }
1080e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        });
1081e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        // Make the scrolling behavior nicer
1082e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mListView.setFriction(mFriction);
1083e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        mListView.setVelocityScale(mVelocityScale);
108450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
108550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
108650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
108750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * This moves to the specified time in the view. If the time is not already
108850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * in range it will move the list so that the first of the month containing
108950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * the time is at the top of the view. If the new time is already in view
109050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * the list will not be scrolled unless forceScroll is true. This time may
109150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * optionally be highlighted as selected as well.
1092a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     *
109350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * @param date The time to move to.
109450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * @param animate Whether to scroll to the given time or just redraw at the
109550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *            new location.
109650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * @param setSelected Whether to set the given time as selected.
109750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * @param forceScroll Whether to recenter even if the time is already
109850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *            visible.
1099a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     *
1100a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     * @throws IllegalArgumentException of the provided date is before the
1101a911d4a2b1be7e9ea909a579167826e4a5bac1a3Svetoslav Ganov     *        range start of after the range end.
110250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1103e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private void goTo(Calendar date, boolean animate, boolean setSelected, boolean forceScroll) {
1104e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (date.before(mMinDate) || date.after(mMaxDate)) {
1105e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("Time not between " + mMinDate.getTime()
1106e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    + " and " + mMaxDate.getTime());
110750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
110850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Find the first and last entirely visible weeks
110950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
111050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        View firstChild = mListView.getChildAt(0);
111150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (firstChild != null && firstChild.getTop() < 0) {
111250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            firstFullyVisiblePosition++;
111350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
111450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        int lastFullyVisiblePosition = firstFullyVisiblePosition + mShownWeekCount - 1;
111550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (firstChild != null && firstChild.getTop() > mBottomBuffer) {
111650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            lastFullyVisiblePosition--;
111750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
111850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (setSelected) {
111950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mAdapter.setSelectedDay(date);
112050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
112150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Get the week we're going to
1122e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        int position = getWeeksSinceMinDate(date);
112350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
112450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Check if the selected day is now outside of our visible range
112550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // and if so scroll to the month that contains it
112650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (position < firstFullyVisiblePosition || position > lastFullyVisiblePosition
112750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                || forceScroll) {
112850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mFirstDayOfMonth.setTimeInMillis(date.getTimeInMillis());
112950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mFirstDayOfMonth.set(Calendar.DAY_OF_MONTH, 1);
113050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
113150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            setMonthDisplayed(mFirstDayOfMonth);
1132156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov
1133156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            // the earliest time we can scroll to is the min date
1134156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            if (mFirstDayOfMonth.before(mMinDate)) {
1135156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov                position = 0;
1136156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            } else {
1137156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov                position = getWeeksSinceMinDate(mFirstDayOfMonth);
1138156f20919b3d5f298f8851215adbf65f8b4dc61bSvetoslav Ganov            }
113950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
114050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
114150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            if (animate) {
1142e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                mListView.smoothScrollToPositionFromTop(position, mListScrollTopOffset,
114350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        GOTO_SCROLL_DURATION);
114450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            } else {
1145e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                mListView.setSelectionFromTop(position, mListScrollTopOffset);
114650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                // Perform any after scroll operations that are needed
114750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE);
114850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
114950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        } else if (setSelected) {
115050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            // Otherwise just set the selection
115150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            setMonthDisplayed(date);
115250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
115350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
115450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
115550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1156e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * Parses the given <code>date</code> and in case of success sets
1157e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * the result to the <code>outDate</code>.
1158e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *
1159e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return True if the date was parsed.
1160e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     */
1161e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private boolean parseDate(String date, Calendar outDate) {
1162e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        try {
1163e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            outDate.setTime(mDateFormat.parse(date));
1164e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return true;
1165e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        } catch (ParseException e) {
1166e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
1167e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return false;
1168e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        }
1169e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    }
1170e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov
1171e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    /**
117250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Called when a <code>view</code> transitions to a new <code>scrollState
117350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * </code>.
117450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1175e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private void onScrollStateChanged(AbsListView view, int scrollState) {
117650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
117750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
117850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
117950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
118050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Updates the title and selected month if the <code>view</code> has moved to a new
118150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * month.
118250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1183e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
118450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            int totalItemCount) {
118550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        WeekView child = (WeekView) view.getChildAt(0);
118650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (child == null) {
118750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return;
118850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
118950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
119050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Figure out where we are
119150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
119250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
119350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // If we have moved since our last call update the direction
119450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (currScroll < mPreviousScrollPosition) {
119550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mIsScrollingUp = true;
119650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        } else if (currScroll > mPreviousScrollPosition) {
119750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mIsScrollingUp = false;
119850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        } else {
119950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return;
120050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
120150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
120250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Use some hysteresis for checking which month to highlight. This
120350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // causes the month to transition when two full weeks of a month are
120450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // visible when scrolling up, and when the first day in a month reaches
120550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // the top of the screen when scrolling down.
120628104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov        int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
120750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (mIsScrollingUp) {
120850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
120950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        } else if (offset != 0) {
121050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            child = (WeekView) view.getChildAt(offset);
121150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
121250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
121350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Find out which month we're moving into
121450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        int month;
121550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (mIsScrollingUp) {
121650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            month = child.getMonthOfFirstWeekDay();
121750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        } else {
121850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            month = child.getMonthOfLastWeekDay();
121950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
122050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
122150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // And how it relates to our current highlighted month
122250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        int monthDiff;
122350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if (mCurrentMonthDisplayed == 11 && month == 0) {
122450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            monthDiff = 1;
122550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        } else if (mCurrentMonthDisplayed == 0 && month == 11) {
122650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            monthDiff = -1;
122750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        } else {
122850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            monthDiff = month - mCurrentMonthDisplayed;
122950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
123050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
123150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Only switch months if we're scrolling away from the currently
123250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // selected month
123350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        if ((!mIsScrollingUp && monthDiff > 0) || (mIsScrollingUp && monthDiff < 0)) {
123450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            Calendar firstDay = child.getFirstDay();
123550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            if (mIsScrollingUp) {
123650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                firstDay.add(Calendar.DAY_OF_MONTH, -DAYS_PER_WEEK);
123750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            } else {
123850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                firstDay.add(Calendar.DAY_OF_MONTH, DAYS_PER_WEEK);
123950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
124050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            setMonthDisplayed(firstDay);
124150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
124250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mPreviousScrollPosition = currScroll;
124350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        mPreviousScrollState = mCurrentScrollState;
124450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
124550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
124650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
124750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Sets the month displayed at the top of this view based on time. Override
124850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * to add custom events when the title is changed.
124950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     *
125050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * @param calendar A day in the new focus month.
125150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1252e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private void setMonthDisplayed(Calendar calendar) {
1253a085179558e38d114e72b136a59912821d65a49aSvetoslav        mCurrentMonthDisplayed = calendar.get(Calendar.MONTH);
1254a085179558e38d114e72b136a59912821d65a49aSvetoslav        mAdapter.setFocusMonth(mCurrentMonthDisplayed);
1255a085179558e38d114e72b136a59912821d65a49aSvetoslav        final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
1256a085179558e38d114e72b136a59912821d65a49aSvetoslav                | DateUtils.FORMAT_SHOW_YEAR;
1257a085179558e38d114e72b136a59912821d65a49aSvetoslav        final long millis = calendar.getTimeInMillis();
1258a085179558e38d114e72b136a59912821d65a49aSvetoslav        String newMonthName = DateUtils.formatDateRange(mContext, millis, millis, flags);
1259a085179558e38d114e72b136a59912821d65a49aSvetoslav        mMonthName.setText(newMonthName);
1260a085179558e38d114e72b136a59912821d65a49aSvetoslav        mMonthName.invalidate();
126150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
126250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
126350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
1264e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     * @return Returns the number of weeks between the current <code>date</code>
1265e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov     *         and the <code>mMinDate</code>.
126650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1267e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private int getWeeksSinceMinDate(Calendar date) {
1268e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        if (date.before(mMinDate)) {
1269e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            throw new IllegalArgumentException("fromDate: " + mMinDate.getTime()
1270e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    + " does not precede toDate: " + date.getTime());
127150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
127258f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov        long endTimeMillis = date.getTimeInMillis()
127358f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                + date.getTimeZone().getOffset(date.getTimeInMillis());
127458f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov        long startTimeMillis = mMinDate.getTimeInMillis()
127558f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                + mMinDate.getTimeZone().getOffset(mMinDate.getTimeInMillis());
127658f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov        long dayOffsetMillis = (mMinDate.get(Calendar.DAY_OF_WEEK) - mFirstDayOfWeek)
127758f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov                * MILLIS_IN_DAY;
127858f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov        return (int) ((endTimeMillis - startTimeMillis + dayOffsetMillis) / MILLIS_IN_WEEK);
127950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
128050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
128150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
128250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * Command responsible for acting upon scroll state changes.
128350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1284e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private class ScrollStateRunnable implements Runnable {
128550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        private AbsListView mView;
128650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
128750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        private int mNewState;
128850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
128950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
129050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Sets up the runnable with a short delay in case the scroll state
129150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * immediately changes again.
129250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
129350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param view The list view that changed state
129450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param scrollState The new state it changed to
129550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
129650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public void doScrollStateChange(AbsListView view, int scrollState) {
129750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mView = view;
129850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mNewState = scrollState;
129950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            removeCallbacks(this);
130050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            postDelayed(this, SCROLL_CHANGE_DELAY);
130150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
130250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
130350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public void run() {
130450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mCurrentScrollState = mNewState;
130550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            // Fix the position after a scroll or a fling ends
130650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
130750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
130850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                View child = mView.getChildAt(0);
130950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                if (child == null) {
131050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    // The view is no longer visible, just return
131150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    return;
131250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                }
1313e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                int dist = child.getBottom() - mListScrollTopOffset;
1314e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                if (dist > mListScrollTopOffset) {
131550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    if (mIsScrollingUp) {
131650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        mView.smoothScrollBy(dist - child.getHeight(), ADJUSTMENT_SCROLL_DURATION);
131750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    } else {
131850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                        mView.smoothScrollBy(dist, ADJUSTMENT_SCROLL_DURATION);
131950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    }
132050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                }
132150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
1322e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mPreviousScrollState = mNewState;
132350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
132450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
132550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
132650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
132750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * <p>
132850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * This is a specialized adapter for creating a list of weeks with
132950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * selectable days. It can be configured to display the week number, start
133050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * the week on a given day, show a reduced number of days, or display an
133150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * arbitrary number of weeks at a time.
133250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * </p>
133350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1334e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private class WeeksAdapter extends BaseAdapter implements OnTouchListener {
133522c1c2cd103ef8beadaf0e78c12b22b60ce9cfc3Alan Viverette        private final Calendar mSelectedDate = Calendar.getInstance();
133622c1c2cd103ef8beadaf0e78c12b22b60ce9cfc3Alan Viverette        private final GestureDetector mGestureDetector;
133750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1338e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mSelectedWeek;
133950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1340e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mFocusedMonth;
134150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1342e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mTotalWeekCount;
134350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
134422c1c2cd103ef8beadaf0e78c12b22b60ce9cfc3Alan Viverette        public WeeksAdapter() {
1345e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
134650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            init();
134750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
134850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
134950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
135050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Set up the gesture detector and selected time
135150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
1352e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private void init() {
1353e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
1354e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mTotalWeekCount = getWeeksSinceMinDate(mMaxDate);
1355e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            if (mMinDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek
1356e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                || mMaxDate.get(Calendar.DAY_OF_WEEK) != mFirstDayOfWeek) {
1357e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                mTotalWeekCount++;
135828104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov            }
135922c1c2cd103ef8beadaf0e78c12b22b60ce9cfc3Alan Viverette            notifyDataSetChanged();
136050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
136150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
136250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
136350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Updates the selected day and related parameters.
1364e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *
136550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param selectedDay The time to highlight
136650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
136750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public void setSelectedDay(Calendar selectedDay) {
1368e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            if (selectedDay.get(Calendar.DAY_OF_YEAR) == mSelectedDate.get(Calendar.DAY_OF_YEAR)
1369e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    && selectedDay.get(Calendar.YEAR) == mSelectedDate.get(Calendar.YEAR)) {
137050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                return;
137150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
1372e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mSelectedDate.setTimeInMillis(selectedDay.getTimeInMillis());
1373e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mSelectedWeek = getWeeksSinceMinDate(mSelectedDate);
1374e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mFocusedMonth = mSelectedDate.get(Calendar.MONTH);
137550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            notifyDataSetChanged();
137650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
137750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
137850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
137950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @return The selected day of month.
138050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
138150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public Calendar getSelectedDay() {
1382e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            return mSelectedDate;
138350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
138450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
138550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        @Override
138650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public int getCount() {
138750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return mTotalWeekCount;
138850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
138950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
139050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        @Override
139150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public Object getItem(int position) {
139250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return null;
139350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
139450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
139550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        @Override
139650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public long getItemId(int position) {
139750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return position;
139850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
139950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
140050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        @Override
140150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public View getView(int position, View convertView, ViewGroup parent) {
140251c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            WeekView weekView = null;
140350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            if (convertView != null) {
140451c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                weekView = (WeekView) convertView;
140550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            } else {
140651c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                weekView = new WeekView(mContext);
140750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                android.widget.AbsListView.LayoutParams params =
140851c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                    new android.widget.AbsListView.LayoutParams(LayoutParams.WRAP_CONTENT,
140951c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                            LayoutParams.WRAP_CONTENT);
141051c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                weekView.setLayoutParams(params);
141151c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                weekView.setClickable(true);
141251c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                weekView.setOnTouchListener(this);
141350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
141450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
141551c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            int selectedWeekDay = (mSelectedWeek == position) ? mSelectedDate.get(
141651c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                    Calendar.DAY_OF_WEEK) : -1;
141751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            weekView.init(position, selectedWeekDay, mFocusedMonth);
141850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
141951c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            return weekView;
142050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
142150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
142250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
142350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Changes which month is in focus and updates the view.
142450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
142550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param month The month to show as in focus [0-11]
142650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
142750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public void setFocusMonth(int month) {
1428e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            if (mFocusedMonth == month) {
142950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                return;
143050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
1431e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mFocusedMonth = month;
143250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            notifyDataSetChanged();
143350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
143450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
143550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        @Override
143650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public boolean onTouch(View v, MotionEvent event) {
143751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            if (mListView.isEnabled() && mGestureDetector.onTouchEvent(event)) {
143850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                WeekView weekView = (WeekView) v;
14391a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov                // if we cannot find a day for the given location we are done
14401a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov                if (!weekView.getDayFromLocation(event.getX(), mTempDate)) {
14411a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov                    return true;
14421a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov                }
144328104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov                // it is possible that the touched day is outside the valid range
144428104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov                // we draw whole weeks but range end can fall not on the week end
1445e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
144628104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov                    return true;
144750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                }
1448e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                onDateTapped(mTempDate);
144950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                return true;
145050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
145150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return false;
145250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
145350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
145450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
145550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Maintains the same hour/min/sec but moves the day to the tapped day.
1456e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         *
145750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param day The day that was tapped
145850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
1459e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private void onDateTapped(Calendar day) {
146050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            setSelectedDay(day);
146128104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov            setMonthDisplayed(day);
146250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
146350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
146450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
146550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * This is here so we can identify single tap events and set the
146650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * selected day correctly
146750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
1468e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
146950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            @Override
147050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            public boolean onSingleTapUp(MotionEvent e) {
147150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                return true;
147250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
147350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
147450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
147550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
147650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    /**
147750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * <p>
147850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * This is a dynamic view for drawing a single week. It can be configured to
147950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * display the week number, start the week on a given day, or show a reduced
148050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * number of days. It is intended for use as a single view within a
148150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * ListView. See {@link WeeksAdapter} for usage.
148250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     * </p>
148350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov     */
1484e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov    private class WeekView extends View {
148550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1486e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private final Rect mTempRect = new Rect();
148750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1488e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private final Paint mDrawPaint = new Paint();
148999b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov
1490e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private final Paint mMonthNumDrawPaint = new Paint();
149199b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov
149250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Cache the number strings so we don't have to recompute them each time
1493e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private String[] mDayNumbers;
149450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
149550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Quick lookup for checking which days are in the focus month
1496e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private boolean[] mFocusDay;
149750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1498ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        // Whether this view has a focused day.
1499ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        private boolean mHasFocusedDay;
1500ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
1501ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        // Whether this view has only focused days.
1502ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        private boolean mHasUnfocusedDay;
1503ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov
150450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // The first day displayed by this item
1505e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private Calendar mFirstDay;
150650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
150750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // The month of the first day in this week
1508e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mMonthOfFirstWeekDay = -1;
150950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
151050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // The month of the last day in this week
1511e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mLastWeekDayMonth = -1;
151250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
151350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // The position of this week, equivalent to weeks since the week of Jan
151450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // 1st, 1900
1515e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mWeek = -1;
151650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
151750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Quick reference to the width of this view, matches parent
1518e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mWidth;
151950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
152050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // The height this view should draw at in pixels, set by height param
1521e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mHeight;
152250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
152350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // If this view contains the selected day
1524e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private boolean mHasSelectedDay = false;
152550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
152650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // Which day is selected [0-6] or -1 if no day is selected
152751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        private int mSelectedDay = -1;
152850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
152950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // The number of days + a spot for week number if it is displayed
1530e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mNumCells;
153150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
153250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // The left edge of the selected day
1533e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mSelectedLeft = -1;
153450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
153550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        // The right edge of the selected day
1536e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private int mSelectedRight = -1;
153750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
153850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public WeekView(Context context) {
153950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            super(context);
154050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
154150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            // Sets up any standard paints that will be used
1542ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            initilaizePaints();
154350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
154450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
154550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
154651c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov         * Initializes this week view.
154750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
154851c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov         * @param weekNumber The number of the week this view represents. The
154951c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov         *            week number is a zero based index of the weeks since
155051c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov         *            {@link CalendarView#getMinDate()}.
155151c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov         * @param selectedWeekDay The selected day of the week from 0 to 6, -1 if no
155251c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov         *            selected day.
155351c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov         * @param focusedMonth The month that is currently in focus i.e.
155451c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov         *            highlighted.
155550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
155651c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov        public void init(int weekNumber, int selectedWeekDay, int focusedMonth) {
155751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            mSelectedDay = selectedWeekDay;
155851c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            mHasSelectedDay = mSelectedDay != -1;
1559e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mNumCells = mShowWeekNumber ? mDaysPerWeek + 1 : mDaysPerWeek;
156051c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov            mWeek = weekNumber;
1561e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mTempDate.setTimeInMillis(mMinDate.getTimeInMillis());
156258f51255eb20f9c4d16c11554f372ff92d45fcc7Svetoslav Ganov
1563e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mTempDate.add(Calendar.WEEK_OF_YEAR, mWeek);
1564e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mTempDate.setFirstDayOfWeek(mFirstDayOfWeek);
156550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
156650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            // Allocate space for caching the day numbers and focus values
156750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mDayNumbers = new String[mNumCells];
156850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mFocusDay = new boolean[mNumCells];
156950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
157050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            // If we're showing the week number calculate it based on Monday
157150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            int i = 0;
157299b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            if (mShowWeekNumber) {
1573d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                mDayNumbers[0] = String.format(Locale.getDefault(), "%d",
1574d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                        mTempDate.get(Calendar.WEEK_OF_YEAR));
157550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                i++;
157650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
157750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
157850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            // Now adjust our starting day based on the start day of the week
1579e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            int diff = mFirstDayOfWeek - mTempDate.get(Calendar.DAY_OF_WEEK);
1580e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mTempDate.add(Calendar.DAY_OF_MONTH, diff);
158150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1582e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mFirstDay = (Calendar) mTempDate.clone();
1583e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mMonthOfFirstWeekDay = mTempDate.get(Calendar.MONTH);
158450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1585ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mHasUnfocusedDay = true;
158650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            for (; i < mNumCells; i++) {
1587ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                final boolean isFocusedDay = (mTempDate.get(Calendar.MONTH) == focusedMonth);
1588ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                mFocusDay[i] = isFocusedDay;
1589ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                mHasFocusedDay |= isFocusedDay;
1590ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                mHasUnfocusedDay &= !isFocusedDay;
159128104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov                // do not draw dates outside the valid range to avoid user confusion
1592e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                if (mTempDate.before(mMinDate) || mTempDate.after(mMaxDate)) {
159328104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov                    mDayNumbers[i] = "";
159428104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov                } else {
1595d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                    mDayNumbers[i] = String.format(Locale.getDefault(), "%d",
1596d88e3054d08b1f821bb2e27b2647aa16a60d8742Fabrice Di Meglio                            mTempDate.get(Calendar.DAY_OF_MONTH));
159728104e1de5595a22a6987181b13ddeb192739afdSvetoslav Ganov                }
1598e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                mTempDate.add(Calendar.DAY_OF_MONTH, 1);
159950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
160050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            // We do one extra add at the end of the loop, if that pushed us to
160150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            // new month undo it
1602e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            if (mTempDate.get(Calendar.DAY_OF_MONTH) == 1) {
1603e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                mTempDate.add(Calendar.DAY_OF_MONTH, -1);
160450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
1605e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mLastWeekDayMonth = mTempDate.get(Calendar.MONTH);
160650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
160750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            updateSelectionPositions();
160850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
160950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
161050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
16110c6f3795b3c9ee5210e8933deb10d3a5d2cabec9Svetoslav Ganov         * Initialize the paint instances.
161250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
1613ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        private void initilaizePaints() {
161450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mDrawPaint.setFakeBoldText(false);
161550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mDrawPaint.setAntiAlias(true);
161650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mDrawPaint.setStyle(Style.FILL);
161750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
161850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mMonthNumDrawPaint.setFakeBoldText(true);
161950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mMonthNumDrawPaint.setAntiAlias(true);
162050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mMonthNumDrawPaint.setStyle(Style.FILL);
162150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mMonthNumDrawPaint.setTextAlign(Align.CENTER);
16220c6f3795b3c9ee5210e8933deb10d3a5d2cabec9Svetoslav Ganov            mMonthNumDrawPaint.setTextSize(mDateTextSize);
162350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
162450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
162550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
162650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Returns the month of the first day in this week.
162750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
162850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @return The month the first day of this view is in.
162950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
163050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public int getMonthOfFirstWeekDay() {
163150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return mMonthOfFirstWeekDay;
163250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
163350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
163450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
163550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Returns the month of the last day in this week
163650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
163750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @return The month the last day of this view is in
163850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
163950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public int getMonthOfLastWeekDay() {
164050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return mLastWeekDayMonth;
164150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
164250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
164350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
164450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Returns the first day in this view.
164550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
164650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @return The first day in the view.
164750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
164850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        public Calendar getFirstDay() {
164950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            return mFirstDay;
165050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
165150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
165250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
165350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * Calculates the day that the given x position is in, accounting for
16541a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov         * week number.
165550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
16561a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov         * @param x The x position of the touch event.
16571a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov         * @return True if a day was found for the given location.
165850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
16591a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov        public boolean getDayFromLocation(float x, Calendar outCalendar) {
1660550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            final boolean isLayoutRtl = isLayoutRtl();
1661550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1662550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            int start;
1663550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            int end;
1664550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1665550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            if (isLayoutRtl) {
1666550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                start = 0;
1667550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                end = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
1668550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            } else {
1669550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                start = mShowWeekNumber ? mWidth / mNumCells : 0;
1670550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                end = mWidth;
1671550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            }
1672550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1673550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            if (x < start || x > end) {
1674e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                outCalendar.clear();
16751a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov                return false;
167650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
1677550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1678550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            // Selection is (x - start) / (pixels/day) which is (x - start) * day / pixels
1679550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            int dayPosition = (int) ((x - start) * mDaysPerWeek / (end - start));
1680550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1681550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            if (isLayoutRtl) {
1682550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                dayPosition = mDaysPerWeek - 1 - dayPosition;
1683550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            }
1684550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
168550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            outCalendar.setTimeInMillis(mFirstDay.getTimeInMillis());
168650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            outCalendar.add(Calendar.DAY_OF_MONTH, dayPosition);
1687550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
16881a730dcc4fdf78f97b3640f20323be5f2d5c4516Svetoslav Ganov            return true;
168950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
169050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
169150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        @Override
169250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        protected void onDraw(Canvas canvas) {
169350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            drawBackground(canvas);
1694ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            drawWeekNumbersAndDates(canvas);
1695e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            drawWeekSeparators(canvas);
1696e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            drawSelectedDateVerticalBars(canvas);
169750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
169850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
169950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
170050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * This draws the selection highlight if a day is selected in this week.
170150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
170250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param canvas The canvas to draw on
170350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
1704e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private void drawBackground(Canvas canvas) {
170550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            if (!mHasSelectedDay) {
170650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                return;
170750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
1708e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mDrawPaint.setColor(mSelectedWeekBackgroundColor);
170950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1710e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mTempRect.top = mWeekSeperatorLineWidth;
171150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mTempRect.bottom = mHeight;
1712550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1713550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            final boolean isLayoutRtl = isLayoutRtl();
1714550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1715550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            if (isLayoutRtl) {
1716550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mTempRect.left = 0;
1717550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mTempRect.right = mSelectedLeft - 2;
1718550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            } else {
1719550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mTempRect.left = mShowWeekNumber ? mWidth / mNumCells : 0;
1720550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mTempRect.right = mSelectedLeft - 2;
1721550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            }
172250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            canvas.drawRect(mTempRect, mDrawPaint);
172350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
1724550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            if (isLayoutRtl) {
1725550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mTempRect.left = mSelectedRight + 3;
1726550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mTempRect.right = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
1727550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            } else {
1728550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mTempRect.left = mSelectedRight + 3;
1729550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mTempRect.right = mWidth;
1730550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            }
173150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            canvas.drawRect(mTempRect, mDrawPaint);
173250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
173350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
173450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
1735e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Draws the week and month day numbers for this week.
173650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
173750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * @param canvas The canvas to draw on
173850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
1739ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov        private void drawWeekNumbersAndDates(Canvas canvas) {
1740550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            final float textHeight = mDrawPaint.getTextSize();
1741550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            final int y = (int) ((mHeight + textHeight) / 2) - mWeekSeperatorLineWidth;
1742550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            final int nDays = mNumCells;
1743550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            final int divisor = 2 * nDays;
174450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
174550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mDrawPaint.setTextAlign(Align.CENTER);
1746ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mDrawPaint.setTextSize(mDateTextSize);
1747550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
174850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            int i = 0;
1749550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1750550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            if (isLayoutRtl()) {
1751550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                for (; i < nDays - 1; i++) {
1752550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
1753550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                            : mUnfocusedMonthDateColor);
1754550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    int x = (2 * i + 1) * mWidth / divisor;
1755550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    canvas.drawText(mDayNumbers[nDays - 1 - i], x, y, mMonthNumDrawPaint);
1756550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                }
1757550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                if (mShowWeekNumber) {
1758550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    mDrawPaint.setColor(mWeekNumberColor);
1759550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    int x = mWidth - mWidth / divisor;
1760550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
1761550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                }
1762550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            } else {
1763550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                if (mShowWeekNumber) {
1764550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    mDrawPaint.setColor(mWeekNumberColor);
1765550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    int x = mWidth / divisor;
1766550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    canvas.drawText(mDayNumbers[0], x, y, mDrawPaint);
1767550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    i++;
1768550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                }
1769550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                for (; i < nDays; i++) {
1770550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    mMonthNumDrawPaint.setColor(mFocusDay[i] ? mFocusedMonthDateColor
1771550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                            : mUnfocusedMonthDateColor);
1772550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    int x = (2 * i + 1) * mWidth / divisor;
1773550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    canvas.drawText(mDayNumbers[i], x, y, mMonthNumDrawPaint);
1774550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                }
177550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
177650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
177750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
177850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
1779e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Draws a horizontal line for separating the weeks.
178050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         *
178199b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov         * @param canvas The canvas to draw on.
178250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
1783e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private void drawWeekSeparators(Canvas canvas) {
178499b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            // If it is the topmost fully visible child do not draw separator line
178599b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            int firstFullyVisiblePosition = mListView.getFirstVisiblePosition();
178699b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            if (mListView.getChildAt(0).getTop() < 0) {
178799b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov                firstFullyVisiblePosition++;
178899b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            }
178999b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            if (firstFullyVisiblePosition == mWeek) {
179099b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov                return;
179199b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            }
1792e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mDrawPaint.setColor(mWeekSeparatorLineColor);
1793e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mDrawPaint.setStrokeWidth(mWeekSeperatorLineWidth);
1794550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            float startX;
1795550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            float stopX;
1796550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            if (isLayoutRtl()) {
1797550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                startX = 0;
1798550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                stopX = mShowWeekNumber ? mWidth - mWidth / mNumCells : mWidth;
1799550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            } else {
1800550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                startX = mShowWeekNumber ? mWidth / mNumCells : 0;
1801550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                stopX = mWidth;
1802550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            }
1803550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio            canvas.drawLine(startX, 0, stopX, 0, mDrawPaint);
180499b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov        }
180550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
180699b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov        /**
1807e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov         * Draws the selected date bars if this week has a selected day.
180899b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov         *
180999b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov         * @param canvas The canvas to draw on
181099b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov         */
1811e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private void drawSelectedDateVerticalBars(Canvas canvas) {
181299b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov            if (!mHasSelectedDay) {
181399b539f6326a5f0df123b34766becec023bf7a84Svetoslav Ganov                return;
181450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
1815e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mSelectedDateVerticalBar.setBounds(mSelectedLeft - mSelectedDateVerticalBarWidth / 2,
1816e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    mWeekSeperatorLineWidth,
1817e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    mSelectedLeft + mSelectedDateVerticalBarWidth / 2, mHeight);
1818e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mSelectedDateVerticalBar.draw(canvas);
1819e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mSelectedDateVerticalBar.setBounds(mSelectedRight - mSelectedDateVerticalBarWidth / 2,
1820e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    mWeekSeperatorLineWidth,
1821e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov                    mSelectedRight + mSelectedDateVerticalBarWidth / 2, mHeight);
1822e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov            mSelectedDateVerticalBar.draw(canvas);
182350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
182450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
182550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        @Override
182650f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
182750f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            mWidth = w;
182850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            updateSelectionPositions();
182950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
183050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
183150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        /**
183250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         * This calculates the positions for the selected day lines.
183350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov         */
1834e9730bf3d2dcbea1879f24c18aaf9810ac57084cSvetoslav Ganov        private void updateSelectionPositions() {
183550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            if (mHasSelectedDay) {
1836550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                final boolean isLayoutRtl = isLayoutRtl();
183751c52edad7d40697d7fb2a091f850506fa897643Svetoslav Ganov                int selectedPosition = mSelectedDay - mFirstDayOfWeek;
183850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                if (selectedPosition < 0) {
183950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    selectedPosition += 7;
184050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                }
1841550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                if (mShowWeekNumber && !isLayoutRtl) {
184250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                    selectedPosition++;
184350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov                }
1844550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                if (isLayoutRtl) {
1845550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    mSelectedLeft = (mDaysPerWeek - 1 - selectedPosition) * mWidth / mNumCells;
1846550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio
1847550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                } else {
1848550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                    mSelectedLeft = selectedPosition * mWidth / mNumCells;
1849550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                }
1850550ea7f5465d95b65b7d02aed67d903c9beeba47Fabrice Di Meglio                mSelectedRight = mSelectedLeft + mWidth / mNumCells;
185150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            }
185250f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
185350f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov
185450f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        @Override
185550f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1856ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov            mHeight = (mListView.getHeight() - mListView.getPaddingTop() - mListView
1857ff37505cb13f96ddec09594517ad6f4fa2fd1defSvetoslav Ganov                    .getPaddingBottom()) / mShownWeekCount;
185850f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov            setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight);
185950f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov        }
186050f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov    }
186150f34d14f6dd3411fdbdb6a7b8b285c2b8fdbf5cSvetoslav Ganov}
1862