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