package com.prolificinteractive.materialcalendarview;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.prolificinteractive.materialcalendarview.MaterialCalendarView.ShowOtherDates;
import com.prolificinteractive.materialcalendarview.format.DayFormatter;
import com.prolificinteractive.materialcalendarview.format.WeekDayFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import static com.prolificinteractive.materialcalendarview.MaterialCalendarView.SHOW_DEFAULTS;
import static com.prolificinteractive.materialcalendarview.MaterialCalendarView.showOtherMonths;
import static java.util.Calendar.DATE;
import static java.util.Calendar.DAY_OF_WEEK;
abstract class CalendarPagerView extends ViewGroup implements View.OnClickListener {
protected static final int DEFAULT_DAYS_IN_WEEK = 7;
protected static final int DEFAULT_MAX_WEEKS = 6;
protected static final int DAY_NAMES_ROW = 1;
private static final Calendar tempWorkingCalendar = CalendarUtils.getInstance();
private final ArrayList<WeekDayView> weekDayViews = new ArrayList<>();
private final ArrayList<DecoratorResult> decoratorResults = new ArrayList<>();
@ShowOtherDates
protected int showOtherDates = SHOW_DEFAULTS;
private MaterialCalendarView mcv;
private CalendarDay firstViewDay;
private CalendarDay minDate = null;
private CalendarDay maxDate = null;
private int firstDayOfWeek;
private final Collection<DayView> dayViews = new ArrayList<>();
public CalendarPagerView(@NonNull MaterialCalendarView view,
CalendarDay firstViewDay,
int firstDayOfWeek) {
super(view.getContext());
this.mcv = view;
this.firstViewDay = firstViewDay;
this.firstDayOfWeek = firstDayOfWeek;
setClipChildren(false);
setClipToPadding(false);
buildWeekDays(resetAndGetWorkingCalendar());
buildDayViews(dayViews, resetAndGetWorkingCalendar());
}
private void buildWeekDays(Calendar calendar) {
for (int i = 0; i < DEFAULT_DAYS_IN_WEEK; i++) {
WeekDayView weekDayView = new WeekDayView(getContext(), CalendarUtils.getDayOfWeek(calendar));
weekDayViews.add(weekDayView);
addView(weekDayView);
calendar.add(DATE, 1);
}
}
protected void addDayView(Collection<DayView> dayViews, Calendar calendar) {
CalendarDay day = CalendarDay.from(calendar);
DayView dayView = new DayView(getContext(), day);
dayView.setOnClickListener(this);
dayViews.add(dayView);
addView(dayView, new LayoutParams());
calendar.add(DATE, 1);
}
protected Calendar resetAndGetWorkingCalendar() {
getFirstViewDay().copyTo(tempWorkingCalendar);
//noinspection ResourceType
tempWorkingCalendar.setFirstDayOfWeek(getFirstDayOfWeek());
int dow = CalendarUtils.getDayOfWeek(tempWorkingCalendar);
int delta = getFirstDayOfWeek() - dow;
//If the delta is positive, we want to remove a week
boolean removeRow = showOtherMonths(showOtherDates) ? delta >= 0 : delta > 0;
if (removeRow) {
delta -= DEFAULT_DAYS_IN_WEEK;
}
tempWorkingCalendar.add(DATE, delta);
return tempWorkingCalendar;
}
protected int getFirstDayOfWeek() {
return firstDayOfWeek;
}
protected abstract void buildDayViews(Collection<DayView> dayViews, Calendar calendar);
protected abstract boolean isDayEnabled(CalendarDay day);
void setDayViewDecorators(List<DecoratorResult> results) {
this.decoratorResults.clear();
if (results != null) {
this.decoratorResults.addAll(results);
}
invalidateDecorators();
}
public void setWeekDayTextAppearance(int taId) {
for (WeekDayView weekDayView : weekDayViews) {
weekDayView.setTextAppearance(getContext(), taId);
}
}
public void setDateTextAppearance(int taId) {
for (DayView dayView : dayViews) {
dayView.setTextAppearance(getContext(), taId);
}
}
public void setShowOtherDates(@ShowOtherDates int showFlags) {
this.showOtherDates = showFlags;
updateUi();
}
public void setSelectionEnabled(boolean selectionEnabled) {
for (DayView dayView : dayViews) {
dayView.setOnClickListener(selectionEnabled ? this : null);
dayView.setClickable(selectionEnabled);
}
}
public void setSelectionColor(int color) {
for (DayView dayView : dayViews) {
dayView.setSelectionColor(color);
}
}
public void setWeekDayFormatter(WeekDayFormatter formatter) {
for (WeekDayView dayView : weekDayViews) {
dayView.setWeekDayFormatter(formatter);
}
}
public void setDayFormatter(DayFormatter formatter) {
for (DayView dayView : dayViews) {
dayView.setDayFormatter(formatter);
}
}
public void setMinimumDate(CalendarDay minDate) {
this.minDate = minDate;
updateUi();
}
public void setMaximumDate(CalendarDay maxDate) {
this.maxDate = maxDate;
updateUi();
}
public void setSelectedDates(Collection<CalendarDay> dates) {
for (DayView dayView : dayViews) {
CalendarDay day = dayView.getDate();
dayView.setChecked(dates != null && dates.contains(day));
}
postInvalidate();
}
protected void updateUi() {
for (DayView dayView : dayViews) {
CalendarDay day = dayView.getDate();
dayView.setupSelection(
showOtherDates, day.isInRange(minDate, maxDate), isDayEnabled(day));
}
postInvalidate();
}
protected void invalidateDecorators() {
final DayViewFacade facadeAccumulator = new DayViewFacade();
for (DayView dayView : dayViews) {
facadeAccumulator.reset();
for (DecoratorResult result : decoratorResults) {
if (result.decorator.shouldDecorate(dayView.getDate())) {
result.result.applyTo(facadeAccumulator);
}
}
dayView.applyFacade(facadeAccumulator);
}
}
@Override
public void onClick(View v) {
if (v instanceof DayView) {
final DayView dayView = (DayView) v;
mcv.onDateClicked(dayView);
}
}
/*
* Custom ViewGroup Code
*/
/**
* {@inheritDoc}
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
/**
* {@inheritDoc}
*/
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);
final int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
final int specHeightSize = MeasureSpec.getSize(heightMeasureSpec);
final int specHeightMode = MeasureSpec.getMode(heightMeasureSpec);
//We expect to be somewhere inside a MaterialCalendarView, which should measure EXACTLY
if (specHeightMode == MeasureSpec.UNSPECIFIED || specWidthMode == MeasureSpec.UNSPECIFIED) {
throw new IllegalStateException("CalendarPagerView should never be left to decide it's size");
}
//The spec width should be a correct multiple
final int measureTileWidth = specWidthSize / DEFAULT_DAYS_IN_WEEK;
final int measureTileHeight = specHeightSize / getRows();
//Just use the spec sizes
setMeasuredDimension(specWidthSize, specHeightSize);
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
measureTileWidth,
MeasureSpec.EXACTLY
);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
measureTileHeight,
MeasureSpec.EXACTLY
);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
/**
* Return the number of rows to display per page
* @return
*/
protected abstract int getRows();
/**
* {@inheritDoc}
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int parentLeft = 0;
int childTop = 0;
int childLeft = parentLeft;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
child.layout(childLeft, childTop, childLeft + width, childTop + height);
childLeft += width;
//We should warp every so many children
if (i % DEFAULT_DAYS_IN_WEEK == (DEFAULT_DAYS_IN_WEEK - 1)) {
childLeft = parentLeft;
childTop += height;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams();
}
@Override
public boolean shouldDelayChildPressedState() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams();
}
@Override
public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(CalendarPagerView.class.getName());
}
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(CalendarPagerView.class.getName());
}
protected CalendarDay getFirstViewDay() {
return firstViewDay;
}
/**
* Simple layout params class for MonthView, since every child is the same size
*/
protected static class LayoutParams extends MarginLayoutParams {
/**
* {@inheritDoc}
*/
public LayoutParams() {
super(WRAP_CONTENT, WRAP_CONTENT);
}
}
}