Datetimepicker: TimePicker display="spinner" no longer working in RN 64 (Android)

Created on 16 Mar 2021  路  15Comments  路  Source: react-native-datetimepicker/datetimepicker

Bug report

Hello!

Summary

Setting display to spinner on the time picker in Android renders a clock in React Native 64. It works as expected in React Native 63. I have only tested on Android.

Reproducible sample code

See this repo for a sample. Branch main uses RN 63 and works as expected. Branch rn-64 is exactly the same code, simply upgraded by running npx react-native upgrade

https://github.com/klandell/time-spinner-bug

Steps to reproduce

Open the app on an Android device. Click show time spinner. On react native 63, it shows as a spinner. On react native 64 it shows as a clock.

Environment info

Vanilla react native app, not Expo.

System:
    OS: macOS 11.1
    CPU: (8) x64 Intel(R) Core(TM) i5-8257U CPU @ 1.40GHz
    Memory: 564.87 MB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 15.0.1 - /var/folders/bq/rv3dbnrd4gnfymg9n66rgdc80000gn/T/yarn--1615903404422-0.9955909284122346/node
    Yarn: 1.22.10 - /var/folders/bq/rv3dbnrd4gnfymg9n66rgdc80000gn/T/yarn--1615903404422-0.9955909284122346/yarn
    npm: 7.0.3 - /usr/local/bin/npm
    Watchman: Not Found
  Managers:
    CocoaPods: 1.10.1 - /usr/local/bin/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 14.4, DriverKit 20.2, macOS 11.1, tvOS 14.3, watchOS 7.2
    Android SDK:
      Android NDK: 21.0.6113669
  IDEs:
    Android Studio: 4.1 AI-201.8743.12.41.6953283
    Xcode: 12.4/12D4e - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.9 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 17.0.1 => 17.0.1 
    react-native: 0.64.0 => 0.64.0 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

datetimepicker version: 3.2
iOS / Android version: Samsung Galaxy S20e, Android 10.

help wanted released

All 15 comments

yeah this is happening in react native 64 only on Android

I am getting SpinnerTimePickerDialog in static TimePickerDialog getDialog()... and display = SPINNER.
image

If I change:

  private void fixSpinner(Context context, int hourOfDay, int minute, boolean is24HourView, RNTimePickerDisplay display) {
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N && display == RNTimePickerDisplay.SPINNER) {

to

  private void fixSpinner(Context context, int hourOfDay, int minute, boolean is24HourView, RNTimePickerDisplay display) {
    if (display == RNTimePickerDisplay.SPINNER) {

It works.

My recommendation is to do:

  private void fixSpinner(Context context, int hourOfDay, int minute, boolean is24HourView, RNTimePickerDisplay display) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && display == RNTimePickerDisplay.SPINNER) {

I'll set up a PR

:tada: This issue has been resolved in version 3.4.1 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

@klandell please let us know if the issue is resolved in latest release and please test it on different android versions (not just the appearance but the actual function), thank you!

@vonovak @billnbell Thank you both for looking into this. I updated to the latest package in the rn-64 branch of the demo repo above to 3.4.1. Unfortunately, the app is now crashing with the following exception:

--------- beginning of crash
03-23 09:13:21.320 19282 19282 E AndroidRuntime: FATAL EXCEPTION: main
03-23 09:13:21.320 19282 19282 E AndroidRuntime: Process: com.awesomeproject, PID: 19282
03-23 09:13:21.320 19282 19282 E AndroidRuntime: java.lang.RuntimeException: java.lang.NoSuchFieldException: TimePicker
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at com.reactcommunity.rndatetimepicker.RNDismissableTimePickerDialog.fixSpinner(RNDismissableTimePickerDialog.java:104)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at com.reactcommunity.rndatetimepicker.RNDismissableTimePickerDialog.<init>(RNDismissableTimePickerDialog.java:63)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at com.reactcommunity.rndatetimepicker.RNTimePickerDialogFragment.getDialog(RNTimePickerDialogFragment.java:83)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at com.reactcommunity.rndatetimepicker.RNTimePickerDialogFragment.createDialog(RNTimePickerDialogFragment.java:112)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at com.reactcommunity.rndatetimepicker.RNTimePickerDialogFragment.onCreateDialog(RNTimePickerDialogFragment.java:41)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.DialogFragment.onGetLayoutInflater(DialogFragment.java:380)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.Fragment.performGetLayoutInflater(Fragment.java:1412)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at android.os.Handler.handleCallback(Handler.java:883)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:100)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:237)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:8167)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)
03-23 09:13:21.320 19282 19282 E AndroidRuntime: Caused by: java.lang.NoSuchFieldException: TimePicker
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at java.lang.Class.getField(Class.java:1604)
03-23 09:13:21.320 19282 19282 E AndroidRuntime:        at com.reactcommunity.rndatetimepicker.RNDismissableTimePickerDialog.fixSpinner(RNDismissableTimePickerDialog.java:77)

Hmm, sorry about that. If you or someone else could look into this, that would be great, my time is limited now. In the mean time, im going to revert the fix because it was probably better before. Thanks.

Thanks. I'll try to take a look at this next week as my time is limited right now as well. Unless someone else gets to it first!

I was able to do a little bit of digging into this. From what I can tell so far the issue seems to be stemming from this code

activityContext.getResources().getIdentifier(
        resourceName,
        "style",
        activityContext.getPackageName()
),

in the getDialog method of RNTimePickerDialogFragment which creates the RNDismissableTimePickerDialog.

This value is passed as the theme variable to the RNDismissableTimePickerDialog constructor here:

  public RNDismissableTimePickerDialog(
    Context context,
    int theme,
    @Nullable TimePickerDialog.OnTimeSetListener callback,
    int hourOfDay,
    int minute,
    int minuteInterval,
    boolean is24HourView,
    RNTimePickerDisplay display
  ) {
    super(context, theme, callback, hourOfDay, minute, minuteInterval, is24HourView, display);
    fixSpinner(context, hourOfDay, minute, is24HourView, display);
  }

In React native 63, int theme = 2131558599 during debugging. In React Native 64 int theme = 0.

I'm not sure of the reason yet.

I just inspected the react-native-0.63.4.aar file packaged with react native.

In react-native-0.63.4.aar -> res -> values -> values.xml I see the following.

<style name="SpinnerTimePickerDialog" parent="Theme.AppCompat.Light.Dialog" ns1:targetApi="lollipop">
  <item name="android:timePickerStyle">@style/SpinnerTimePickerStyle</item>
</style>
<style name="SpinnerTimePickerStyle" parent="android:Widget.Material.Light.TimePicker" ns1:targetApi="lollipop">
  <item name="android:timePickerMode">spinner</item>
</style>

These values do not exist in react-native-0.64.0.aar -> res -> values -> values.xml

This is the commit that removed the styles this library relied on:
https://github.com/facebook/react-native/commit/c8fed9e3858876c4e9cfe452a9f51c9241f82aad

Simply adding

<style name="SpinnerTimePickerDialog" parent="Theme.AppCompat.Light.Dialog">
    <item name="android:timePickerStyle">@style/SpinnerTimePickerStyle</item>
</style>
<style name="SpinnerTimePickerStyle" parent="android:Widget.Material.Light.TimePicker">
    <item name="android:timePickerMode">spinner</item>
</style>

to my styles.xml file fixed the issue.

Is there a recommended way of integrating this change into the this library? I removed ns1:targetApi="lollipop" from my styles.xml file because the parser was complaining about it. I assume that field is needed in the library though?

If someone with more familiarity with Android could weigh in it would be hugely helpful.

ah, so it's this: https://github.com/react-native-datetimepicker/datetimepicker/issues/258 becoming a real issue 6 months later 馃槅

Nice, thanks a lot for the investigation!

I removed ns1:targetApi="lollipop" from my styles.xml file because the parser was complaining about it

I'm assuming we should keep it. There probably is a commit in RN's history that explains what those things do and why they are there.

So if you add those things to the styles.xml file in this package that should probably fix it. The file does not exists but I'm guessing that android studio will have a way to create it or you can do it manually. Unfortunately, I'm not an android guru either but I think this should work.

Thanks again! :+1:

:tada: This issue has been resolved in version 3.4.3 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

Was this page helpful?
0 / 5 - 0 ratings