List<List<MonthCellDescriptor>> getMonthCells(MonthDescriptor month, Calendar startCal) { Calendar cal = Calendar.getInstance(locale); cal.setTime(startCal.getTime()); List<List<MonthCellDescriptor>> cells = new ArrayList<List<MonthCellDescriptor>>(); cal.set(DAY_OF_MONTH, 1); int firstDayOfWeek = cal.get(DAY_OF_WEEK); int offset = cal.getFirstDayOfWeek() - firstDayOfWeek; if (offset > 0) { offset -= 7; } cal.add(Calendar.DATE, offset); Calendar minSelectedCal = minDate(selectedCals); Calendar maxSelectedCal = maxDate(selectedCals); while ((cal.get(MONTH) < month.getMonth() + 1 || cal.get(YEAR) < month.getYear()) // && cal.get(YEAR) <= month.getYear()) { Logr.d("Building week row starting at %s", cal.getTime()); List<MonthCellDescriptor> weekCells = new ArrayList<MonthCellDescriptor>(); cells.add(weekCells); for (int c = 0; c < 7; c++) { Date date = cal.getTime(); boolean isCurrentMonth = cal.get(MONTH) == month.getMonth(); boolean isSelected = isCurrentMonth && containsDate(selectedCals, cal); boolean isSelectable = isCurrentMonth && betweenDates(cal, minCal, maxCal) && isDateSelectable(date); boolean isToday = sameDate(cal, today); boolean isHighlighted = containsDate(highlightedCals, cal); int value = cal.get(DAY_OF_MONTH); MonthCellDescriptor.RangeState rangeState = MonthCellDescriptor.RangeState.NONE; if (selectedCals.size() > 1) { if (sameDate(minSelectedCal, cal)) { rangeState = MonthCellDescriptor.RangeState.FIRST; } else if (sameDate(maxDate(selectedCals), cal)) { rangeState = MonthCellDescriptor.RangeState.LAST; } else if (betweenDates(cal, minSelectedCal, maxSelectedCal)) { rangeState = MonthCellDescriptor.RangeState.MIDDLE; } } weekCells.add( new MonthCellDescriptor( date, isCurrentMonth, isSelectable, isSelected, isToday, isHighlighted, value, rangeState)); cal.add(DATE, 1); } } return cells; }
/** Return cell and month-index (for scrolling) for a given Date. */ private MonthCellWithMonthIndex getMonthCellWithIndexByDate(Date date) { int index = 0; Calendar searchCal = Calendar.getInstance(locale); searchCal.setTime(date); Calendar actCal = Calendar.getInstance(locale); for (List<List<MonthCellDescriptor>> monthCells : cells) { for (List<MonthCellDescriptor> weekCells : monthCells) { for (MonthCellDescriptor actCell : weekCells) { actCal.setTime(actCell.getDate()); if (sameDate(actCal, searchCal) && actCell.isSelectable()) { return new MonthCellWithMonthIndex(actCell, index); } } } index++; } return null; }
public void highlightDates(Collection<Date> dates) { for (Date date : dates) { validateDate(date); MonthCellWithMonthIndex monthCellWithMonthIndex = getMonthCellWithIndexByDate(date); if (monthCellWithMonthIndex != null) { Calendar newlyHighlightedCal = Calendar.getInstance(); newlyHighlightedCal.setTime(date); MonthCellDescriptor cell = monthCellWithMonthIndex.cell; highlightedCells.add(cell); highlightedCals.add(newlyHighlightedCal); cell.setHighlighted(true); } } adapter.notifyDataSetChanged(); setAdapter(adapter); }
private boolean doSelectDate(Date date, MonthCellDescriptor cell) { Calendar newlySelectedCal = Calendar.getInstance(locale); newlySelectedCal.setTime(date); // Sanitize input: clear out the hours/minutes/seconds/millis. setMidnight(newlySelectedCal); // Clear any remaining range state. for (MonthCellDescriptor selectedCell : selectedCells) { selectedCell.setRangeState(RangeState.NONE); } switch (selectionMode) { case RANGE: if (selectedCals.size() > 1) { // We've already got a range selected: clear the old one. clearOldSelections(); } else if (selectedCals.size() == 1 && newlySelectedCal.before(selectedCals.get(0))) { // We're moving the start of the range back in time: clear the // old start date. clearOldSelections(); } break; case MULTIPLE: date = applyMultiSelect(date, newlySelectedCal); break; case SINGLE: clearOldSelections(); break; default: throw new IllegalStateException("Unknown selectionMode " + selectionMode); } if (date != null) { // Select a new cell. if (selectedCells.size() == 0 || !selectedCells.get(0).equals(cell)) { selectedCells.add(cell); cell.setSelected(true); } selectedCals.add(newlySelectedCal); if (selectionMode == SelectionMode.RANGE && selectedCells.size() > 1) { // Select all days in between start and end. Date start = selectedCells.get(0).getDate(); Date end = selectedCells.get(1).getDate(); selectedCells.get(0).setRangeState(MonthCellDescriptor.RangeState.FIRST); selectedCells.get(1).setRangeState(MonthCellDescriptor.RangeState.LAST); for (List<List<MonthCellDescriptor>> month : cells) { for (List<MonthCellDescriptor> week : month) { for (MonthCellDescriptor singleCell : week) { if (singleCell.getDate().after(start) && singleCell.getDate().before(end) && singleCell.isSelectable()) { singleCell.setSelected(true); singleCell.setRangeState(MonthCellDescriptor.RangeState.MIDDLE); selectedCells.add(singleCell); } } } } } } // Update the adapter. validateAndUpdate(); return date != null; }
/** * Both date parameters must be non-null and their {@link Date#getTime()} must not return 0. Time * of day will be ignored. For instance, if you pass in {@code minDate} as 11/16/2012 5:15pm and * {@code maxDate} as 11/16/2013 4:30am, 11/16/2012 will be the first selectable date and * 11/15/2013 will be the last selectable date ({@code maxDate} is exclusive). * * <p>This will implicitly set the {@link SelectionMode} to {@link SelectionMode#SINGLE}. If you * want a different selection mode, use {@link FluentInitializer#inMode(SelectionMode)} on the * {@link FluentInitializer} this method returns. * * <p>The calendar will be constructed using the given locale. This means that all names (months, * days) will be in the language of the locale and the weeks start with the day specified by the * locale. * * @param minDate Earliest selectable date, inclusive. Must be earlier than {@code maxDate}. * @param maxDate Latest selectable date, exclusive. Must be later than {@code minDate}. */ public FluentInitializer init(Date minDate, Date maxDate, Locale locale) { if (minDate == null || maxDate == null) { throw new IllegalArgumentException( "minDate and maxDate must be non-null. " + dbg(minDate, maxDate)); } if (minDate.after(maxDate)) { throw new IllegalArgumentException( "minDate must be before maxDate. " + dbg(minDate, maxDate)); } if (minDate.getTime() == 0 || maxDate.getTime() == 0) { throw new IllegalArgumentException( "minDate and maxDate must be non-zero. " + dbg(minDate, maxDate)); } if (locale == null) { throw new IllegalArgumentException("Locale is null."); } // Make sure that all calendar instances use the same locale. this.locale = locale; today = Calendar.getInstance(locale); minCal = Calendar.getInstance(locale); maxCal = Calendar.getInstance(locale); monthCounter = Calendar.getInstance(locale); monthNameFormat = new SimpleDateFormat(getContext().getString(R.string.month_name_format), locale); for (MonthDescriptor month : months) { month.setLabel(monthNameFormat.format(month.getDate())); } weekdayNameFormat = new SimpleDateFormat(getContext().getString(R.string.day_name_format), locale); fullDateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale); this.selectionMode = SelectionMode.SINGLE; // Clear out any previously-selected dates/cells. selectedCals.clear(); selectedCells.clear(); highlightedCells.clear(); // Clear previous state. cells.clear(); months.clear(); minCal.setTime(minDate); maxCal.setTime(maxDate); setMidnight(minCal); setMidnight(maxCal); displayOnly = false; // maxDate is exclusive: bump back to the previous day so if maxDate is // the first of a month, // we don't accidentally include that month in the view. maxCal.add(MINUTE, -1); // Now iterate between minCal and maxCal and build up our list of months // to show. monthCounter.setTime(minCal.getTime()); final int maxMonth = maxCal.get(MONTH); final int maxYear = maxCal.get(YEAR); while ((monthCounter.get(MONTH) <= maxMonth // Up to, including the // month. || monthCounter.get(YEAR) < maxYear) // Up to the year. && monthCounter.get(YEAR) < maxYear + 1) { // But not > next yr. Date date = monthCounter.getTime(); MonthDescriptor month = new MonthDescriptor( monthCounter.get(MONTH), monthCounter.get(YEAR), date, monthNameFormat.format(date)); cells.add(getMonthCells(month, monthCounter)); // Logr.d("Adding month %s", month); months.add(month); monthCounter.add(MONTH, 1); } validateAndUpdate(); return new FluentInitializer(); }