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