Skip to content

Commit

Permalink
Fix DatePickerAndroid with mode spinner on Android Nougat(7.0) (#24739)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
luancurti authored and facebook-github-bot committed May 17, 2019
1 parent cf9babf commit bb060d6
Showing 1 changed file with 70 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@

package com.facebook.react.modules.datepicker;

import android.app.DatePickerDialog;
import javax.annotation.Nullable;

import android.app.DatePickerDialog;
import android.content.Context;
import android.os.Build;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.DatePicker;

import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
* <p>
Expand All @@ -33,6 +37,7 @@ public DismissableDatePickerDialog(
int monthOfYear,
int dayOfMonth) {
super(context, callback, year, monthOfYear, dayOfMonth);
fixSpinner(context, year, monthOfYear, dayOfMonth);
}

public DismissableDatePickerDialog(
Expand All @@ -43,6 +48,7 @@ public DismissableDatePickerDialog(
int monthOfYear,
int dayOfMonth) {
super(context, theme, callback, year, monthOfYear, dayOfMonth);
fixSpinner(context, year, monthOfYear, dayOfMonth);
}

@Override
Expand All @@ -53,4 +59,65 @@ protected void onStop() {
super.onStop();
}
}

private void fixSpinner(Context context, int year, int month, int dayOfMonth) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N) {
try {
// Get the theme's android:datePickerMode
final int MODE_SPINNER = 2;
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.DatePickerSpinnerDelegate");
Field delegateField = findField(DatePicker.class, delegateClass, "mDelegate");
Object delegate = delegateField.get(datePicker);
Class<?> spinnerDelegateClass;
spinnerDelegateClass = Class.forName("android.widget.DatePickerSpinnerDelegate");

// In 7.0 Nougat for some reason the datePickerMode is ignored and the delegate is DatePickerClockDelegate
if (delegate.getClass() != spinnerDelegateClass) {
delegateField.set(datePicker, null); // throw out the DatePickerClockDelegate!
datePicker.removeAllViews(); // remove the DatePickerClockDelegate views
Method createSpinnerUIDelegate = DatePicker.class.getDeclaredMethod("createSpinnerUIDelegate", Context.class, AttributeSet.class, int.class, int.class);
createSpinnerUIDelegate.setAccessible(true);

// Instantiate a DatePickerSpinnerDelegate throughout createSpinnerUIDelegate method
delegate = createSpinnerUIDelegate.invoke(datePicker, context, null, android.R.attr.datePickerStyle, 0);
delegateField.set(datePicker, delegate); // set the DatePicker.mDelegate to the spinner delegate
datePicker.setCalendarViewShown(false);
// Initialize the date for the DatePicker delegate again
datePicker.init(year, month, dayOfMonth, this);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

private static Field findField(Class objectClass, Class fieldClass, String expectedName) {
try {
Field field = objectClass.getDeclaredField(expectedName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException 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;
}
}

0 comments on commit bb060d6

Please sign in to comment.