3939import android .text .TextUtils ;
4040import android .view .LayoutInflater ;
4141import android .view .View ;
42+ import android .view .View .OnClickListener ;
4243import android .view .ViewGroup ;
4344import android .view .Window ;
4445import android .widget .Button ;
@@ -99,10 +100,13 @@ public class MaterialDatePicker<S> extends DialogFragment {
99100 private static final String INPUT_MODE_KEY = "INPUT_MODE_KEY" ;
100101 private static final String CALENDAR_FRAGMENT_TAG = "CALENDAR_FRAGMENT_TAG" ;
101102 private static final String TEXT_INPUT_FRAGMENT_TAG = "TEXT_INPUT_FRAGMENT_TAG" ;
103+ private static final String CLEARABLE_KEY = "CLEARABLE_KEY" ;
104+ private static final String DISMISS_ON_CLEAR_KEY = "DISMISS_ON_CLEAR_KEY" ;
102105
103106 static final Object CONFIRM_BUTTON_TAG = "CONFIRM_BUTTON_TAG" ;
104107 static final Object CANCEL_BUTTON_TAG = "CANCEL_BUTTON_TAG" ;
105108 static final Object TOGGLE_BUTTON_TAG = "TOGGLE_BUTTON_TAG" ;
109+ static final Object CLEAR_BUTTON_TAG = "CLEAR_BUTTON_TAG" ;
106110
107111 /** Date picker will start with calendar view. */
108112 public static final int INPUT_MODE_CALENDAR = 0 ;
@@ -143,6 +147,8 @@ public String getHeaderText() {
143147 new LinkedHashSet <>();
144148 private final LinkedHashSet <DialogInterface .OnCancelListener > onCancelListeners =
145149 new LinkedHashSet <>();
150+ private final LinkedHashSet <OnClickListener > onClearButtonClickListeners =
151+ new LinkedHashSet <>();
146152 private final LinkedHashSet <DialogInterface .OnDismissListener > onDismissListeners =
147153 new LinkedHashSet <>();
148154
@@ -169,6 +175,9 @@ public String getHeaderText() {
169175 private CheckableImageButton headerToggleButton ;
170176 @ Nullable private MaterialShapeDrawable background ;
171177 private Button confirmButton ;
178+ private Button clearButton ;
179+ private boolean isClearable ;
180+ private boolean dismissOnClear ;
172181
173182 private boolean edgeToEdgeEnabled ;
174183 @ Nullable private CharSequence fullTitleText ;
@@ -199,6 +208,8 @@ static <S> MaterialDatePicker<S> newInstance(@NonNull Builder<S> options) {
199208 options .negativeButtonContentDescriptionResId );
200209 args .putCharSequence (
201210 NEGATIVE_BUTTON_CONTENT_DESCRIPTION_KEY , options .negativeButtonContentDescription );
211+ args .putBoolean (CLEARABLE_KEY , options .isClearable );
212+ args .putBoolean (DISMISS_ON_CLEAR_KEY , options .dismissOnClear );
202213 materialDatePickerDialogFragment .setArguments (args );
203214 return materialDatePickerDialogFragment ;
204215 }
@@ -232,6 +243,8 @@ public final void onSaveInstanceState(@NonNull Bundle bundle) {
232243 NEGATIVE_BUTTON_CONTENT_DESCRIPTION_RES_ID_KEY , negativeButtonContentDescriptionResId );
233244 bundle .putCharSequence (
234245 NEGATIVE_BUTTON_CONTENT_DESCRIPTION_KEY , negativeButtonContentDescription );
246+ bundle .putBoolean (CLEARABLE_KEY , isClearable );
247+ bundle .putBoolean (DISMISS_ON_CLEAR_KEY , dismissOnClear );
235248 }
236249
237250 @ Override
@@ -261,6 +274,8 @@ public final void onCreate(@Nullable Bundle bundle) {
261274 fullTitleText =
262275 titleText != null ? titleText : requireContext ().getResources ().getText (titleTextResId );
263276 singleLineTitleText = getFirstLineBySeparator (fullTitleText );
277+ isClearable = activeBundle .getBoolean (CLEARABLE_KEY );
278+ dismissOnClear = activeBundle .getBoolean (DISMISS_ON_CLEAR_KEY );
264279 }
265280
266281 private int getThemeResId (Context context ) {
@@ -331,11 +346,7 @@ public final View onCreateView(
331346 initHeaderToggle (context );
332347
333348 confirmButton = root .findViewById (R .id .confirm_button );
334- if (getDateSelector ().isSelectionComplete ()) {
335- confirmButton .setEnabled (true );
336- } else {
337- confirmButton .setEnabled (false );
338- }
349+ confirmButton .setEnabled (getDateSelector ().isSelectionComplete ());
339350 confirmButton .setTag (CONFIRM_BUTTON_TAG );
340351 if (positiveButtonText != null ) {
341352 confirmButton .setText (positiveButtonText );
@@ -364,6 +375,35 @@ public final View onCreateView(
364375 getContext ().getResources ().getText (negativeButtonContentDescriptionResId ));
365376 }
366377 cancelButton .setOnClickListener (this ::onNegativeButtonClick );
378+
379+ clearButton = root .findViewById (R .id .clear_button );
380+ clearButton .setTag (CLEAR_BUTTON_TAG );
381+ if (isClearable ) {
382+ clearButton .setEnabled (getDateSelector ().isSelectionComplete ());
383+ clearButton .setOnClickListener (
384+ new View .OnClickListener () {
385+ @ Override
386+ public void onClick (View v ) {
387+ // Clear the selection
388+ calendar .clearSelection ();
389+
390+ // Call 'cleared' listeners, if any
391+ for (View .OnClickListener listener : onClearButtonClickListeners ) {
392+ listener .onClick (v );
393+ }
394+
395+ if (dismissOnClear ) {
396+ dismiss ();
397+ }
398+ }
399+ });
400+
401+ clearButton .setVisibility (View .VISIBLE );
402+
403+ } else {
404+ clearButton .setVisibility (View .GONE );
405+ }
406+
367407 return root ;
368408 }
369409
@@ -531,12 +571,13 @@ private void startPickerFragment() {
531571 @ Override
532572 public void onSelectionChanged (S selection ) {
533573 updateHeader (getHeaderText ());
534- confirmButton . setEnabled ( getDateSelector (). isSelectionComplete () );
574+ updateActionButtonEnabledState ( );
535575 }
536576
537577 @ Override
538578 public void onIncompleteSelectionChanged () {
539579 confirmButton .setEnabled (false );
580+ clearButton .setEnabled (false );
540581 }
541582 });
542583
@@ -562,7 +603,7 @@ private void initHeaderToggle(Context context) {
562603 headerToggleButton .setOnClickListener (
563604 v -> {
564605 // Update confirm button in case in progress selection has been reset
565- confirmButton . setEnabled ( getDateSelector (). isSelectionComplete () );
606+ updateActionButtonEnabledState ( );
566607
567608 headerToggleButton .toggle ();
568609 inputMode = (inputMode == INPUT_MODE_TEXT ) ? INPUT_MODE_CALENDAR : INPUT_MODE_TEXT ;
@@ -572,6 +613,11 @@ private void initHeaderToggle(Context context) {
572613 });
573614 }
574615
616+ private void updateActionButtonEnabledState () {
617+ confirmButton .setEnabled (getDateSelector ().isSelectionComplete ());
618+ clearButton .setEnabled (isClearable && getDateSelector ().isSelectionComplete ());
619+ }
620+
575621 private void updateToggleContentDescription (@ NonNull CheckableImageButton toggle ) {
576622 String contentDescription =
577623 inputMode == INPUT_MODE_TEXT
@@ -696,6 +742,27 @@ public void clearOnNegativeButtonClickListeners() {
696742 onNegativeButtonClickListeners .clear ();
697743 }
698744
745+ /** The supplied listener is called when the user clicks the clear button. */
746+ public boolean addOnClearButtonClickListener (
747+ @ NonNull OnClickListener onClearButtonClickListener ) {
748+ return onClearButtonClickListeners .add (onClearButtonClickListener );
749+ }
750+
751+ /**
752+ * Removes a listener previously added via {@link MaterialDatePicker#addOnClearButtonClickListener}.
753+ */
754+ public boolean removeOnClearButtonClickListener (
755+ @ NonNull OnClickListener onClearButtonClickListener ) {
756+ return onClearButtonClickListeners .remove (onClearButtonClickListener );
757+ }
758+
759+ /**
760+ * Removes all listeners added via {@link MaterialDatePicker#addOnClearButtonClickListener}.
761+ */
762+ public void clearOnClearButtonClickListeners () {
763+ onClearButtonClickListeners .clear ();
764+ }
765+
699766 /**
700767 * The supplied listener is called when the user cancels the picker via back button or a touch
701768 * outside the view. It is not called when the user clicks the cancel button. To add a listener
@@ -754,6 +821,8 @@ public static final class Builder<S> {
754821 CharSequence negativeButtonContentDescription = null ;
755822 @ Nullable S selection = null ;
756823 @ InputMode int inputMode = INPUT_MODE_CALENDAR ;
824+ boolean isClearable = false ;
825+ boolean dismissOnClear = false ;
757826
758827 private Builder (DateSelector <S > dateSelector ) {
759828 this .dateSelector = dateSelector ;
@@ -975,6 +1044,20 @@ public Builder<S> setInputMode(@InputMode int inputMode) {
9751044 return this ;
9761045 }
9771046
1047+ /** Sets the state of the clear button */
1048+ @ NonNull
1049+ public Builder <S > setClearable (boolean clearable ) {
1050+ this .isClearable = clearable ;
1051+ return this ;
1052+ }
1053+
1054+ /** Sets the state of the dialog dismiss state when clear button is clicked */
1055+ @ NonNull
1056+ public Builder <S > setDismissOnClear (boolean dismiss ) {
1057+ this .dismissOnClear = dismiss ;
1058+ return this ;
1059+ }
1060+
9781061 /** Creates a {@link MaterialDatePicker} with the provided options. */
9791062 @ NonNull
9801063 public MaterialDatePicker <S > build () {
0 commit comments