Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jitpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
jdk:
- openjdk11
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public interface DateSelector<S> extends Parcelable {
*/
void setSelection(@NonNull S selection);

/**
* Clears the current selection.
*/
void clearSelection();

/**
* Allows this selection handler to respond to clicks within the {@link AdapterView}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,19 @@ void sendAccessibilityFocusEventToMonthDropdown() {
}
}

public void clearSelection() {
if (dateSelector != null) {
// Clear the selection then call 'selection changed' listeners, if any
dateSelector.clearSelection();
for (OnSelectionChangedListener<S> listener : onSelectionChangedListeners) {
listener.onSelectionChanged(dateSelector.getSelection());
}

// Update the gridview
recyclerView.getAdapter().notifyDataSetChanged();
}
}

void toggleVisibleSelector() {
if (calendarSelector == CalendarSelector.YEAR) {
setSelector(CalendarSelector.DAY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
Expand Down Expand Up @@ -98,10 +99,13 @@ public class MaterialDatePicker<S> extends DialogFragment {
private static final String INPUT_MODE_KEY = "INPUT_MODE_KEY";
private static final String CALENDAR_FRAGMENT_TAG = "CALENDAR_FRAGMENT_TAG";
private static final String TEXT_INPUT_FRAGMENT_TAG = "TEXT_INPUT_FRAGMENT_TAG";
private static final String CLEARABLE_KEY = "CLEARABLE_KEY";
private static final String DISMISS_ON_CLEAR_KEY = "DISMISS_ON_CLEAR_KEY";

static final Object CONFIRM_BUTTON_TAG = "CONFIRM_BUTTON_TAG";
static final Object CANCEL_BUTTON_TAG = "CANCEL_BUTTON_TAG";
static final Object TOGGLE_BUTTON_TAG = "TOGGLE_BUTTON_TAG";
static final Object CLEAR_BUTTON_TAG = "CLEAR_BUTTON_TAG";

/** Date picker will start with calendar view. */
public static final int INPUT_MODE_CALENDAR = 0;
Expand Down Expand Up @@ -142,6 +146,8 @@ public String getHeaderText() {
new LinkedHashSet<>();
private final LinkedHashSet<DialogInterface.OnCancelListener> onCancelListeners =
new LinkedHashSet<>();
private final LinkedHashSet<OnClickListener> onClearButtonClickListeners =
new LinkedHashSet<>();
private final LinkedHashSet<DialogInterface.OnDismissListener> onDismissListeners =
new LinkedHashSet<>();

Expand All @@ -168,6 +174,10 @@ public String getHeaderText() {
private CheckableImageButton headerToggleButton;
@Nullable private MaterialShapeDrawable background;
private Button confirmButton;
private Button headerConfirmButton;
private Button clearButton;
private boolean isClearable;
private boolean dismissOnClear;

private boolean edgeToEdgeEnabled;
@Nullable private CharSequence fullTitleText;
Expand Down Expand Up @@ -198,6 +208,8 @@ static <S> MaterialDatePicker<S> newInstance(@NonNull Builder<S> options) {
options.negativeButtonContentDescriptionResId);
args.putCharSequence(
NEGATIVE_BUTTON_CONTENT_DESCRIPTION_KEY, options.negativeButtonContentDescription);
args.putBoolean(CLEARABLE_KEY, options.isClearable);
args.putBoolean(DISMISS_ON_CLEAR_KEY, options.dismissOnClear);
materialDatePickerDialogFragment.setArguments(args);
return materialDatePickerDialogFragment;
}
Expand Down Expand Up @@ -231,6 +243,8 @@ public final void onSaveInstanceState(@NonNull Bundle bundle) {
NEGATIVE_BUTTON_CONTENT_DESCRIPTION_RES_ID_KEY, negativeButtonContentDescriptionResId);
bundle.putCharSequence(
NEGATIVE_BUTTON_CONTENT_DESCRIPTION_KEY, negativeButtonContentDescription);
bundle.putBoolean(CLEARABLE_KEY, isClearable);
bundle.putBoolean(DISMISS_ON_CLEAR_KEY, dismissOnClear);
}

@Override
Expand Down Expand Up @@ -260,6 +274,8 @@ public final void onCreate(@Nullable Bundle bundle) {
fullTitleText =
titleText != null ? titleText : requireContext().getResources().getText(titleTextResId);
singleLineTitleText = getFirstLineBySeparator(fullTitleText);
isClearable = activeBundle.getBoolean(CLEARABLE_KEY);
dismissOnClear = activeBundle.getBoolean(DISMISS_ON_CLEAR_KEY);
}

private int getThemeResId(Context context) {
Expand Down Expand Up @@ -330,11 +346,7 @@ public final View onCreateView(
initHeaderToggle(context);

confirmButton = root.findViewById(R.id.confirm_button);
if (getDateSelector().isSelectionComplete()) {
confirmButton.setEnabled(true);
} else {
confirmButton.setEnabled(false);
}
confirmButton.setEnabled(getDateSelector().isSelectionComplete());
confirmButton.setTag(CONFIRM_BUTTON_TAG);
if (positiveButtonText != null) {
confirmButton.setText(positiveButtonText);
Expand Down Expand Up @@ -363,6 +375,76 @@ public final View onCreateView(
getContext().getResources().getText(negativeButtonContentDescriptionResId));
}
cancelButton.setOnClickListener(this::onNegativeButtonClick);

// In fullscreen mode we have a confirm and cancel button in the header as well
if (fullscreen) {
headerConfirmButton = root.findViewById(R.id.header_confirm_button);
headerConfirmButton.setEnabled(getDateSelector().isSelectionComplete());
headerConfirmButton.setTag(CONFIRM_BUTTON_TAG);
if (positiveButtonText != null) {
headerConfirmButton.setText(positiveButtonText);
} else if (positiveButtonTextResId != 0) {
headerConfirmButton.setText(positiveButtonTextResId);
}
headerConfirmButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
for (MaterialPickerOnPositiveButtonClickListener<? super S> listener :
onPositiveButtonClickListeners) {
listener.onPositiveButtonClick(getSelection());
}
dismiss();
}
});

Button headerCancelButton = root.findViewById(R.id.header_cancel_button);
headerCancelButton.setTag(CANCEL_BUTTON_TAG);
if (negativeButtonText != null) {
headerCancelButton.setText(negativeButtonText);
} else if (negativeButtonTextResId != 0) {
headerCancelButton.setText(negativeButtonTextResId);
}
headerCancelButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
for (View.OnClickListener listener : onNegativeButtonClickListeners) {
listener.onClick(v);
}
dismiss();
}
});
}

clearButton = root.findViewById(R.id.clear_button);
clearButton.setTag(CLEAR_BUTTON_TAG);
if (isClearable) {
clearButton.setEnabled(getDateSelector().isSelectionComplete());
clearButton.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
// Clear the selection
calendar.clearSelection();

// Call 'cleared' listeners, if any
for (View.OnClickListener listener : onClearButtonClickListeners) {
listener.onClick(v);
}

if (dismissOnClear) {
dismiss();
}
}
});

clearButton.setVisibility(View.VISIBLE);

} else {
clearButton.setVisibility(View.GONE);
}

return root;
}

Expand Down Expand Up @@ -530,12 +612,14 @@ private void startPickerFragment() {
@Override
public void onSelectionChanged(S selection) {
updateHeader(getHeaderText());
confirmButton.setEnabled(getDateSelector().isSelectionComplete());
updateActionButtonEnabledState();
}

@Override
public void onIncompleteSelectionChanged() {
confirmButton.setEnabled(false);
if (fullscreen) headerConfirmButton.setEnabled(false);
clearButton.setEnabled(false);
}
});

Expand All @@ -560,7 +644,10 @@ private void initHeaderToggle(Context context) {
headerToggleButton.setOnClickListener(
v -> {
// Update confirm button in case in progress selection has been reset
confirmButton.setEnabled(getDateSelector().isSelectionComplete());
updateActionButtonEnabledState();
v -> {
// Update confirm button in case in progress selection has been reset
updateActionButtonEnabledState();

headerToggleButton.toggle();
inputMode = (inputMode == INPUT_MODE_TEXT) ? INPUT_MODE_CALENDAR : INPUT_MODE_TEXT;
Expand All @@ -569,6 +656,12 @@ private void initHeaderToggle(Context context) {
});
}

private void updateActionButtonEnabledState() {
confirmButton.setEnabled(getDateSelector().isSelectionComplete());
if (fullscreen) headerConfirmButton.setEnabled(getDateSelector().isSelectionComplete());
clearButton.setEnabled(isClearable && getDateSelector().isSelectionComplete());
}

private void updateToggleContentDescription(@NonNull CheckableImageButton toggle) {
String contentDescription =
inputMode == INPUT_MODE_TEXT
Expand Down Expand Up @@ -683,6 +776,27 @@ public void clearOnNegativeButtonClickListeners() {
onNegativeButtonClickListeners.clear();
}

/** The supplied listener is called when the user clicks the clear button. */
public boolean addOnClearButtonClickListener(
@NonNull OnClickListener onClearButtonClickListener) {
return onClearButtonClickListeners.add(onClearButtonClickListener);
}

/**
* Removes a listener previously added via {@link MaterialDatePicker#addOnClearButtonClickListener}.
*/
public boolean removeOnClearButtonClickListener(
@NonNull OnClickListener onClearButtonClickListener) {
return onClearButtonClickListeners.remove(onClearButtonClickListener);
}

/**
* Removes all listeners added via {@link MaterialDatePicker#addOnClearButtonClickListener}.
*/
public void clearOnClearButtonClickListeners() {
onClearButtonClickListeners.clear();
}

/**
* The supplied listener is called when the user cancels the picker via back button or a touch
* outside the view. It is not called when the user clicks the cancel button. To add a listener
Expand Down Expand Up @@ -741,6 +855,8 @@ public static final class Builder<S> {
CharSequence negativeButtonContentDescription = null;
@Nullable S selection = null;
@InputMode int inputMode = INPUT_MODE_CALENDAR;
boolean isClearable = false;
boolean dismissOnClear = false;

private Builder(DateSelector<S> dateSelector) {
this.dateSelector = dateSelector;
Expand Down Expand Up @@ -962,6 +1078,20 @@ public Builder<S> setInputMode(@InputMode int inputMode) {
return this;
}

/** Sets the state of the clear button */
@NonNull
public Builder<S> setClearable(boolean clearable) {
this.isClearable = clearable;
return this;
}

/** Sets the state of the dialog dismiss state when clear button is clicked */
@NonNull
public Builder<S> setDismissOnClear(boolean dismiss) {
this.dismissOnClear = dismiss;
return this;
}

/** Creates a {@link MaterialDatePicker} with the provided options. */
@NonNull
public MaterialDatePicker<S> build() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ public boolean isSelectionComplete() {
&& isValidRange(selectedStartItem, selectedEndItem);
}

@Override
public void clearSelection() {
selectedStartItem = null;
selectedEndItem = null;
}

@Override
public void setSelection(@NonNull Pair<Long, Long> selection) {
if (selection.first != null && selection.second != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ public void select(long selection) {
selectedItem = selection;
}

private void clearSelection() {
@Override
public void clearSelection() {
selectedItem = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@
android:gravity="end"
android:orientation="horizontal">

<Button
android:id="@+id/clear_button"
style="?attr/buttonBarNeutralButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/mtrl_picker_clear"/>

<Space
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="invisible" />

<Button
android:id="@+id/cancel_button"
style="?attr/buttonBarNegativeButtonStyle"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 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.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/date_picker_actions"
android:layout_width="match_parent"
android:layout_height="@dimen/mtrl_calendar_action_height_fullscreen"
android:paddingStart="@dimen/mtrl_calendar_action_padding_fullscreen"
android:paddingLeft="@dimen/mtrl_calendar_action_padding_fullscreen"
android:paddingEnd="@dimen/mtrl_calendar_action_padding_fullscreen"
android:paddingRight="@dimen/mtrl_calendar_action_padding_fullscreen"
android:gravity="end"
android:orientation="horizontal">

<Button
android:id="@+id/clear_button"
style="?attr/buttonBarNeutralButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/mtrl_picker_clear"/>

<Space
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="invisible" />

<Button
android:id="@+id/cancel_button"
style="?attr/buttonBarNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/mtrl_picker_cancel"/>

<Button
android:id="@+id/confirm_button"
style="?attr/buttonBarPositiveButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:minWidth="@dimen/mtrl_calendar_action_confirm_button_min_width"
android:text="@string/mtrl_picker_confirm"/>

</LinearLayout>
Loading