Skip to content

Commit bb060d6

Browse files
luancurtifacebook-github-bot
authored andcommitted
Fix DatePickerAndroid with mode spinner on Android Nougat(7.0) (#24739)
Summary: When mode is spinner in Android Nougat the DatePicker shows calendar I got it from https://gist.github.com/jeffdgr8/6bc5f990bf0c13a7334ce385d482af9f and did some adjustments in order to work with `DatePicker`. Workaround for this bug: https://code.google.com/p/android/issues/detail?id=222208. In Android 7.0 Nougat, spinner mode for the DatePicker in DatePickerDialog is incorrectly displayed as calendar. ## Changelog [Android][Fixed] Fix date picker with mode spinner on Android Nougat (7.0) Pull Request resolved: #24739 Differential Revision: D15391354 Pulled By: cpojer fbshipit-source-id: 09f45367250aa14857a9c68846c7f2ce7c49ee3b
1 parent cf9babf commit bb060d6

File tree

1 file changed

+70
-3
lines changed

1 file changed

+70
-3
lines changed

ReactAndroid/src/main/java/com/facebook/react/modules/datepicker/DismissableDatePickerDialog.java

+70-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77

88
package com.facebook.react.modules.datepicker;
99

10-
import android.app.DatePickerDialog;
11-
import javax.annotation.Nullable;
12-
1310
import android.app.DatePickerDialog;
1411
import android.content.Context;
1512
import android.os.Build;
13+
import android.content.res.TypedArray;
14+
import android.util.AttributeSet;
15+
import android.widget.DatePicker;
16+
17+
import javax.annotation.Nullable;
18+
import java.lang.reflect.Field;
19+
import java.lang.reflect.Method;
1620

1721
/**
1822
* <p>
@@ -33,6 +37,7 @@ public DismissableDatePickerDialog(
3337
int monthOfYear,
3438
int dayOfMonth) {
3539
super(context, callback, year, monthOfYear, dayOfMonth);
40+
fixSpinner(context, year, monthOfYear, dayOfMonth);
3641
}
3742

3843
public DismissableDatePickerDialog(
@@ -43,6 +48,7 @@ public DismissableDatePickerDialog(
4348
int monthOfYear,
4449
int dayOfMonth) {
4550
super(context, theme, callback, year, monthOfYear, dayOfMonth);
51+
fixSpinner(context, year, monthOfYear, dayOfMonth);
4652
}
4753

4854
@Override
@@ -53,4 +59,65 @@ protected void onStop() {
5359
super.onStop();
5460
}
5561
}
62+
63+
private void fixSpinner(Context context, int year, int month, int dayOfMonth) {
64+
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {
65+
try {
66+
// Get the theme's android:datePickerMode
67+
final int MODE_SPINNER = 2;
68+
Class<?> styleableClass = Class.forName("com.android.internal.R$styleable");
69+
Field datePickerStyleableField = styleableClass.getField("DatePicker");
70+
int[] datePickerStyleable = (int[]) datePickerStyleableField.get(null);
71+
72+
final TypedArray a = context.obtainStyledAttributes(null, datePickerStyleable, android.R.attr.datePickerStyle, 0);
73+
Field datePickerModeStyleableField = styleableClass.getField("DatePicker_datePickerMode");
74+
int datePickerModeStyleable = datePickerModeStyleableField.getInt(null);
75+
final int mode = a.getInt(datePickerModeStyleable, MODE_SPINNER);
76+
a.recycle();
77+
78+
if (mode == MODE_SPINNER) {
79+
DatePicker datePicker = (DatePicker) findField(DatePickerDialog.class, DatePicker.class, "mDatePicker").get(this);
80+
Class<?> delegateClass = Class.forName("android.widget.DatePickerSpinnerDelegate");
81+
Field delegateField = findField(DatePicker.class, delegateClass, "mDelegate");
82+
Object delegate = delegateField.get(datePicker);
83+
Class<?> spinnerDelegateClass;
84+
spinnerDelegateClass = Class.forName("android.widget.DatePickerSpinnerDelegate");
85+
86+
// In 7.0 Nougat for some reason the datePickerMode is ignored and the delegate is DatePickerClockDelegate
87+
if (delegate.getClass() != spinnerDelegateClass) {
88+
delegateField.set(datePicker, null); // throw out the DatePickerClockDelegate!
89+
datePicker.removeAllViews(); // remove the DatePickerClockDelegate views
90+
Method createSpinnerUIDelegate = DatePicker.class.getDeclaredMethod("createSpinnerUIDelegate", Context.class, AttributeSet.class, int.class, int.class);
91+
createSpinnerUIDelegate.setAccessible(true);
92+
93+
// Instantiate a DatePickerSpinnerDelegate throughout createSpinnerUIDelegate method
94+
delegate = createSpinnerUIDelegate.invoke(datePicker, context, null, android.R.attr.datePickerStyle, 0);
95+
delegateField.set(datePicker, delegate); // set the DatePicker.mDelegate to the spinner delegate
96+
datePicker.setCalendarViewShown(false);
97+
// Initialize the date for the DatePicker delegate again
98+
datePicker.init(year, month, dayOfMonth, this);
99+
}
100+
}
101+
} catch (Exception e) {
102+
throw new RuntimeException(e);
103+
}
104+
}
105+
}
106+
107+
private static Field findField(Class objectClass, Class fieldClass, String expectedName) {
108+
try {
109+
Field field = objectClass.getDeclaredField(expectedName);
110+
field.setAccessible(true);
111+
return field;
112+
} catch (NoSuchFieldException e) {
113+
} // ignore
114+
// search for it if it wasn't found under the expected ivar name
115+
for (Field searchField : objectClass.getDeclaredFields()) {
116+
if (searchField.getType() == fieldClass) {
117+
searchField.setAccessible(true);
118+
return searchField;
119+
}
120+
}
121+
return null;
122+
}
56123
}

0 commit comments

Comments
 (0)