Skip to content

Commit fb44b90

Browse files
ldjcmuikim24
authored andcommitted
Use CalendarBounds to invalidate out of bounds dates
PiperOrigin-RevId: 257249906
1 parent 31d0e9a commit fb44b90

22 files changed

+293
-143
lines changed

catalog/java/io/material/catalog/picker/PickerMainDemoFragment.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public View onCreateDemoView(
6363
dialogLaunchersLayout,
6464
R.string.cat_picker_date_calendar_fullscreen,
6565
MaterialDatePicker.Builder.datePicker().setTheme(fullscreenTheme));
66+
6667
setupDialogFragment(
6768
dialogLaunchersLayout,
6869
R.string.cat_picker_date_range_calendar,

lib/java/com/google/android/material/picker/CalendarBounds.java renamed to lib/java/com/google/android/material/picker/CalendarConstraints.java

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,28 @@
2727
* @hide
2828
*/
2929
@RestrictTo(Scope.LIBRARY_GROUP)
30-
public final class CalendarBounds implements Parcelable {
30+
public final class CalendarConstraints implements Parcelable {
3131

3232
private final Month start;
3333
private final Month end;
3434
private final Month current;
35+
private final DateValidator validator;
3536

3637
private final int yearSpan;
3738
private final int monthSpan;
3839

39-
private CalendarBounds(Month start, Month end, Month current) {
40+
/** Used to determine whether {@link MaterialCalendar} days are enabled. */
41+
public interface DateValidator extends Parcelable {
42+
43+
/** Returns true if the provided {@code date} is enabled. */
44+
boolean isValid(long date);
45+
}
46+
47+
private CalendarConstraints(Month start, Month end, Month current, DateValidator validator) {
4048
this.start = start;
4149
this.end = end;
4250
this.current = current;
51+
this.validator = validator;
4352
if (start.compareTo(current) > 0) {
4453
throw new IllegalArgumentException("start Month cannot be after current Month");
4554
}
@@ -51,23 +60,37 @@ private CalendarBounds(Month start, Month end, Month current) {
5160
}
5261

5362
/**
54-
* Creates a CalendarBounds instance which opens onto {@code current} and is bounded between
63+
* Creates a CalendarConstraints instance that opens on today if it is within the bounds or {@code
64+
* start} if today is not within the bounds.
65+
*/
66+
public static CalendarConstraints create(Month start, Month end) {
67+
Month today = Month.today();
68+
if (end.compareTo(today) >= 0 && today.compareTo(start) >= 0) {
69+
return create(start, end, Month.today());
70+
}
71+
return create(start, end, start);
72+
}
73+
74+
/**
75+
* Creates a CalendarConstraints instance which opens onto {@code current} and is bounded between
5576
* {@code start} and {@code end}.
5677
*/
57-
public static CalendarBounds create(Month start, Month end, Month current) {
58-
return new CalendarBounds(start, end, current);
78+
public static CalendarConstraints create(Month start, Month end, Month current) {
79+
return create(start, end, current, new DateValidatorPointForward(0));
5980
}
6081

6182
/**
62-
* Creates a CalendarBounds instance that opens on today if it is within the bounds or {@code
63-
* start} if today is not within the bounds.
83+
* Creates a CalendarConstraints instance which opens onto {@code current}, is bounded between
84+
* {@code start} and {@code end}, and disables dates for which {@link DateValidator#isValid(long)}
85+
* is false.
6486
*/
65-
public static CalendarBounds create(Month start, Month end) {
66-
Month today = Month.today();
67-
if (end.compareTo(today) >= 0 && today.compareTo(start) >= 0) {
68-
return new CalendarBounds(start, end, Month.today());
69-
}
70-
return new CalendarBounds(start, end, start);
87+
public static CalendarConstraints create(
88+
Month start, Month end, Month current, DateValidator validator) {
89+
return new CalendarConstraints(start, end, current, validator);
90+
}
91+
92+
public DateValidator getDateValidator() {
93+
return validator;
7194
}
7295

7396
/** Returns the earliest {@link Month} allowed by this set of bounds. */
@@ -106,10 +129,10 @@ public boolean equals(Object o) {
106129
if (this == o) {
107130
return true;
108131
}
109-
if (!(o instanceof CalendarBounds)) {
132+
if (!(o instanceof CalendarConstraints)) {
110133
return false;
111134
}
112-
CalendarBounds that = (CalendarBounds) o;
135+
CalendarConstraints that = (CalendarConstraints) o;
113136
return start.equals(that.start) && end.equals(that.end) && current.equals(that.current);
114137
}
115138

@@ -122,19 +145,20 @@ public int hashCode() {
122145
/* Parcelable interface */
123146

124147
/** {@link Parcelable.Creator} */
125-
public static final Parcelable.Creator<CalendarBounds> CREATOR =
126-
new Parcelable.Creator<CalendarBounds>() {
148+
public static final Parcelable.Creator<CalendarConstraints> CREATOR =
149+
new Parcelable.Creator<CalendarConstraints>() {
127150
@Override
128-
public CalendarBounds createFromParcel(Parcel source) {
151+
public CalendarConstraints createFromParcel(Parcel source) {
129152
Month start = source.readParcelable(Month.class.getClassLoader());
130153
Month end = source.readParcelable(Month.class.getClassLoader());
131154
Month current = source.readParcelable(Month.class.getClassLoader());
132-
return CalendarBounds.create(start, end, current);
155+
DateValidator validator = source.readParcelable(DateValidator.class.getClassLoader());
156+
return CalendarConstraints.create(start, end, current, validator);
133157
}
134158

135159
@Override
136-
public CalendarBounds[] newArray(int size) {
137-
return new CalendarBounds[size];
160+
public CalendarConstraints[] newArray(int size) {
161+
return new CalendarConstraints[size];
138162
}
139163
};
140164

@@ -148,5 +172,6 @@ public void writeToParcel(Parcel dest, int flags) {
148172
dest.writeParcelable(start, /* parcelableFlags= */ 0);
149173
dest.writeParcelable(end, /* parcelableFlags= */ 0);
150174
dest.writeParcelable(current, /* parcelableFlags= */ 0);
175+
dest.writeParcelable(validator, /* parcelableFlags = */ 0);
151176
}
152177
}

lib/java/com/google/android/material/picker/CalendarStyle.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,15 @@ final class CalendarStyle {
6262
*/
6363
final CalendarItemStyle todayYear;
6464

65+
final CalendarItemStyle invalidDay;
66+
6567
/**
6668
* A {@link Paint} for styling days between selected days with {@link
6769
* R.styleable#MaterialCalendar_rangeFillColor}.
6870
*/
6971
final Paint rangeFill;
7072

71-
private final Context context;
72-
73-
boolean refreshStyles(Context context) {
74-
return !this.context.equals(context);
75-
}
76-
7773
CalendarStyle(Context context) {
78-
this.context = context;
7974
int calendarStyle =
8075
MaterialAttributes.resolveOrThrow(
8176
context, R.attr.materialCalendarStyle, MaterialCalendar.class.getCanonicalName());
@@ -85,6 +80,10 @@ boolean refreshStyles(Context context) {
8580
day =
8681
CalendarItemStyle.create(
8782
context, calendarAttributes.getResourceId(R.styleable.MaterialCalendar_dayStyle, 0));
83+
invalidDay =
84+
CalendarItemStyle.create(
85+
context,
86+
calendarAttributes.getResourceId(R.styleable.MaterialCalendar_dayInvalidStyle, 0));
8887
selectedDay =
8988
CalendarItemStyle.create(
9089
context,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2019 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+
package com.google.android.material.picker;
17+
18+
import android.os.Parcel;
19+
import android.os.Parcelable;
20+
import com.google.android.material.picker.CalendarConstraints.DateValidator;
21+
import java.util.Calendar;
22+
23+
/**
24+
* A {@link CalendarConstraints.DateValidator} that only allows dates from a given point onward to
25+
* be clicked.
26+
*/
27+
public class DateValidatorPointForward implements DateValidator {
28+
29+
private final long point;
30+
31+
public DateValidatorPointForward() {
32+
point = Calendar.getInstance().getTimeInMillis();
33+
}
34+
35+
public DateValidatorPointForward(long point) {
36+
this.point = point;
37+
}
38+
39+
public static final Parcelable.Creator<DateValidatorPointForward> CREATOR =
40+
new Parcelable.Creator<DateValidatorPointForward>() {
41+
@Override
42+
public DateValidatorPointForward createFromParcel(Parcel source) {
43+
return new DateValidatorPointForward(source.readLong());
44+
}
45+
46+
@Override
47+
public DateValidatorPointForward[] newArray(int size) {
48+
return new DateValidatorPointForward[size];
49+
}
50+
};
51+
52+
@Override
53+
public boolean isValid(long date) {
54+
return date >= point;
55+
}
56+
57+
@Override
58+
public int describeContents() {
59+
return 0;
60+
}
61+
62+
@Override
63+
public void writeToParcel(Parcel dest, int flags) {
64+
dest.writeLong(point);
65+
}
66+
}

lib/java/com/google/android/material/picker/MaterialCalendar.java

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ enum CalendarSelector {
5858

5959
private static final String THEME_RES_ID_KEY = "THEME_RES_ID_KEY";
6060
private static final String GRID_SELECTOR_KEY = "GRID_SELECTOR_KEY";
61-
private static final String CALENDAR_BOUNDS_KEY = "CALENDAR_BOUNDS_KEY";
61+
private static final String CALENDAR_CONSTRAINTS_KEY = "CALENDAR_CONSTRAINTS_KEY";
6262

6363
@VisibleForTesting
6464
@RestrictTo(Scope.LIBRARY_GROUP)
6565
public static final Object VIEW_PAGER_TAG = "VIEW_PAGER_TAG";
6666

6767
private int themeResId;
6868
private GridSelector<S> gridSelector;
69-
private CalendarBounds calendarBounds;
69+
private CalendarConstraints calendarConstraints;
7070
private CalendarSelector calendarSelector;
7171
private CalendarStyle calendarStyle;
7272
private RecyclerView yearSelector;
@@ -75,12 +75,12 @@ enum CalendarSelector {
7575
private View dayFrame;
7676

7777
static <T> MaterialCalendar<T> newInstance(
78-
GridSelector<T> gridSelector, int themeResId, CalendarBounds calendarBounds) {
78+
GridSelector<T> gridSelector, int themeResId, CalendarConstraints calendarConstraints) {
7979
MaterialCalendar<T> materialCalendar = new MaterialCalendar<>();
8080
Bundle args = new Bundle();
8181
args.putInt(THEME_RES_ID_KEY, themeResId);
8282
args.putParcelable(GRID_SELECTOR_KEY, gridSelector);
83-
args.putParcelable(CALENDAR_BOUNDS_KEY, calendarBounds);
83+
args.putParcelable(CALENDAR_CONSTRAINTS_KEY, calendarConstraints);
8484
materialCalendar.setArguments(args);
8585
return materialCalendar;
8686
}
@@ -90,7 +90,7 @@ public void onSaveInstanceState(@NonNull Bundle bundle) {
9090
super.onSaveInstanceState(bundle);
9191
bundle.putInt(THEME_RES_ID_KEY, themeResId);
9292
bundle.putParcelable(GRID_SELECTOR_KEY, gridSelector);
93-
bundle.putParcelable(CALENDAR_BOUNDS_KEY, calendarBounds);
93+
bundle.putParcelable(CALENDAR_CONSTRAINTS_KEY, calendarConstraints);
9494
}
9595

9696
@Override
@@ -99,7 +99,7 @@ public void onCreate(@Nullable Bundle bundle) {
9999
Bundle activeBundle = bundle == null ? getArguments() : bundle;
100100
themeResId = activeBundle.getInt(THEME_RES_ID_KEY);
101101
gridSelector = activeBundle.getParcelable(GRID_SELECTOR_KEY);
102-
calendarBounds = activeBundle.getParcelable(CALENDAR_BOUNDS_KEY);
102+
calendarConstraints = activeBundle.getParcelable(CALENDAR_CONSTRAINTS_KEY);
103103
}
104104

105105
@NonNull
@@ -112,7 +112,7 @@ public View onCreateView(
112112
calendarStyle = new CalendarStyle(themedContext);
113113
LayoutInflater themedInflater = layoutInflater.cloneInContext(themedContext);
114114

115-
Month earliestMonth = calendarBounds.getStart();
115+
Month earliestMonth = calendarConstraints.getStart();
116116

117117
int layout;
118118
int orientation;
@@ -139,15 +139,17 @@ public View onCreateView(
139139
getChildFragmentManager(),
140140
getLifecycle(),
141141
gridSelector,
142-
calendarBounds,
142+
calendarConstraints,
143143
day -> {
144-
gridSelector.select(day);
145-
for (OnSelectionChangedListener<S> listener : onSelectionChangedListeners) {
146-
listener.onSelectionChanged(gridSelector.getSelection());
147-
}
148-
monthsPager.getAdapter().notifyDataSetChanged();
149-
if (yearSelector != null) {
150-
yearSelector.getAdapter().notifyDataSetChanged();
144+
if (calendarConstraints.getDateValidator().isValid(day.getTimeInMillis())) {
145+
gridSelector.select(day);
146+
for (OnSelectionChangedListener<S> listener : onSelectionChangedListeners) {
147+
listener.onSelectionChanged(gridSelector.getSelection());
148+
}
149+
monthsPager.getAdapter().notifyDataSetChanged();
150+
if (yearSelector != null) {
151+
yearSelector.getAdapter().notifyDataSetChanged();
152+
}
151153
}
152154
});
153155
monthsPager.setAdapter(monthsPagerAdapter);
@@ -220,22 +222,24 @@ public void onDraw(
220222
};
221223
}
222224

223-
/** Returns the {@link CalendarBounds} in use by this {@link MaterialCalendar}. */
224-
CalendarBounds getCalendarBounds() {
225-
return calendarBounds;
225+
/** Returns the {@link CalendarConstraints} in use by this {@link MaterialCalendar}. */
226+
CalendarConstraints getCalendarConstraints() {
227+
return calendarConstraints;
226228
}
227229

228230
/**
229231
* Changes the currently displayed {@link Month} to {@code moveTo}.
230232
*
231233
* @throws IllegalArgumentException If {@code moveTo} is not within the allowed {@link
232-
* CalendarBounds}.
234+
* CalendarConstraints}.
233235
*/
234236
void setCurrentMonth(Month moveTo) {
235-
calendarBounds =
236-
CalendarBounds.create(calendarBounds.getStart(), calendarBounds.getEnd(), moveTo);
237+
calendarConstraints =
238+
CalendarConstraints.create(
239+
calendarConstraints.getStart(), calendarConstraints.getEnd(), moveTo);
237240
int moveToPosition =
238-
((MonthsPagerAdapter) monthPager.getAdapter()).getPosition(calendarBounds.getCurrent());
241+
((MonthsPagerAdapter) monthPager.getAdapter())
242+
.getPosition(calendarConstraints.getCurrent());
239243
monthPager.setCurrentItem(moveToPosition);
240244
}
241245

@@ -266,7 +270,7 @@ void setSelector(CalendarSelector selector) {
266270
.getLayoutManager()
267271
.scrollToPosition(
268272
((YearGridAdapter) yearSelector.getAdapter())
269-
.getPositionForYear(calendarBounds.getCurrent().year));
273+
.getPositionForYear(calendarConstraints.getCurrent().year));
270274
yearFrame.setVisibility(View.VISIBLE);
271275
dayFrame.setVisibility(View.GONE);
272276
} else if (selector == CalendarSelector.DAY) {
@@ -299,11 +303,12 @@ private void addActionsToMonthNavigation(
299303
new OnPageChangeCallback() {
300304
@Override
301305
public void onPageSelected(int position) {
302-
calendarBounds =
303-
CalendarBounds.create(
304-
calendarBounds.getStart(),
305-
calendarBounds.getEnd(),
306-
monthsPagerAdapter.getPageMonth(position));
306+
calendarConstraints =
307+
CalendarConstraints.create(
308+
calendarConstraints.getStart(),
309+
calendarConstraints.getEnd(),
310+
monthsPagerAdapter.getPageMonth(position),
311+
calendarConstraints.getDateValidator());
307312
monthDropSelect.setText(monthsPagerAdapter.getPageTitle(position));
308313
}
309314
});

0 commit comments

Comments
 (0)