1e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung/*
2e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Copyright (C) 2013 The Android Open Source Project
3e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung *
4e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Licensed under the Apache License, Version 2.0 (the "License");
5e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * you may not use this file except in compliance with the License.
6e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * You may obtain a copy of the License at
7e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung *
8e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung *      http://www.apache.org/licenses/LICENSE-2.0
9e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung *
10e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * Unless required by applicable law or agreed to in writing, software
11e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * distributed under the License is distributed on an "AS IS" BASIS,
12e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * See the License for the specific language governing permissions and
14e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * limitations under the License.
15e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */
16e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
17e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungpackage com.android.datetimepicker.date;
18e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
19e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.annotation.SuppressLint;
20e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.content.Context;
21e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.text.format.Time;
22e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.View;
23e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.view.ViewGroup;
24e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.widget.AbsListView.LayoutParams;
25e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport android.widget.BaseAdapter;
26e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
27e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport com.android.datetimepicker.date.MonthView.OnDayClickListener;
28e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
29e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.Calendar;
30e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungimport java.util.HashMap;
31e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
32e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung/**
33e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung * An adapter for a list of {@link MonthView} items.
34e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung */
35e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kungpublic abstract class MonthAdapter extends BaseAdapter implements OnDayClickListener {
36e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
37e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    private static final String TAG = "SimpleMonthAdapter";
38e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
39e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    private final Context mContext;
407c597aee285f2fe4a13a24334493c6eadddd7939Sam Blitzstein    protected final DatePickerController mController;
41e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
42e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    private CalendarDay mSelectedDay;
43e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
44e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    protected static int WEEK_7_OVERHANG_HEIGHT = 7;
45e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    protected static final int MONTHS_IN_YEAR = 12;
46e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
47e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    /**
48e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     * A convenience class to represent a specific date.
49e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     */
50e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public static class CalendarDay {
51e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        private Calendar calendar;
52e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        private Time time;
53e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        int year;
54e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        int month;
55e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        int day;
56e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
57e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        public CalendarDay() {
58e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            setTime(System.currentTimeMillis());
59e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
60e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
61e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        public CalendarDay(long timeInMillis) {
62e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            setTime(timeInMillis);
63e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
64e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
65e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        public CalendarDay(Calendar calendar) {
66e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            year = calendar.get(Calendar.YEAR);
67e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            month = calendar.get(Calendar.MONTH);
68e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            day = calendar.get(Calendar.DAY_OF_MONTH);
69e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
70e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
71e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        public CalendarDay(int year, int month, int day) {
72e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            setDay(year, month, day);
73e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
74e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
75e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        public void set(CalendarDay date) {
76e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            year = date.year;
77e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            month = date.month;
78e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            day = date.day;
79e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
80e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
81e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        public void setDay(int year, int month, int day) {
82e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            this.year = year;
83e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            this.month = month;
84e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            this.day = day;
85e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
86e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
87e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        public synchronized void setJulianDay(int julianDay) {
88e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            if (time == null) {
89e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung                time = new Time();
90e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            }
91e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            time.setJulianDay(julianDay);
92e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            setTime(time.toMillis(false));
93e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
94e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
95e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        private void setTime(long timeInMillis) {
96e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            if (calendar == null) {
97e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung                calendar = Calendar.getInstance();
98e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            }
99e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            calendar.setTimeInMillis(timeInMillis);
100e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            month = calendar.get(Calendar.MONTH);
101e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            year = calendar.get(Calendar.YEAR);
102e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            day = calendar.get(Calendar.DAY_OF_MONTH);
103e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
1045cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein
1055cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein        public int getYear() {
1065cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein            return year;
1075cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein        }
1085cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein
1095cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein        public int getMonth() {
1105cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein            return month;
1115cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein        }
1125cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein
1135cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein        public int getDay() {
1145cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein            return day;
1155cfe197091de6e7b5550fc2503e00fe554bef5cdSam Blitzstein        }
116e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
117e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
118e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public MonthAdapter(Context context,
119e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            DatePickerController controller) {
120e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        mContext = context;
121e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        mController = controller;
122e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        init();
123e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        setSelectedDay(mController.getSelectedDay());
124e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
125e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
126e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    /**
127e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     * Updates the selected day and related parameters.
128e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     *
129e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     * @param day The day to highlight
130e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     */
131e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public void setSelectedDay(CalendarDay day) {
132e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        mSelectedDay = day;
133e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        notifyDataSetChanged();
134e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
135e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
136e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public CalendarDay getSelectedDay() {
137e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        return mSelectedDay;
138e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
139e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
140e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    /**
141e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     * Set up the gesture detector and selected time
142e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     */
143e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    protected void init() {
144e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        mSelectedDay = new CalendarDay(System.currentTimeMillis());
145e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
146e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
147e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    @Override
148e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public int getCount() {
149e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        return ((mController.getMaxYear() - mController.getMinYear()) + 1) * MONTHS_IN_YEAR;
150e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
151e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
152e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    @Override
153e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public Object getItem(int position) {
154e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        return null;
155e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
156e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
157e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    @Override
158e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public long getItemId(int position) {
159e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        return position;
160e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
161e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
162e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    @Override
163e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public boolean hasStableIds() {
164e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        return true;
165e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
166e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
167e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    @SuppressLint("NewApi")
168e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    @SuppressWarnings("unchecked")
169e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    @Override
170e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public View getView(int position, View convertView, ViewGroup parent) {
171e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        MonthView v;
172e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        HashMap<String, Integer> drawingParams = null;
173e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        if (convertView != null) {
174e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            v = (MonthView) convertView;
175e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            // We store the drawing parameters in the view so it can be recycled
176e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            drawingParams = (HashMap<String, Integer>) v.getTag();
177e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        } else {
178e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            v = createMonthView(mContext);
179e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            // Set up the new view
180e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            LayoutParams params = new LayoutParams(
181e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
182e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            v.setLayoutParams(params);
183e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            v.setClickable(true);
184e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            v.setOnDayClickListener(this);
185e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
186e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        if (drawingParams == null) {
187e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            drawingParams = new HashMap<String, Integer>();
188e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
189e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        drawingParams.clear();
190e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
191e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        final int month = position % MONTHS_IN_YEAR;
192e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        final int year = position / MONTHS_IN_YEAR + mController.getMinYear();
193e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
194e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        int selectedDay = -1;
195e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        if (isSelectedDayInMonth(year, month)) {
196e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            selectedDay = mSelectedDay.day;
197e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
198e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
199e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
200e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        // height/number of weeks before being displayed.
201e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        v.reuse();
202e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
203e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        drawingParams.put(MonthView.VIEW_PARAMS_SELECTED_DAY, selectedDay);
204e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        drawingParams.put(MonthView.VIEW_PARAMS_YEAR, year);
205e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        drawingParams.put(MonthView.VIEW_PARAMS_MONTH, month);
206e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        drawingParams.put(MonthView.VIEW_PARAMS_WEEK_START, mController.getFirstDayOfWeek());
207e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        v.setMonthParams(drawingParams);
208e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        v.invalidate();
209e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        return v;
210e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
211e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
212e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public abstract MonthView createMonthView(Context context);
213e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
214e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    private boolean isSelectedDayInMonth(int year, int month) {
215e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        return mSelectedDay.year == year && mSelectedDay.month == month;
216e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
217e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
218e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
219e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    @Override
220e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    public void onDayClick(MonthView view, CalendarDay day) {
221e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        if (day != null) {
222e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung            onDayTapped(day);
223e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        }
224e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
225e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung
226e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    /**
227e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     * Maintains the same hour/min/sec but moves the day to the tapped day.
228e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     *
229e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     * @param day The day that was tapped
230e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung     */
231e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    protected void onDayTapped(CalendarDay day) {
232e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        mController.tryVibrate();
233e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        mController.onDayOfMonthSelected(day.year, day.month, day.day);
234e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung        setSelectedDay(day);
235e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung    }
236e668d6b1b77ac4b127f961150e0d0a8a088143d9James Kung}
237