Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save karanatwal/37ff5a85ba2cba46ef40565b45ad129d to your computer and use it in GitHub Desktop.
Save karanatwal/37ff5a85ba2cba46ef40565b45ad129d to your computer and use it in GitHub Desktop.
Use DatePicker spinners in API 24
/**
* 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, even when the theme specifies otherwise.
*
* Modified slightly from the equivalent fix for TimePicker from @jeffdgr8:
* https://gist.github.com/jeffdgr8/6bc5f990bf0c13a7334ce385d482af9f
*/
private void fixSpinner(Context context, int year, int month, int dayOfMonth) {
// The spinner vs not distinction probably started in lollipop but applying this
// for versions < nougat leads to a crash trying to get DatePickerSpinnerDelegate
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {
try {
// Get the theme's android:datePickerMode
final int MODE_SPINNER = 1;
Class<?> styleableClass = Class.forName("com.android.internal.R$styleable");
Field datePickerStyleableField = styleableClass.getField("DatePicker");
int[] datePickerStyleable = (int[]) datePickerStyleableField.get(null);
final TypedArray a = context.obtainStyledAttributes(null, datePickerStyleable,
android.R.attr.datePickerStyle, 0);
Field datePickerModeStyleableField = styleableClass.getField("DatePicker_datePickerMode");
int datePickerModeStyleable = datePickerModeStyleableField.getInt(null);
final int mode = a.getInt(datePickerModeStyleable, MODE_SPINNER);
a.recycle();
if (mode == MODE_SPINNER) {
DatePicker datePicker = (DatePicker) findField(DatePickerDialog.class,
DatePicker.class, "mDatePicker").get(this);
Class<?> delegateClass = Class.forName("android.widget.DatePicker$DatePickerDelegate");
Field delegateField = findField(DatePicker.class, delegateClass, "mDelegate");
Object delegate = delegateField.get(datePicker);
Class<?> spinnerDelegateClass = Class.forName("android.widget.DatePickerSpinnerDelegate");
// In 7.0 Nougat for some reason the datePickerMode is ignored and the
// delegate is DatePickerCalendarDelegate
if (delegate.getClass() != spinnerDelegateClass) {
delegateField.set(datePicker, null); // throw out the DatePickerCalendarDelegate!
datePicker.removeAllViews(); // remove the DatePickerCalendarDelegate views
Constructor spinnerDelegateConstructor = spinnerDelegateClass
.getDeclaredConstructor(DatePicker.class, Context.class,
AttributeSet.class, int.class, int.class);
spinnerDelegateConstructor.setAccessible(true);
// Instantiate a DatePickerSpinnerDelegate
delegate = spinnerDelegateConstructor.newInstance(datePicker, context,
null, android.R.attr.datePickerStyle, 0);
// set the DatePicker.mDelegate to the spinner delegate
delegateField.set(datePicker, delegate);
// Set up the DatePicker again, with the DatePickerSpinnerDelegate
datePicker.updateDate(year, month, dayOfMonth);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private Field findField(Class objectClass, Class fieldClass, String expectedName) {
try {
Field field = objectClass.getDeclaredField(expectedName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
Timber.i(e); // ignore
}
// search for it if it wasn't found under the expected ivar name
for (Field searchField : objectClass.getDeclaredFields()) {
if (searchField.getType() == fieldClass) {
searchField.setAccessible(true);
return searchField;
}
}
return null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment