Skip to content

Commit 6281f93

Browse files
authored
feat(android): change dialog buttons text color (#689)
* feat: android dialog buttons text color * fix: flow * reduce some dupes
1 parent 3170571 commit 6281f93

File tree

14 files changed

+216
-102
lines changed

14 files changed

+216
-102
lines changed

README.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,23 +434,48 @@ Allows changing of the time picker to a 24-hour format. By default, this value i
434434
<RNDateTimePicker is24Hour={true} />
435435
```
436436

437-
#### `positiveButtonLabel` (`optional`, `Android only`)
437+
#### `positiveButton` (`optional`, `Android only`)
438+
439+
Set the positive button label and text color.
440+
441+
```js
442+
<RNDateTimePicker positiveButton={{label: 'OK', textColor: 'green'}} />
443+
```
444+
445+
#### `neutralButton` (`optional`, `Android only`)
446+
447+
Allows displaying neutral button on picker dialog.
448+
Pressing button can be observed in onChange handler as `event.type === 'neutralButtonPressed'`
449+
450+
```js
451+
<RNDateTimePicker neutralButton={{label: 'Clear', textColor: 'red'}} />
452+
```
453+
454+
#### `negativeButton` (`optional`, `Android only`)
455+
456+
Set the negative button label and text color.
457+
458+
```js
459+
<RNDateTimePicker negativeButton={{label: 'Cancel', textColor: 'red'}} />
460+
```
461+
462+
#### `positiveButtonLabel` (`optional`, `Android only`, deprecated)
438463

439464
Changes the label of the positive button.
440465

441466
```js
442467
<RNDateTimePicker positiveButtonLabel="OK!" />
443468
```
444469

445-
#### `negativeButtonLabel` (`optional`, `Android only`)
470+
#### `negativeButtonLabel` (`optional`, `Android only`, deprecated)
446471

447472
Changes the label of the negative button.
448473

449474
```js
450475
<RNDateTimePicker positiveButtonLabel="Negative" />
451476
```
452477

453-
#### `neutralButtonLabel` (`optional`, `Android only`)
478+
#### `neutralButtonLabel` (`optional`, `Android only`, deprecated)
454479

455480
Allows displaying neutral button on picker dialog.
456481
Pressing button can be observed in onChange handler as `event.type === 'neutralButtonPressed'`

android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.reactcommunity.rndatetimepicker;
22

33
import android.app.AlertDialog;
4-
import android.app.DatePickerDialog;
54
import android.content.Context;
65
import android.content.DialogInterface;
76
import android.content.res.Resources;
7+
import android.graphics.Color;
88
import android.os.Bundle;
99
import android.util.TypedValue;
1010
import android.widget.Button;
@@ -23,6 +23,12 @@
2323

2424
public class Common {
2525

26+
public static final String POSITIVE = "positive";
27+
public static final String NEUTRAL = "neutral";
28+
public static final String NEGATIVE = "negative";
29+
public static final String LABEL = "label";
30+
public static final String TEXT_COLOR = "textColor";
31+
2632
public static void dismissDialog(FragmentActivity activity, String fragmentTag, Promise promise) {
2733
if (activity == null) {
2834
promise.reject(
@@ -56,29 +62,51 @@ public static int getDefaultDialogButtonTextColor(@NonNull Context activity) {
5662
}
5763

5864
@NonNull
59-
public static DialogInterface.OnShowListener setButtonTextColor(@NonNull Context activityContext, final AlertDialog dialog) {
65+
public static DialogInterface.OnShowListener setButtonTextColor(@NonNull Context activityContext, final AlertDialog dialog, final Bundle args, final boolean needsColorOverride) {
6066
return new DialogInterface.OnShowListener() {
6167
@Override
6268
public void onShow(DialogInterface dialogInterface) {
69+
// change text color only if custom color is set or if spinner mode is set
70+
// because spinner suffers from https://github.com/react-native-datetimepicker/datetimepicker/issues/543
71+
6372
Button positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
6473
Button negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
6574
Button neutralButton = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
6675

6776
int textColorPrimary = getDefaultDialogButtonTextColor(activityContext);
68-
69-
if (positiveButton != null) {
70-
positiveButton.setTextColor(textColorPrimary);
71-
}
72-
if (negativeButton != null) {
73-
negativeButton.setTextColor(textColorPrimary);
74-
}
75-
if (neutralButton != null) {
76-
neutralButton.setTextColor(textColorPrimary);
77-
}
77+
setTextColor(positiveButton, POSITIVE, args, needsColorOverride, textColorPrimary);
78+
setTextColor(negativeButton, NEGATIVE, args, needsColorOverride, textColorPrimary);
79+
setTextColor(neutralButton, NEUTRAL, args, needsColorOverride, textColorPrimary);
7880
}
7981
};
8082
}
8183

84+
private static void setTextColor(Button button, String buttonKey, final Bundle args, final boolean needsColorOverride, int textColorPrimary) {
85+
if (button == null) return;
86+
87+
Integer color = getButtonColor(args, buttonKey);
88+
if (needsColorOverride || color != null) {
89+
button.setTextColor(color != null ? color : textColorPrimary);
90+
}
91+
}
92+
93+
private static Integer getButtonColor(final Bundle args, String buttonKey) {
94+
Bundle buttons = args.getBundle(RNConstants.ARG_DIALOG_BUTTONS);
95+
if (buttons == null) {
96+
return null;
97+
}
98+
Bundle buttonParams = buttons.getBundle(buttonKey);
99+
if (buttonParams == null) {
100+
return null;
101+
}
102+
// yes, this cast is safe. the color is passed as int from JS (RN.processColor)
103+
int color = (int) buttonParams.getDouble(TEXT_COLOR, Color.TRANSPARENT);
104+
if (color == Color.TRANSPARENT) {
105+
return null;
106+
}
107+
return color;
108+
}
109+
82110
public static RNTimePickerDisplay getDisplayTime(Bundle args) {
83111
RNTimePickerDisplay display = RNTimePickerDisplay.DEFAULT;
84112
if (args != null && args.getString(RNConstants.ARG_DISPLAY, null) != null) {
@@ -94,4 +122,21 @@ public static RNDatePickerDisplay getDisplayDate(Bundle args) {
94122
}
95123
return display;
96124
}
125+
126+
public static void setButtonTitles(@NonNull Bundle args, AlertDialog dialog, DialogInterface.OnClickListener onNeutralButtonActionListener) {
127+
Bundle buttons = args.getBundle(RNConstants.ARG_DIALOG_BUTTONS);
128+
if (buttons == null) {
129+
return;
130+
}
131+
setButtonLabel(buttons.getBundle(NEUTRAL), dialog, AlertDialog.BUTTON_NEUTRAL, onNeutralButtonActionListener);
132+
setButtonLabel(buttons.getBundle(POSITIVE), dialog, AlertDialog.BUTTON_POSITIVE, (DialogInterface.OnClickListener) dialog);
133+
setButtonLabel(buttons.getBundle(NEGATIVE), dialog, AlertDialog.BUTTON_NEGATIVE, (DialogInterface.OnClickListener) dialog);
134+
}
135+
136+
private static void setButtonLabel(Bundle buttonConfig, AlertDialog dialog, int whichButton, DialogInterface.OnClickListener listener) {
137+
if (buttonConfig == null || buttonConfig.getString(LABEL) == null) {
138+
return;
139+
}
140+
dialog.setButton(whichButton, buttonConfig.getString(LABEL), listener);
141+
}
97142
}

android/src/main/java/com/reactcommunity/rndatetimepicker/RNConstants.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.reactcommunity.rndatetimepicker;
22

3+
import android.app.TimePickerDialog;
4+
35
public final class RNConstants {
46
public static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY";
57
public static final String ARG_VALUE = "value";
@@ -8,17 +10,15 @@ public final class RNConstants {
810
public static final String ARG_INTERVAL = "minuteInterval";
911
public static final String ARG_IS24HOUR = "is24Hour";
1012
public static final String ARG_DISPLAY = "display";
11-
public static final String ARG_NEUTRAL_BUTTON_LABEL = "neutralButtonLabel";
12-
public static final String ARG_POSITIVE_BUTTON_LABEL = "positiveButtonLabel";
13-
public static final String ARG_NEGATIVE_BUTTON_LABEL = "negativeButtonLabel";
13+
public static final String ARG_DIALOG_BUTTONS = "dialogButtons";
1414
public static final String ARG_TZOFFSET_MINS = "timeZoneOffsetInMinutes";
1515
public static final String ACTION_DATE_SET = "dateSetAction";
1616
public static final String ACTION_TIME_SET = "timeSetAction";
1717
public static final String ACTION_DISMISSED = "dismissedAction";
1818
public static final String ACTION_NEUTRAL_BUTTON = "neutralButtonAction";
1919

2020
/**
21-
* Minimum date supported by {@link DatePicker}, 01 Jan 1900
21+
* Minimum date supported by {@link TimePickerDialog}, 01 Jan 1900
2222
*/
2323
public static final long DEFAULT_MIN_DATE = -2208988800001l;
2424

android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import static com.reactcommunity.rndatetimepicker.Common.getDisplayDate;
1111
import static com.reactcommunity.rndatetimepicker.Common.setButtonTextColor;
12+
import static com.reactcommunity.rndatetimepicker.Common.setButtonTitles;
1213

1314
import android.annotation.SuppressLint;
1415
import android.app.DatePickerDialog;
@@ -38,13 +39,13 @@ public class RNDatePickerDialogFragment extends DialogFragment {
3839
@Nullable
3940
private OnDismissListener mOnDismissListener;
4041
@Nullable
41-
private static OnClickListener mOnNeutralButtonActionListener;
42+
private OnClickListener mOnNeutralButtonActionListener;
4243

4344
@NonNull
4445
@Override
4546
public Dialog onCreateDialog(Bundle savedInstanceState) {
4647
Bundle args = getArguments();
47-
instance = createDialog(args, getActivity(), mOnDateSetListener);
48+
instance = createDialog(args);
4849
return instance;
4950
}
5051

@@ -91,28 +92,18 @@ DatePickerDialog getDialog(
9192
);
9293
}
9394

94-
static DatePickerDialog createDialog(
95-
Bundle args,
96-
Context activityContext,
97-
@Nullable OnDateSetListener onDateSetListener) {
98-
95+
private DatePickerDialog createDialog(Bundle args) {
96+
Context activityContext = getActivity();
9997
final Calendar c = Calendar.getInstance();
10098

101-
DatePickerDialog dialog = getDialog(args, activityContext, onDateSetListener);
99+
DatePickerDialog dialog = getDialog(args, activityContext, mOnDateSetListener);
102100

103101
if (args != null) {
104-
if (args.containsKey(RNConstants.ARG_NEUTRAL_BUTTON_LABEL)) {
105-
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, args.getString(RNConstants.ARG_NEUTRAL_BUTTON_LABEL), mOnNeutralButtonActionListener);
106-
}
107-
if (args.containsKey(RNConstants.ARG_POSITIVE_BUTTON_LABEL)) {
108-
dialog.setButton(DialogInterface.BUTTON_POSITIVE, args.getString(RNConstants.ARG_POSITIVE_BUTTON_LABEL), dialog);
109-
}
110-
if (args.containsKey(RNConstants.ARG_NEGATIVE_BUTTON_LABEL)) {
111-
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, args.getString(RNConstants.ARG_NEGATIVE_BUTTON_LABEL), dialog);
112-
}
113-
RNDatePickerDisplay display = getDisplayDate(args);
114-
if (display == RNDatePickerDisplay.SPINNER) {
115-
dialog.setOnShowListener(setButtonTextColor(activityContext, dialog));
102+
setButtonTitles(args, dialog, mOnNeutralButtonActionListener);
103+
if (activityContext != null) {
104+
RNDatePickerDisplay display = getDisplayDate(args);
105+
boolean needsColorOverride = display == RNDatePickerDisplay.SPINNER;
106+
dialog.setOnShowListener(setButtonTextColor(activityContext, dialog, args, needsColorOverride));
116107
}
117108
}
118109

android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogModule.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,8 @@ private Bundle createFragmentArguments(ReadableMap options) {
168168
if (options.hasKey(RNConstants.ARG_DISPLAY) && !options.isNull(RNConstants.ARG_DISPLAY)) {
169169
args.putString(RNConstants.ARG_DISPLAY, options.getString(RNConstants.ARG_DISPLAY));
170170
}
171-
if (options.hasKey(RNConstants.ARG_NEUTRAL_BUTTON_LABEL) && !options.isNull(RNConstants.ARG_NEUTRAL_BUTTON_LABEL)) {
172-
args.putString(RNConstants.ARG_NEUTRAL_BUTTON_LABEL, options.getString(RNConstants.ARG_NEUTRAL_BUTTON_LABEL));
173-
}
174-
if (options.hasKey(RNConstants.ARG_POSITIVE_BUTTON_LABEL) && !options.isNull(RNConstants.ARG_POSITIVE_BUTTON_LABEL)) {
175-
args.putString(RNConstants.ARG_POSITIVE_BUTTON_LABEL, options.getString(RNConstants.ARG_POSITIVE_BUTTON_LABEL));
176-
}
177-
if (options.hasKey(RNConstants.ARG_NEGATIVE_BUTTON_LABEL) && !options.isNull(RNConstants.ARG_NEGATIVE_BUTTON_LABEL)) {
178-
args.putString(RNConstants.ARG_NEGATIVE_BUTTON_LABEL, options.getString(RNConstants.ARG_NEGATIVE_BUTTON_LABEL));
171+
if (options.hasKey(RNConstants.ARG_DIALOG_BUTTONS) && !options.isNull(RNConstants.ARG_DIALOG_BUTTONS)) {
172+
args.putBundle(RNConstants.ARG_DIALOG_BUTTONS, Arguments.toBundle(options.getMap(RNConstants.ARG_DIALOG_BUTTONS)));
179173
}
180174
if (options.hasKey(RNConstants.ARG_TZOFFSET_MINS) && !options.isNull(RNConstants.ARG_TZOFFSET_MINS)) {
181175
args.putLong(RNConstants.ARG_TZOFFSET_MINS, (long) options.getDouble(RNConstants.ARG_TZOFFSET_MINS));

android/src/main/java/com/reactcommunity/rndatetimepicker/RNTimePickerDialogFragment.java

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import static com.reactcommunity.rndatetimepicker.Common.getDisplayTime;
1212
import static com.reactcommunity.rndatetimepicker.Common.setButtonTextColor;
13+
import static com.reactcommunity.rndatetimepicker.Common.setButtonTitles;
1314

1415
import android.app.Dialog;
1516
import android.app.TimePickerDialog;
@@ -34,13 +35,13 @@ public class RNTimePickerDialogFragment extends DialogFragment {
3435
@Nullable
3536
private OnDismissListener mOnDismissListener;
3637
@Nullable
37-
private static OnClickListener mOnNeutralButtonActionListener;
38+
private OnClickListener mOnNeutralButtonActionListener;
3839

3940
@NonNull
4041
@Override
4142
public Dialog onCreateDialog(Bundle savedInstanceState) {
4243
final Bundle args = getArguments();
43-
instance = createDialog(args, getActivity(), mOnTimeSetListener);
44+
instance = createDialog(args);
4445
return instance;
4546
}
4647

@@ -92,25 +93,16 @@ static TimePickerDialog getDialog(
9293
);
9394
}
9495

95-
static TimePickerDialog createDialog(
96-
Bundle args, Context activityContext,
97-
@Nullable OnTimeSetListener onTimeSetListener) {
98-
99-
TimePickerDialog dialog = getDialog(args, activityContext, onTimeSetListener);
96+
private TimePickerDialog createDialog(Bundle args) {
97+
Context activityContext = getActivity();
98+
TimePickerDialog dialog = getDialog(args, activityContext, mOnTimeSetListener);
10099

101100
if (args != null) {
102-
if (args.containsKey(RNConstants.ARG_NEUTRAL_BUTTON_LABEL)) {
103-
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, args.getString(RNConstants.ARG_NEUTRAL_BUTTON_LABEL), mOnNeutralButtonActionListener);
104-
}
105-
if (args.containsKey(RNConstants.ARG_POSITIVE_BUTTON_LABEL)) {
106-
dialog.setButton(DialogInterface.BUTTON_POSITIVE, args.getString(RNConstants.ARG_POSITIVE_BUTTON_LABEL), dialog);
107-
}
108-
if (args.containsKey(RNConstants.ARG_NEGATIVE_BUTTON_LABEL)) {
109-
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, args.getString(RNConstants.ARG_NEGATIVE_BUTTON_LABEL), dialog);
110-
}
111-
RNTimePickerDisplay display = getDisplayTime(args);
112-
if (display == RNTimePickerDisplay.SPINNER) {
113-
dialog.setOnShowListener(setButtonTextColor(activityContext, dialog));
101+
setButtonTitles(args, dialog, mOnNeutralButtonActionListener);
102+
if (activityContext != null) {
103+
RNTimePickerDisplay display = getDisplayTime(args);
104+
boolean needsColorOverride = display == RNTimePickerDisplay.SPINNER;
105+
dialog.setOnShowListener(setButtonTextColor(activityContext, dialog, args, needsColorOverride));
114106
}
115107
}
116108
return dialog;

android/src/main/java/com/reactcommunity/rndatetimepicker/RNTimePickerDialogModule.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,8 @@ private Bundle createFragmentArguments(ReadableMap options) {
144144
if (options.hasKey(RNConstants.ARG_DISPLAY) && !options.isNull(RNConstants.ARG_DISPLAY)) {
145145
args.putString(RNConstants.ARG_DISPLAY, options.getString(RNConstants.ARG_DISPLAY));
146146
}
147-
if (options.hasKey(RNConstants.ARG_NEUTRAL_BUTTON_LABEL) && !options.isNull(RNConstants.ARG_NEUTRAL_BUTTON_LABEL)) {
148-
args.putString(RNConstants.ARG_NEUTRAL_BUTTON_LABEL, options.getString(RNConstants.ARG_NEUTRAL_BUTTON_LABEL));
149-
}
150-
if (options.hasKey(RNConstants.ARG_POSITIVE_BUTTON_LABEL) && !options.isNull(RNConstants.ARG_POSITIVE_BUTTON_LABEL)) {
151-
args.putString(RNConstants.ARG_POSITIVE_BUTTON_LABEL, options.getString(RNConstants.ARG_POSITIVE_BUTTON_LABEL));
152-
}
153-
if (options.hasKey(RNConstants.ARG_NEGATIVE_BUTTON_LABEL) && !options.isNull(RNConstants.ARG_NEGATIVE_BUTTON_LABEL)) {
154-
args.putString(RNConstants.ARG_NEGATIVE_BUTTON_LABEL, options.getString(RNConstants.ARG_NEGATIVE_BUTTON_LABEL));
147+
if (options.hasKey(RNConstants.ARG_DIALOG_BUTTONS) && !options.isNull(RNConstants.ARG_DIALOG_BUTTONS)) {
148+
args.putBundle(RNConstants.ARG_DIALOG_BUTTONS, Arguments.toBundle(options.getMap(RNConstants.ARG_DIALOG_BUTTONS)));
155149
}
156150
if (options.hasKey(RNConstants.ARG_INTERVAL) && !options.isNull(RNConstants.ARG_INTERVAL)) {
157151
args.putInt(RNConstants.ARG_INTERVAL, options.getInt(RNConstants.ARG_INTERVAL));

example/App.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ export const App = () => {
9696
};
9797

9898
const onChange = (event, selectedDate) => {
99-
const currentDate = selectedDate || date;
10099
if (Platform.OS === 'android') {
101100
setShow(false);
102101
}
@@ -117,7 +116,7 @@ export const App = () => {
117116
if (event.type === 'neutralButtonPressed') {
118117
setDate(new Date(0));
119118
} else {
120-
setDate(currentDate);
119+
setDate(selectedDate);
121120
}
122121
};
123122

@@ -353,7 +352,8 @@ export const App = () => {
353352
onChange={onChange}
354353
textColor={textColor || undefined}
355354
accentColor={accentColor || undefined}
356-
neutralButtonLabel={neutralButtonLabel}
355+
neutralButton={{label: neutralButtonLabel}}
356+
negativeButton={{label: 'Cancel', textColor: 'red'}}
357357
disabled={disabled}
358358
/>
359359
)}

example/android/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@ buildscript {
1818
}
1919
}
2020

21+
def REACT_NATIVE_VERSION = new File(['node', '--print',"JSON.parse(require('fs').readFileSync(require.resolve('react-native/package.json'), 'utf-8')).version"].execute(null, rootDir).text.trim())
22+
2123
allprojects {
24+
configurations.all {
25+
resolutionStrategy {
26+
force "com.facebook.react:react-native:" + REACT_NATIVE_VERSION
27+
}
28+
}
2229
repositories {
2330
maven {
2431
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm

0 commit comments

Comments
 (0)