1 /* 2 * Copyright (C) 2015 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 17 package android.widget; 18 19 import android.annotation.IdRes; 20 import android.annotation.LayoutRes; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.content.res.ColorStateList; 25 import android.content.res.TypedArray; 26 import android.graphics.Rect; 27 import android.icu.util.Calendar; 28 import android.util.SparseArray; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.widget.SimpleMonthView.OnDayClickListener; 33 34 import com.android.internal.widget.PagerAdapter; 35 36 /** 37 * An adapter for a list of {@link android.widget.SimpleMonthView} items. 38 */ 39 class DayPickerPagerAdapter extends PagerAdapter { 40 private static final int MONTHS_IN_YEAR = 12; 41 42 private final Calendar mMinDate = Calendar.getInstance(); 43 private final Calendar mMaxDate = Calendar.getInstance(); 44 45 private final SparseArray<ViewHolder> mItems = new SparseArray<>(); 46 47 private final LayoutInflater mInflater; 48 private final int mLayoutResId; 49 private final int mCalendarViewId; 50 51 private Calendar mSelectedDay = null; 52 53 private int mMonthTextAppearance; 54 private int mDayOfWeekTextAppearance; 55 private int mDayTextAppearance; 56 57 private ColorStateList mCalendarTextColor; 58 private ColorStateList mDaySelectorColor; 59 private ColorStateList mDayHighlightColor; 60 61 private OnDaySelectedListener mOnDaySelectedListener; 62 63 private int mCount; 64 private int mFirstDayOfWeek; 65 DayPickerPagerAdapter(@onNull Context context, @LayoutRes int layoutResId, @IdRes int calendarViewId)66 public DayPickerPagerAdapter(@NonNull Context context, @LayoutRes int layoutResId, 67 @IdRes int calendarViewId) { 68 mInflater = LayoutInflater.from(context); 69 mLayoutResId = layoutResId; 70 mCalendarViewId = calendarViewId; 71 72 final TypedArray ta = context.obtainStyledAttributes(new int[] { 73 com.android.internal.R.attr.colorControlHighlight}); 74 mDayHighlightColor = ta.getColorStateList(0); 75 ta.recycle(); 76 } 77 setRange(@onNull Calendar min, @NonNull Calendar max)78 public void setRange(@NonNull Calendar min, @NonNull Calendar max) { 79 mMinDate.setTimeInMillis(min.getTimeInMillis()); 80 mMaxDate.setTimeInMillis(max.getTimeInMillis()); 81 82 final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR); 83 final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH); 84 mCount = diffMonth + MONTHS_IN_YEAR * diffYear + 1; 85 86 // Positions are now invalid, clear everything and start over. 87 notifyDataSetChanged(); 88 } 89 90 /** 91 * Sets the first day of the week. 92 * 93 * @param weekStart which day the week should start on, valid values are 94 * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY} 95 */ setFirstDayOfWeek(int weekStart)96 public void setFirstDayOfWeek(int weekStart) { 97 mFirstDayOfWeek = weekStart; 98 99 // Update displayed views. 100 final int count = mItems.size(); 101 for (int i = 0; i < count; i++) { 102 final SimpleMonthView monthView = mItems.valueAt(i).calendar; 103 monthView.setFirstDayOfWeek(weekStart); 104 } 105 } 106 getFirstDayOfWeek()107 public int getFirstDayOfWeek() { 108 return mFirstDayOfWeek; 109 } 110 getBoundsForDate(Calendar day, Rect outBounds)111 public boolean getBoundsForDate(Calendar day, Rect outBounds) { 112 final int position = getPositionForDay(day); 113 final ViewHolder monthView = mItems.get(position, null); 114 if (monthView == null) { 115 return false; 116 } else { 117 final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH); 118 return monthView.calendar.getBoundsForDay(dayOfMonth, outBounds); 119 } 120 } 121 122 /** 123 * Sets the selected day. 124 * 125 * @param day the selected day 126 */ setSelectedDay(@ullable Calendar day)127 public void setSelectedDay(@Nullable Calendar day) { 128 final int oldPosition = getPositionForDay(mSelectedDay); 129 final int newPosition = getPositionForDay(day); 130 131 // Clear the old position if necessary. 132 if (oldPosition != newPosition && oldPosition >= 0) { 133 final ViewHolder oldMonthView = mItems.get(oldPosition, null); 134 if (oldMonthView != null) { 135 oldMonthView.calendar.setSelectedDay(-1); 136 } 137 } 138 139 // Set the new position. 140 if (newPosition >= 0) { 141 final ViewHolder newMonthView = mItems.get(newPosition, null); 142 if (newMonthView != null) { 143 final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH); 144 newMonthView.calendar.setSelectedDay(dayOfMonth); 145 } 146 } 147 148 mSelectedDay = day; 149 } 150 151 /** 152 * Sets the listener to call when the user selects a day. 153 * 154 * @param listener The listener to call. 155 */ setOnDaySelectedListener(OnDaySelectedListener listener)156 public void setOnDaySelectedListener(OnDaySelectedListener listener) { 157 mOnDaySelectedListener = listener; 158 } 159 setCalendarTextColor(ColorStateList calendarTextColor)160 void setCalendarTextColor(ColorStateList calendarTextColor) { 161 mCalendarTextColor = calendarTextColor; 162 notifyDataSetChanged(); 163 } 164 setDaySelectorColor(ColorStateList selectorColor)165 void setDaySelectorColor(ColorStateList selectorColor) { 166 mDaySelectorColor = selectorColor; 167 notifyDataSetChanged(); 168 } 169 setMonthTextAppearance(int resId)170 void setMonthTextAppearance(int resId) { 171 mMonthTextAppearance = resId; 172 notifyDataSetChanged(); 173 } 174 setDayOfWeekTextAppearance(int resId)175 void setDayOfWeekTextAppearance(int resId) { 176 mDayOfWeekTextAppearance = resId; 177 notifyDataSetChanged(); 178 } 179 getDayOfWeekTextAppearance()180 int getDayOfWeekTextAppearance() { 181 return mDayOfWeekTextAppearance; 182 } 183 setDayTextAppearance(int resId)184 void setDayTextAppearance(int resId) { 185 mDayTextAppearance = resId; 186 notifyDataSetChanged(); 187 } 188 getDayTextAppearance()189 int getDayTextAppearance() { 190 return mDayTextAppearance; 191 } 192 193 @Override getCount()194 public int getCount() { 195 return mCount; 196 } 197 198 @Override isViewFromObject(View view, Object object)199 public boolean isViewFromObject(View view, Object object) { 200 final ViewHolder holder = (ViewHolder) object; 201 return view == holder.container; 202 } 203 getMonthForPosition(int position)204 private int getMonthForPosition(int position) { 205 return (position + mMinDate.get(Calendar.MONTH)) % MONTHS_IN_YEAR; 206 } 207 getYearForPosition(int position)208 private int getYearForPosition(int position) { 209 final int yearOffset = (position + mMinDate.get(Calendar.MONTH)) / MONTHS_IN_YEAR; 210 return yearOffset + mMinDate.get(Calendar.YEAR); 211 } 212 getPositionForDay(@ullable Calendar day)213 private int getPositionForDay(@Nullable Calendar day) { 214 if (day == null) { 215 return -1; 216 } 217 218 final int yearOffset = day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR); 219 final int monthOffset = day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH); 220 final int position = yearOffset * MONTHS_IN_YEAR + monthOffset; 221 return position; 222 } 223 224 @Override instantiateItem(ViewGroup container, int position)225 public Object instantiateItem(ViewGroup container, int position) { 226 final View itemView = mInflater.inflate(mLayoutResId, container, false); 227 228 final SimpleMonthView v = itemView.findViewById(mCalendarViewId); 229 v.setOnDayClickListener(mOnDayClickListener); 230 v.setMonthTextAppearance(mMonthTextAppearance); 231 v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance); 232 v.setDayTextAppearance(mDayTextAppearance); 233 234 if (mDaySelectorColor != null) { 235 v.setDaySelectorColor(mDaySelectorColor); 236 } 237 238 if (mDayHighlightColor != null) { 239 v.setDayHighlightColor(mDayHighlightColor); 240 } 241 242 if (mCalendarTextColor != null) { 243 v.setMonthTextColor(mCalendarTextColor); 244 v.setDayOfWeekTextColor(mCalendarTextColor); 245 v.setDayTextColor(mCalendarTextColor); 246 } 247 248 final int month = getMonthForPosition(position); 249 final int year = getYearForPosition(position); 250 251 final int selectedDay; 252 if (mSelectedDay != null && mSelectedDay.get(Calendar.MONTH) == month && mSelectedDay.get( 253 Calendar.YEAR) == year) { 254 selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH); 255 } else { 256 selectedDay = -1; 257 } 258 259 final int enabledDayRangeStart; 260 if (mMinDate.get(Calendar.MONTH) == month && mMinDate.get(Calendar.YEAR) == year) { 261 enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH); 262 } else { 263 enabledDayRangeStart = 1; 264 } 265 266 final int enabledDayRangeEnd; 267 if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) { 268 enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH); 269 } else { 270 enabledDayRangeEnd = 31; 271 } 272 273 v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek, 274 enabledDayRangeStart, enabledDayRangeEnd); 275 276 final ViewHolder holder = new ViewHolder(position, itemView, v); 277 mItems.put(position, holder); 278 279 container.addView(itemView); 280 281 return holder; 282 } 283 284 @Override destroyItem(ViewGroup container, int position, Object object)285 public void destroyItem(ViewGroup container, int position, Object object) { 286 final ViewHolder holder = (ViewHolder) object; 287 container.removeView(holder.container); 288 289 mItems.remove(position); 290 } 291 292 @Override getItemPosition(Object object)293 public int getItemPosition(Object object) { 294 final ViewHolder holder = (ViewHolder) object; 295 return holder.position; 296 } 297 298 @Override getPageTitle(int position)299 public CharSequence getPageTitle(int position) { 300 final SimpleMonthView v = mItems.get(position).calendar; 301 if (v != null) { 302 return v.getMonthYearLabel(); 303 } 304 return null; 305 } 306 getView(Object object)307 SimpleMonthView getView(Object object) { 308 if (object == null) { 309 return null; 310 } 311 final ViewHolder holder = (ViewHolder) object; 312 return holder.calendar; 313 } 314 315 private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() { 316 @Override 317 public void onDayClick(SimpleMonthView view, Calendar day) { 318 if (day != null) { 319 setSelectedDay(day); 320 321 if (mOnDaySelectedListener != null) { 322 mOnDaySelectedListener.onDaySelected(DayPickerPagerAdapter.this, day); 323 } 324 } 325 } 326 }; 327 328 private static class ViewHolder { 329 public final int position; 330 public final View container; 331 public final SimpleMonthView calendar; 332 ViewHolder(int position, View container, SimpleMonthView calendar)333 public ViewHolder(int position, View container, SimpleMonthView calendar) { 334 this.position = position; 335 this.container = container; 336 this.calendar = calendar; 337 } 338 } 339 340 public interface OnDaySelectedListener { onDaySelected(DayPickerPagerAdapter view, Calendar day)341 public void onDaySelected(DayPickerPagerAdapter view, Calendar day); 342 } 343 } 344