[Android] "Remove animations" setting isn't reflected when using `AccessibilityInfo`
See original GitHub issueDescription
- When removing animations through “Android settings” -> “Accessibility” -> “Remove animations”, it doesn’t seem to result in
AccessibilityInfo.isReduceMotionEnabled()
returningtrue
or 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
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:6 (2 by maintainers)
Top GitHub Comments
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 thatvalue == 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.
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!