/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.codetroopers.betterpickers.calendardatepicker; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; import android.text.format.Time; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView.LayoutParams; import android.widget.BaseAdapter; import com.codetroopers.betterpickers.Utils; import com.codetroopers.betterpickers.calendardatepicker.MonthView.OnDayClickListener; import java.util.Calendar; import java.util.HashMap; /** * An adapter for a list of {@link MonthView} items. */ public abstract class MonthAdapter extends BaseAdapter implements OnDayClickListener { private static final String TAG = "SimpleMonthAdapter"; private final Context mContext; private final CalendarDatePickerController mController; private CalendarDay mSelectedDay; private TypedArray mThemeColors; protected static int WEEK_7_OVERHANG_HEIGHT = 7; protected static final int MONTHS_IN_YEAR = 12; public void setTheme(TypedArray mThemeColors) { this.mThemeColors = mThemeColors; } /** * A convenience class to represent a specific date. */ public static class CalendarDay implements Comparable<CalendarDay>, Parcelable { private Calendar calendar; private long calendarTimeInMillis; private Time time; private long timeInMillis; int year; int month; int day; public CalendarDay() { setTime(System.currentTimeMillis()); } public CalendarDay(long timeInMillis) { setTime(timeInMillis); } public CalendarDay(Calendar calendar) { year = calendar.get(Calendar.YEAR); month = calendar.get(Calendar.MONTH); day = calendar.get(Calendar.DAY_OF_MONTH); } public CalendarDay(int year, int month, int day) { setDay(year, month, day); } public CalendarDay(Parcel in) { this.calendarTimeInMillis = in.readLong(); this.calendar = Calendar.getInstance(); this.calendar.setTimeInMillis(this.calendarTimeInMillis); this.timeInMillis = in.readLong(); this.time = new Time(); this.time.set(this.timeInMillis); this.year = in.readInt(); this.month = in.readInt(); this.day = in.readInt(); } public void set(CalendarDay date) { this.year = date.year; this.month = date.month; this.day = date.day; } public void setDay(int year, int month, int day) { this.calendar = Calendar.getInstance(); this.calendar.set(year, month, day, 0, 0, 0); this.calendar.set(Calendar.MILLISECOND, 0); this.year = calendar.get(Calendar.YEAR); this.month = calendar.get(Calendar.MONTH); this.day = calendar.get(Calendar.DAY_OF_MONTH); } public long getDateInMillis() { if (calendar == null) { calendar = Calendar.getInstance(); calendar.set(year, month, day, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); } return calendar.getTimeInMillis(); } public synchronized void setJulianDay(int julianDay) { if (time == null) { time = new Time(); } time.setJulianDay(julianDay); setTime(time.toMillis(false)); } private void setTime(long timeInMillis) { if (calendar == null) { calendar = Calendar.getInstance(); } calendar.setTimeInMillis(timeInMillis); month = calendar.get(Calendar.MONTH); year = calendar.get(Calendar.YEAR); day = calendar.get(Calendar.DAY_OF_MONTH); } @Override public int compareTo(@NonNull CalendarDay another) { if (year < another.year || (year == another.year && month < another.month) || (year == another.year && month == another.month && day < another.day)) { return -1; } if ((year == another.year && month == another.month && day == another.day)) { return 0; } return 1; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { if (calendar != null) { calendarTimeInMillis = calendar.getTimeInMillis(); } dest.writeLong(calendarTimeInMillis); if (time != null) { timeInMillis = time.toMillis(false); } dest.writeInt(year); dest.writeInt(month); dest.writeInt(day); } public static final Parcelable.Creator<CalendarDay> CREATOR = new Parcelable.Creator<CalendarDay>() { public CalendarDay createFromParcel(Parcel in) { return new CalendarDay(in); } public CalendarDay[] newArray(int size) { return new CalendarDay[size]; } }; } public MonthAdapter(Context context, CalendarDatePickerController controller) { mContext = context; mController = controller; init(); setSelectedDay(mController.getSelectedDay()); } /** * Updates the selected day and related parameters. * * @param day The day to highlight */ public void setSelectedDay(CalendarDay day) { mSelectedDay = day; notifyDataSetChanged(); } public CalendarDay getSelectedDay() { return mSelectedDay; } /** * Set up the gesture detector and selected time */ protected void init() { mSelectedDay = new CalendarDay(System.currentTimeMillis()); if (mSelectedDay.compareTo(mController.getMaxDate()) > 0) { mSelectedDay = mController.getMaxDate(); } if (mSelectedDay.compareTo(mController.getMinDate()) < 0) { mSelectedDay = mController.getMinDate(); } } @Override public int getCount() { return (((mController.getMaxDate().year - mController.getMinDate().year) + 1) * MONTHS_IN_YEAR) - (MONTHS_IN_YEAR - 1 - mController.getMaxDate().month) - mController.getMinDate().month; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return position; } @Override public boolean hasStableIds() { return true; } @SuppressLint("NewApi") @SuppressWarnings("unchecked") @Override public View getView(int position, View convertView, ViewGroup parent) { MonthView monthView; HashMap<String, Integer> drawingParams = null; if (convertView != null) { monthView = (MonthView) convertView; // We store the drawing parameters in the view so it can be recycled drawingParams = (HashMap<String, Integer>) monthView.getTag(); } else { monthView = createMonthView(mContext); monthView.setTheme(mThemeColors); // Set up the new view LayoutParams params = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); monthView.setLayoutParams(params); monthView.setClickable(true); monthView.setOnDayClickListener(this); } if (drawingParams == null) { drawingParams = new HashMap<String, Integer>(); } drawingParams.clear(); final int month = (position + mController.getMinDate().month) % MONTHS_IN_YEAR; final int year = (position + mController.getMinDate().month) / MONTHS_IN_YEAR + mController.getMinDate().year; int selectedDay = -1; if (isSelectedDayInMonth(year, month)) { selectedDay = mSelectedDay.day; } int rangeMin = -1; if (isRangeMinInMonth(year, month)) { rangeMin = mController.getMinDate().day; } int rangeMax = -1; if (isRangeMaxInMonth(year, month)) { rangeMax = mController.getMaxDate().day; } // Invokes requestLayout() to ensure that the recycled view is set with the appropriate // height/number of weeks before being displayed. monthView.reuse(); // Set disabled days if they exist if (mController.getDisabledDays() != null) { monthView.setDisabledDays(mController.getDisabledDays()); } drawingParams.put(MonthView.VIEW_PARAMS_SELECTED_DAY, selectedDay); drawingParams.put(MonthView.VIEW_PARAMS_YEAR, year); drawingParams.put(MonthView.VIEW_PARAMS_MONTH, month); drawingParams.put(MonthView.VIEW_PARAMS_WEEK_START, mController.getFirstDayOfWeek()); drawingParams.put(MonthView.VIEW_PARAMS_RANGE_MIN, rangeMin); drawingParams.put(MonthView.VIEW_PARAMS_RANGE_MAX, rangeMax); monthView.setMonthParams(drawingParams); monthView.invalidate(); return monthView; } public abstract MonthView createMonthView(Context context); private boolean isSelectedDayInMonth(int year, int month) { return mSelectedDay.year == year && mSelectedDay.month == month; } private boolean isRangeMinInMonth(int year, int month) { return mController.getMinDate().year == year && mController.getMinDate().month == month; } private boolean isRangeMaxInMonth(int year, int month) { return mController.getMaxDate().year == year && mController.getMaxDate().month == month; } @Override public void onDayClick(MonthView view, CalendarDay day) { if (day != null && isDayInRange(day) && !isDayDisabled(day)) { onDayTapped(day); } } private boolean isDayInRange(CalendarDay day) { return day.compareTo(mController.getMinDate()) >= 0 && day.compareTo(mController.getMaxDate()) <= 0; } private boolean isDayDisabled(CalendarDay day) { if (mController.getDisabledDays() == null) { return false; } return mController.getDisabledDays() .indexOfKey(Utils.formatDisabledDayForKey(day.year, day.month, day.day)) >= 0; } /** * Maintains the same hour/min/sec but moves the day to the tapped day. * * @param day The day that was tapped */ protected void onDayTapped(CalendarDay day) { mController.tryVibrate(); mController.onDayOfMonthSelected(day.year, day.month, day.day); setSelectedDay(day); } }