|  | /* | 
|  | * Copyright (C) 2015 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 android.widget; | 
|  |  | 
|  | import android.annotation.IdRes; | 
|  | import android.annotation.LayoutRes; | 
|  | import android.annotation.NonNull; | 
|  | import android.annotation.Nullable; | 
|  | import android.content.Context; | 
|  | import android.content.res.ColorStateList; | 
|  | import android.content.res.TypedArray; | 
|  | import android.graphics.Rect; | 
|  | import android.icu.util.Calendar; | 
|  | import android.util.SparseArray; | 
|  | import android.view.LayoutInflater; | 
|  | import android.view.View; | 
|  | import android.view.ViewGroup; | 
|  | import android.widget.SimpleMonthView.OnDayClickListener; | 
|  |  | 
|  | import com.android.internal.widget.PagerAdapter; | 
|  |  | 
|  | /** | 
|  | * An adapter for a list of {@link android.widget.SimpleMonthView} items. | 
|  | */ | 
|  | class DayPickerPagerAdapter extends PagerAdapter { | 
|  | private static final int MONTHS_IN_YEAR = 12; | 
|  |  | 
|  | private final Calendar mMinDate = Calendar.getInstance(); | 
|  | private final Calendar mMaxDate = Calendar.getInstance(); | 
|  |  | 
|  | private final SparseArray<ViewHolder> mItems = new SparseArray<>(); | 
|  |  | 
|  | private final LayoutInflater mInflater; | 
|  | private final int mLayoutResId; | 
|  | private final int mCalendarViewId; | 
|  |  | 
|  | private Calendar mSelectedDay = null; | 
|  |  | 
|  | private int mMonthTextAppearance; | 
|  | private int mDayOfWeekTextAppearance; | 
|  | private int mDayTextAppearance; | 
|  |  | 
|  | private ColorStateList mCalendarTextColor; | 
|  | private ColorStateList mDaySelectorColor; | 
|  | private ColorStateList mDayHighlightColor; | 
|  |  | 
|  | private OnDaySelectedListener mOnDaySelectedListener; | 
|  |  | 
|  | private int mCount; | 
|  | private int mFirstDayOfWeek; | 
|  |  | 
|  | public DayPickerPagerAdapter(@NonNull Context context, @LayoutRes int layoutResId, | 
|  | @IdRes int calendarViewId) { | 
|  | mInflater = LayoutInflater.from(context); | 
|  | mLayoutResId = layoutResId; | 
|  | mCalendarViewId = calendarViewId; | 
|  |  | 
|  | final TypedArray ta = context.obtainStyledAttributes(new int[] { | 
|  | com.android.internal.R.attr.colorControlHighlight}); | 
|  | mDayHighlightColor = ta.getColorStateList(0); | 
|  | ta.recycle(); | 
|  | } | 
|  |  | 
|  | public void setRange(@NonNull Calendar min, @NonNull Calendar max) { | 
|  | mMinDate.setTimeInMillis(min.getTimeInMillis()); | 
|  | mMaxDate.setTimeInMillis(max.getTimeInMillis()); | 
|  |  | 
|  | final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR); | 
|  | final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH); | 
|  | mCount = diffMonth + MONTHS_IN_YEAR * diffYear + 1; | 
|  |  | 
|  | // Positions are now invalid, clear everything and start over. | 
|  | notifyDataSetChanged(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets the first day of the week. | 
|  | * | 
|  | * @param weekStart which day the week should start on, valid values are | 
|  | *                  {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY} | 
|  | */ | 
|  | public void setFirstDayOfWeek(int weekStart) { | 
|  | mFirstDayOfWeek = weekStart; | 
|  |  | 
|  | // Update displayed views. | 
|  | final int count = mItems.size(); | 
|  | for (int i = 0; i < count; i++) { | 
|  | final SimpleMonthView monthView = mItems.valueAt(i).calendar; | 
|  | monthView.setFirstDayOfWeek(weekStart); | 
|  | } | 
|  | } | 
|  |  | 
|  | public int getFirstDayOfWeek() { | 
|  | return mFirstDayOfWeek; | 
|  | } | 
|  |  | 
|  | public boolean getBoundsForDate(Calendar day, Rect outBounds) { | 
|  | final int position = getPositionForDay(day); | 
|  | final ViewHolder monthView = mItems.get(position, null); | 
|  | if (monthView == null) { | 
|  | return false; | 
|  | } else { | 
|  | final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH); | 
|  | return monthView.calendar.getBoundsForDay(dayOfMonth, outBounds); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets the selected day. | 
|  | * | 
|  | * @param day the selected day | 
|  | */ | 
|  | public void setSelectedDay(@Nullable Calendar day) { | 
|  | final int oldPosition = getPositionForDay(mSelectedDay); | 
|  | final int newPosition = getPositionForDay(day); | 
|  |  | 
|  | // Clear the old position if necessary. | 
|  | if (oldPosition != newPosition && oldPosition >= 0) { | 
|  | final ViewHolder oldMonthView = mItems.get(oldPosition, null); | 
|  | if (oldMonthView != null) { | 
|  | oldMonthView.calendar.setSelectedDay(-1); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Set the new position. | 
|  | if (newPosition >= 0) { | 
|  | final ViewHolder newMonthView = mItems.get(newPosition, null); | 
|  | if (newMonthView != null) { | 
|  | final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH); | 
|  | newMonthView.calendar.setSelectedDay(dayOfMonth); | 
|  | } | 
|  | } | 
|  |  | 
|  | mSelectedDay = day; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets the listener to call when the user selects a day. | 
|  | * | 
|  | * @param listener The listener to call. | 
|  | */ | 
|  | public void setOnDaySelectedListener(OnDaySelectedListener listener) { | 
|  | mOnDaySelectedListener = listener; | 
|  | } | 
|  |  | 
|  | void setCalendarTextColor(ColorStateList calendarTextColor) { | 
|  | mCalendarTextColor = calendarTextColor; | 
|  | notifyDataSetChanged(); | 
|  | } | 
|  |  | 
|  | void setDaySelectorColor(ColorStateList selectorColor) { | 
|  | mDaySelectorColor = selectorColor; | 
|  | notifyDataSetChanged(); | 
|  | } | 
|  |  | 
|  | void setMonthTextAppearance(int resId) { | 
|  | mMonthTextAppearance = resId; | 
|  | notifyDataSetChanged(); | 
|  | } | 
|  |  | 
|  | void setDayOfWeekTextAppearance(int resId) { | 
|  | mDayOfWeekTextAppearance = resId; | 
|  | notifyDataSetChanged(); | 
|  | } | 
|  |  | 
|  | int getDayOfWeekTextAppearance() { | 
|  | return mDayOfWeekTextAppearance; | 
|  | } | 
|  |  | 
|  | void setDayTextAppearance(int resId) { | 
|  | mDayTextAppearance = resId; | 
|  | notifyDataSetChanged(); | 
|  | } | 
|  |  | 
|  | int getDayTextAppearance() { | 
|  | return mDayTextAppearance; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int getCount() { | 
|  | return mCount; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isViewFromObject(View view, Object object) { | 
|  | final ViewHolder holder = (ViewHolder) object; | 
|  | return view == holder.container; | 
|  | } | 
|  |  | 
|  | private int getMonthForPosition(int position) { | 
|  | return (position + mMinDate.get(Calendar.MONTH)) % MONTHS_IN_YEAR; | 
|  | } | 
|  |  | 
|  | private int getYearForPosition(int position) { | 
|  | final int yearOffset = (position + mMinDate.get(Calendar.MONTH)) / MONTHS_IN_YEAR; | 
|  | return yearOffset + mMinDate.get(Calendar.YEAR); | 
|  | } | 
|  |  | 
|  | private int getPositionForDay(@Nullable Calendar day) { | 
|  | if (day == null) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | final int yearOffset = day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR); | 
|  | final int monthOffset = day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH); | 
|  | final int position = yearOffset * MONTHS_IN_YEAR + monthOffset; | 
|  | return position; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object instantiateItem(ViewGroup container, int position) { | 
|  | final View itemView = mInflater.inflate(mLayoutResId, container, false); | 
|  |  | 
|  | final SimpleMonthView v = itemView.findViewById(mCalendarViewId); | 
|  | v.setOnDayClickListener(mOnDayClickListener); | 
|  | v.setMonthTextAppearance(mMonthTextAppearance); | 
|  | v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance); | 
|  | v.setDayTextAppearance(mDayTextAppearance); | 
|  |  | 
|  | if (mDaySelectorColor != null) { | 
|  | v.setDaySelectorColor(mDaySelectorColor); | 
|  | } | 
|  |  | 
|  | if (mDayHighlightColor != null) { | 
|  | v.setDayHighlightColor(mDayHighlightColor); | 
|  | } | 
|  |  | 
|  | if (mCalendarTextColor != null) { | 
|  | v.setMonthTextColor(mCalendarTextColor); | 
|  | v.setDayOfWeekTextColor(mCalendarTextColor); | 
|  | v.setDayTextColor(mCalendarTextColor); | 
|  | } | 
|  |  | 
|  | final int month = getMonthForPosition(position); | 
|  | final int year = getYearForPosition(position); | 
|  |  | 
|  | final int selectedDay; | 
|  | if (mSelectedDay != null && mSelectedDay.get(Calendar.MONTH) == month && mSelectedDay.get( | 
|  | Calendar.YEAR) == year) { | 
|  | selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH); | 
|  | } else { | 
|  | selectedDay = -1; | 
|  | } | 
|  |  | 
|  | final int enabledDayRangeStart; | 
|  | if (mMinDate.get(Calendar.MONTH) == month && mMinDate.get(Calendar.YEAR) == year) { | 
|  | enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH); | 
|  | } else { | 
|  | enabledDayRangeStart = 1; | 
|  | } | 
|  |  | 
|  | final int enabledDayRangeEnd; | 
|  | if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) { | 
|  | enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH); | 
|  | } else { | 
|  | enabledDayRangeEnd = 31; | 
|  | } | 
|  |  | 
|  | v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek, | 
|  | enabledDayRangeStart, enabledDayRangeEnd); | 
|  |  | 
|  | final ViewHolder holder = new ViewHolder(position, itemView, v); | 
|  | mItems.put(position, holder); | 
|  |  | 
|  | container.addView(itemView); | 
|  |  | 
|  | return holder; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void destroyItem(ViewGroup container, int position, Object object) { | 
|  | final ViewHolder holder = (ViewHolder) object; | 
|  | container.removeView(holder.container); | 
|  |  | 
|  | mItems.remove(position); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int getItemPosition(Object object) { | 
|  | final ViewHolder holder = (ViewHolder) object; | 
|  | return holder.position; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public CharSequence getPageTitle(int position) { | 
|  | final SimpleMonthView v = mItems.get(position).calendar; | 
|  | if (v != null) { | 
|  | return v.getMonthYearLabel(); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | SimpleMonthView getView(Object object) { | 
|  | if (object == null) { | 
|  | return null; | 
|  | } | 
|  | final ViewHolder holder = (ViewHolder) object; | 
|  | return holder.calendar; | 
|  | } | 
|  |  | 
|  | private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() { | 
|  | @Override | 
|  | public void onDayClick(SimpleMonthView view, Calendar day) { | 
|  | if (day != null) { | 
|  | setSelectedDay(day); | 
|  |  | 
|  | if (mOnDaySelectedListener != null) { | 
|  | mOnDaySelectedListener.onDaySelected(DayPickerPagerAdapter.this, day); | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | private static class ViewHolder { | 
|  | public final int position; | 
|  | public final View container; | 
|  | public final SimpleMonthView calendar; | 
|  |  | 
|  | public ViewHolder(int position, View container, SimpleMonthView calendar) { | 
|  | this.position = position; | 
|  | this.container = container; | 
|  | this.calendar = calendar; | 
|  | } | 
|  | } | 
|  |  | 
|  | public interface OnDaySelectedListener { | 
|  | public void onDaySelected(DayPickerPagerAdapter view, Calendar day); | 
|  | } | 
|  | } |