react-native icon indicating copy to clipboard operation
react-native copied to clipboard

[Android] "Remove animations" setting isn't reflected when using `AccessibilityInfo`

Open te-online opened this issue 4 years ago • 6 comments

Description

  • When removing animations through "Android settings" -> "Accessibility" -> "Remove animations", it doesn't seem to result in AccessibilityInfo.isReduceMotionEnabled() returning true 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

te-online avatar Mar 23 '21 16:03 te-online

I have the same problem. I'm using RN 0.61.5.

janlonden avatar Apr 26 '21 12:04 janlonden

Running into the same issue on Android using RN 0.63.2.

joshbuchea avatar May 05 '21 22:05 joshbuchea

I have this issue using RN 0.66.3.

Lily418 avatar Nov 19 '21 17:11 Lily418

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.

martsie avatar Jan 31 '22 02:01 martsie

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!

joshbuchea avatar Mar 10 '22 19:03 joshbuchea

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.

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.

horsemankukka avatar May 01 '22 23:05 horsemankukka

I still experience this bug with React native 0.71

RikSchefferOberon avatar Mar 15 '23 13:03 RikSchefferOberon