Skip to content

Commit b6ed713

Browse files
authored
fix(android): time picker spinner to consider keyboard input (#661)
* chore: remove deprecated method calls * fix: report correct time with keyboard input on spinner * fix tests
1 parent 7fc238c commit b6ed713

File tree

4 files changed

+65
-41
lines changed

4 files changed

+65
-41
lines changed

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

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private boolean isSpinner() {
7676
* @return returns 'real' minutes (0-59)
7777
*/
7878
private int getRealMinutes(int minutesOrSpinnerIndex) {
79-
if (mDisplay == RNTimePickerDisplay.SPINNER) {
79+
if (isSpinner()) {
8080
return minutesOrSpinnerIndex * mTimePickerInterval;
8181
}
8282

@@ -147,27 +147,42 @@ private void correctEnteredMinutes(final TimePicker view, final int hourOfDay, f
147147
runnable = new Runnable() {
148148
@Override
149149
public void run() {
150-
// set valid hour & minutes
151-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
152-
view.setHour(hourOfDay);
153-
view.setMinute(correctedMinutes);
154-
} else {
155-
view.setCurrentHour(hourOfDay);
156-
// we need to set minutes to 0 for this to work on older android devices
157-
view.setCurrentMinute(0);
158-
view.setCurrentMinute(correctedMinutes);
159-
}
160-
if (pickerIsInTextInputMode()) {
161-
// move caret to the end of input
162-
View maybeTextInput = view.findFocus();
163-
if (maybeTextInput instanceof EditText) {
164-
final EditText textInput = (EditText) maybeTextInput;
165-
textInput.setSelection(textInput.getText().length());
166-
} else {
167-
Log.e("RN-datetimepicker", "could not set selection on time picker, this is a known issue on some Huawei devices");
168-
}
169-
}
150+
if (pickerIsInTextInputMode()) {
151+
// only rewrite input when the value makes sense to be corrected
152+
// eg. given interval 3, when user wants to enter 53
153+
// we don't rewrite the first number "5" to 6, because it would be confusing
154+
// but the value will be corrected in onTimeChanged()
155+
// however, when they enter 10, we rewrite it to 9
156+
boolean canRewriteTextInput = correctedMinutes > 5;
157+
if (!canRewriteTextInput) {
158+
return;
159+
}
160+
fixTime();
161+
moveCursorToEnd();
162+
} else {
163+
fixTime();
164+
}
170165
}
166+
private void fixTime() {
167+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
168+
view.setHour(hourOfDay);
169+
view.setMinute(correctedMinutes);
170+
} else {
171+
view.setCurrentHour(hourOfDay);
172+
// we need to set minutes to 0 first for this to work on older android devices
173+
view.setCurrentMinute(0);
174+
view.setCurrentMinute(correctedMinutes);
175+
}
176+
}
177+
private void moveCursorToEnd() {
178+
View maybeTextInput = view.findFocus();
179+
if (maybeTextInput instanceof EditText) {
180+
final EditText textInput = (EditText) maybeTextInput;
181+
textInput.setSelection(textInput.getText().length());
182+
} else {
183+
Log.e("RN-datetimepicker", "could not set selection on time picker, this is a known issue on some Huawei devices");
184+
}
185+
}
171186
};
172187

173188
handler.postDelayed(runnable, 500);
@@ -191,15 +206,17 @@ public void onTimeChanged(final TimePicker view, final int hourOfDay, final int
191206

192207
@Override
193208
public void onClick(DialogInterface dialog, int which) {
194-
if (mTimePicker != null && which == BUTTON_POSITIVE && timePickerHasCustomMinuteInterval()) {
195-
final int hours = mTimePicker.getCurrentHour();
196-
197-
final int realMinutes = getRealMinutes();
198-
int validMinutes = isSpinner() ? realMinutes : snapRealMinutesToInterval(realMinutes);
199-
200-
if (mTimeSetListener != null) {
201-
mTimeSetListener.onTimeSet(mTimePicker, hours, validMinutes);
202-
}
209+
boolean needsCustomHandling = timePickerHasCustomMinuteInterval() || isSpinner();
210+
if (mTimePicker != null && which == BUTTON_POSITIVE && needsCustomHandling) {
211+
mTimePicker.clearFocus();
212+
final int hours = mTimePicker.getCurrentHour();
213+
int realMinutes = getRealMinutes();
214+
int reportedMinutes = timePickerHasCustomMinuteInterval()
215+
? snapRealMinutesToInterval(realMinutes)
216+
: realMinutes;
217+
if (mTimeSetListener != null) {
218+
mTimeSetListener.onTimeSet(mTimePicker, hours, reportedMinutes);
219+
}
203220
} else {
204221
super.onClick(dialog, which);
205222
}
@@ -228,15 +245,19 @@ public void updateTime(int hourOfDay, int minuteOfHour) {
228245
public void onAttachedToWindow() {
229246
super.onAttachedToWindow();
230247

248+
int timePickerId = mContext.getResources().getIdentifier("timePicker", "id", "android");
249+
mTimePicker = this.findViewById(timePickerId);
250+
231251
if (timePickerHasCustomMinuteInterval()) {
232252
setupPickerDialog();
233253
}
234254
}
235255

236256
private void setupPickerDialog() {
237-
int timePickerId = mContext.getResources().getIdentifier("timePicker", "id", "android");
238-
mTimePicker = this.findViewById(timePickerId);
239-
257+
if (mTimePicker == null) {
258+
Log.e("RN-datetimepicker", "time picker was null");
259+
return;
260+
}
240261
int realMinuteBackup = mTimePicker.getCurrentMinute();
241262

242263
if (isSpinner()) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public DatePickerDialogListener(final Promise promise) {
5454

5555
@Override
5656
public void onDateSet(DatePicker view, int year, int month, int day) {
57-
if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) {
57+
if (!mPromiseResolved && getReactApplicationContext().hasActiveReactInstance()) {
5858
WritableMap result = new WritableNativeMap();
5959
result.putString("action", RNConstants.ACTION_DATE_SET);
6060
result.putInt("year", year);
@@ -67,7 +67,7 @@ public void onDateSet(DatePicker view, int year, int month, int day) {
6767

6868
@Override
6969
public void onDismiss(DialogInterface dialog) {
70-
if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) {
70+
if (!mPromiseResolved && getReactApplicationContext().hasActiveReactInstance()) {
7171
WritableMap result = new WritableNativeMap();
7272
result.putString("action", RNConstants.ACTION_DISMISSED);
7373
mPromise.resolve(result);
@@ -77,7 +77,7 @@ public void onDismiss(DialogInterface dialog) {
7777

7878
@Override
7979
public void onClick(DialogInterface dialog, int which) {
80-
if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) {
80+
if (!mPromiseResolved && getReactApplicationContext().hasActiveReactInstance()) {
8181
WritableMap result = new WritableNativeMap();
8282
result.putString("action", RNConstants.ACTION_NEUTRAL_BUTTON);
8383
mPromise.resolve(result);

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
import android.content.DialogInterface;
1616
import android.content.DialogInterface.OnDismissListener;
1717
import android.content.DialogInterface.OnClickListener;
18-
import android.os.Build;
1918
import android.os.Bundle;
2019
import android.text.format.DateFormat;
2120

21+
import androidx.annotation.NonNull;
2222
import androidx.annotation.Nullable;
2323
import androidx.fragment.app.DialogFragment;
2424

@@ -35,6 +35,7 @@ public class RNTimePickerDialogFragment extends DialogFragment {
3535
@Nullable
3636
private static OnClickListener mOnNeutralButtonActionListener;
3737

38+
@NonNull
3839
@Override
3940
public Dialog onCreateDialog(Bundle savedInstanceState) {
4041
final Bundle args = getArguments();
@@ -116,7 +117,7 @@ static TimePickerDialog createDialog(
116117
}
117118

118119
@Override
119-
public void onDismiss(DialogInterface dialog) {
120+
public void onDismiss(@NonNull DialogInterface dialog) {
120121
super.onDismiss(dialog);
121122
if (mOnDismissListener != null) {
122123
mOnDismissListener.onDismiss(dialog);

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.os.Bundle;
2020
import android.widget.TimePicker;
2121

22+
import androidx.annotation.NonNull;
2223
import androidx.annotation.Nullable;
2324
import androidx.fragment.app.FragmentActivity;
2425
import androidx.fragment.app.FragmentManager;
@@ -39,6 +40,7 @@ public RNTimePickerDialogModule(ReactApplicationContext reactContext) {
3940
super(reactContext);
4041
}
4142

43+
@NonNull
4244
@Override
4345
public String getName() {
4446
return FRAGMENT_TAG;
@@ -54,7 +56,7 @@ public TimePickerDialogListener(Promise promise) {
5456

5557
@Override
5658
public void onTimeSet(TimePicker view, int hour, int minute) {
57-
if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) {
59+
if (!mPromiseResolved && getReactApplicationContext().hasActiveReactInstance()) {
5860
WritableMap result = new WritableNativeMap();
5961
result.putString("action", RNConstants.ACTION_TIME_SET);
6062
result.putInt("hour", hour);
@@ -66,7 +68,7 @@ public void onTimeSet(TimePicker view, int hour, int minute) {
6668

6769
@Override
6870
public void onDismiss(DialogInterface dialog) {
69-
if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) {
71+
if (!mPromiseResolved && getReactApplicationContext().hasActiveReactInstance()) {
7072
WritableMap result = new WritableNativeMap();
7173
result.putString("action", RNConstants.ACTION_DISMISSED);
7274
mPromise.resolve(result);
@@ -76,7 +78,7 @@ public void onDismiss(DialogInterface dialog) {
7678

7779
@Override
7880
public void onClick(DialogInterface dialog, int which) {
79-
if (!mPromiseResolved && getReactApplicationContext().hasActiveCatalystInstance()) {
81+
if (!mPromiseResolved && getReactApplicationContext().hasActiveReactInstance()) {
8082
WritableMap result = new WritableNativeMap();
8183
result.putString("action", RNConstants.ACTION_NEUTRAL_BUTTON);
8284
mPromise.resolve(result);

0 commit comments

Comments
 (0)