From 4ccb052d2bb2bc6d261e48b91694464df73a69a9 Mon Sep 17 00:00:00 2001 From: Felipe Roriz Date: Tue, 18 Feb 2020 16:38:35 -0500 Subject: [PATCH] [MaterialDatePicker] Added a DateValidator for a bounded range. Resolves https://github.com/material-components/material-components-android/pull/972 GIT_ORIGIN_REV_ID=29cbbd569f5284f0bdf4cc1bdad60f93f92856eb Co-authored-by: ymarian <38727469+ymarian@users.noreply.github.com> PiperOrigin-RevId: 295810824 --- .../DatePickerMainDemoFragment.java | 13 ++ .../res/layout/cat_picker_validation.xml | 12 +- .../catalog/datepicker/res/values/strings.xml | 2 + .../datepicker/CompositeDateValidator.java | 113 ++++++++++++++++++ .../CompositeDateValidatorTest.java | 71 +++++++++++ 5 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 lib/java/com/google/android/material/datepicker/CompositeDateValidator.java create mode 100644 lib/javatests/com/google/android/material/datepicker/CompositeDateValidatorTest.java diff --git a/catalog/java/io/material/catalog/datepicker/DatePickerMainDemoFragment.java b/catalog/java/io/material/catalog/datepicker/DatePickerMainDemoFragment.java index 33f63b290..128bab0ee 100644 --- a/catalog/java/io/material/catalog/datepicker/DatePickerMainDemoFragment.java +++ b/catalog/java/io/material/catalog/datepicker/DatePickerMainDemoFragment.java @@ -30,11 +30,14 @@ import android.widget.LinearLayout; import android.widget.RadioGroup; import com.google.android.material.button.MaterialButton; import com.google.android.material.datepicker.CalendarConstraints; +import com.google.android.material.datepicker.CompositeDateValidator; import com.google.android.material.datepicker.DateValidatorPointForward; import com.google.android.material.datepicker.MaterialDatePicker; import com.google.android.material.snackbar.Snackbar; import io.material.catalog.feature.DemoFragment; +import java.util.ArrayList; import java.util.Calendar; +import java.util.List; import java.util.TimeZone; /** A fragment that displays the main Picker demos for the Catalog app. */ @@ -190,6 +193,16 @@ public class DatePickerMainDemoFragment extends DemoFragment { constraintsBuilder.setValidator(DateValidatorPointForward.now()); } else if (validationChoice == R.id.cat_picker_validation_weekdays) { constraintsBuilder.setValidator(new DateValidatorWeekdays()); + } else if ((validationChoice == R.id.cat_picker_validation_last_two_weeks)) { + Calendar lowerBoundCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + lowerBoundCalendar.add(Calendar.DAY_OF_MONTH, -14); + long lowerBound = lowerBoundCalendar.getTimeInMillis(); + + List validators = new ArrayList<>(); + validators.add(DateValidatorPointForward.from(lowerBound)); + validators.add(new DateValidatorWeekdays()); + + constraintsBuilder.setValidator(CompositeDateValidator.allOf(validators)); } return constraintsBuilder; } diff --git a/catalog/java/io/material/catalog/datepicker/res/layout/cat_picker_validation.xml b/catalog/java/io/material/catalog/datepicker/res/layout/cat_picker_validation.xml index b85783c78..d7a553d00 100644 --- a/catalog/java/io/material/catalog/datepicker/res/layout/cat_picker_validation.xml +++ b/catalog/java/io/material/catalog/datepicker/res/layout/cat_picker_validation.xml @@ -33,7 +33,7 @@ android:id="@+id/cat_picker_validation_group" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> + android:orientation="vertical"> + diff --git a/catalog/java/io/material/catalog/datepicker/res/values/strings.xml b/catalog/java/io/material/catalog/datepicker/res/values/strings.xml index 20b437c02..78cec210d 100644 --- a/catalog/java/io/material/catalog/datepicker/res/values/strings.xml +++ b/catalog/java/io/material/catalog/datepicker/res/values/strings.xml @@ -65,6 +65,8 @@ Today onward Weekdays + + Last 2 weeks Picker Title diff --git a/lib/java/com/google/android/material/datepicker/CompositeDateValidator.java b/lib/java/com/google/android/material/datepicker/CompositeDateValidator.java new file mode 100644 index 000000000..f35e18ce3 --- /dev/null +++ b/lib/java/com/google/android/material/datepicker/CompositeDateValidator.java @@ -0,0 +1,113 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.material.datepicker; + +import static androidx.core.util.Preconditions.checkNotNull; + +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.NonNull; +import com.google.android.material.datepicker.CalendarConstraints.DateValidator; +import java.util.List; + +/** A {@link DateValidator} that accepts a list of Date Validators. */ +public final class CompositeDateValidator implements DateValidator { + + @NonNull private final List validators; + + private CompositeDateValidator(@NonNull List validators) { + this.validators = validators; + } + + /** + * Returns a {@link DateValidator} that can perform validation for every given {@link + * #validators}. + */ + @NonNull + public static DateValidator allOf(@NonNull List validators) { + return new CompositeDateValidator(validators); + } + + /** Part of {@link Parcelable} requirements. Do not use. */ + public static final Creator CREATOR = + new Creator() { + @NonNull + @Override + public CompositeDateValidator createFromParcel(@NonNull Parcel source) { + @SuppressWarnings("unchecked") + List validators = + source.readArrayList(DateValidator.class.getClassLoader()); + return new CompositeDateValidator(checkNotNull(validators)); + } + + @NonNull + @Override + public CompositeDateValidator[] newArray(int size) { + return new CompositeDateValidator[size]; + } + }; + + /** + * Performs the {@link DateValidator#isValid(long)} check as an AND of all validators in {@link + * #validators}. e.g. If every validator in this class returns `true` for each {@link + * DateValidator#isValid(long)}, this this method will return true. + * + * @param date milliseconds date to validate against. + * @return True, if the given date is valid for every given validator in this class. + */ + @Override + public boolean isValid(long date) { + for (DateValidator validator : validators) { + if (validator == null) { + continue; + } + if (!validator.isValid(date)) { + return false; + } + } + return true; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeList(validators); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof CompositeDateValidator)) { + return false; + } + + CompositeDateValidator that = (CompositeDateValidator) o; + + return validators.equals(that.validators); + } + + @Override + public int hashCode() { + return validators.hashCode(); + } +} diff --git a/lib/javatests/com/google/android/material/datepicker/CompositeDateValidatorTest.java b/lib/javatests/com/google/android/material/datepicker/CompositeDateValidatorTest.java new file mode 100644 index 000000000..5e71575b1 --- /dev/null +++ b/lib/javatests/com/google/android/material/datepicker/CompositeDateValidatorTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.material.datepicker; + +import com.google.android.material.R; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import com.google.android.material.datepicker.CalendarConstraints.DateValidator; +import java.util.ArrayList; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Test for {@link CompositeDateValidator} */ +@RunWith(RobolectricTestRunner.class) +public class CompositeDateValidatorTest { + + private DateValidator subject; + + @Before + public void createSubject() { + DateValidator validator1 = DateValidatorPointBackward.before(5); + DateValidator validator2 = DateValidatorPointForward.from(2); + + ArrayList validators = new ArrayList<>(2); + validators.add(validator1); + validators.add(validator2); + + subject = CompositeDateValidator.allOf(validators); + } + + @Test + public void testValidDate() { + assertThat(subject.isValid(4)).isTrue(); + } + + @Test + public void testInvalidDate() { + assertThat(subject.isValid(1)).isFalse(); + assertThat(subject.isValid(6)).isFalse(); + } + + @Test + public void testParcelable() { + DateValidator original = subject; + + Parcel parcel = Parcel.obtain(); + original.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + DateValidator createdFromParcel = CompositeDateValidator.CREATOR.createFromParcel(parcel); + assertThat(original).isEqualTo(createdFromParcel); + } +}