react-native
react-native copied to clipboard
[Android] "Remove animations" setting isn't reflected when using `AccessibilityInfo`
Description
- When removing animations through "Android settings" -> "Accessibility" -> "Remove animations", it doesn't seem to result in
AccessibilityInfo.isReduceMotionEnabled()returningtrueor the change event to fire. - When going through "Android settings" -> "Developer settings" -> "Animator duration scale" -> "Off", it works as expected.
Note that setting "Remove animations" will also set "Animator duration scale" to "Off", but only manually setting it to "Off" again will result in AccessibilityInfo.isReduceMotionEnabled() to return true
Thus, both settings seem to be in sync somehow, but the setting in Accessibility (available to all users, not just developers) didn't seem to have the desired effect in my tests.
I tested on Android 10 and 11.
React Native version:
0.63.2 (expo SDK 39)
Steps To Reproduce
see description
Expected Results
When setting "Remove animations" in Android's accessibility settings AccessibilityInfo.isReduceMotionEnabled() returns true
Snack, code example, screenshot, or link to a repository:
My best guess is that Settings.Global.TRANSITION_ANIMATION_SCALE is somehow not set to the expected value when using the "Remove animations" switch instead of the related developer setting. See https://github.com/facebook/react-native/blob/0.63-stable/ReactAndroid/src/main/java/com/facebook/react/modules/accessibilityinfo/AccessibilityInfoModule.java#L100
This is the hook I'm using
import { useEffect, useState } from 'react';
import {
AccessibilityChangeEventHandler,
AccessibilityInfo,
} from 'react-native';
export function useReduceMotionEnabled(): [boolean] {
const [reduceMotionEnabled, setReduceMotionEnabled] = useState(false);
useEffect(() => {
AccessibilityInfo.addEventListener(
'reduceMotionChanged',
handleReduceMotionToggled,
);
AccessibilityInfo.isReduceMotionEnabled().then((reduceMotionEnabled) => {
setReduceMotionEnabled(reduceMotionEnabled);
});
return () => {
AccessibilityInfo.removeEventListener(
'reduceMotionChanged',
handleReduceMotionToggled,
);
};
}, []);
const handleReduceMotionToggled: AccessibilityChangeEventHandler = (
state,
) => {
setReduceMotionEnabled(state);
};
return [reduceMotionEnabled];
}
This issue might be related: https://github.com/facebook/react-native/issues/30871
I have the same problem. I'm using RN 0.61.5.
Running into the same issue on Android using RN 0.63.2.
I have this issue using RN 0.66.3.
Has anybody seen a native Java implementation that checks 'Remove animations' status? Would be a fairly easy thing to bridge over but I haven't seen any examples online.
We would love to be able to respect our user's preference to reduce motion on Android. Is there anything we can do to help move this along?
I'm not familiar enough with Java to create a PR, but am happy to help otherwise. What's the process for triaging an issue?
Appreciate any help or guidance anyone is able to offer, thanks!
My best guess is that
Settings.Global.TRANSITION_ANIMATION_SCALEis somehow not set to the expected value when using the "Remove animations" switch instead of the related developer setting.
This is exactly what is happening here.
The “correct” but strictly undocumented check for the accessibility preference seems to be value.equals("0"), as that’s what Android sets the value to when the accessibility preference is set. Flutter does the equivalent check this way.
The other, probably more preferable option is to use getFloat instead and check that value == 0.0f, like Chromium does. This way both using Developer settings and Accessibility settings triggers the feature.
(In contrast, the Android API docs specify that the value is a float and that setting the value to 0.0f should always disable animations, but this weird “type overloading” carrying a single bit of information about who set the preference makes the situation very confusing, so in practice the result is different in every app.)
Disclaimer: I am not an Android coder, I discovered this weirdness today. Found this issue by chance, so decided to share my findings here in case it helps fixing this problem. I don’t consider myself experienced enough with this project altogether to make a PR.
I still experience this bug with React native 0.71