@@ -9,7 +9,13 @@
991010import android.annotation.TargetApi;
1111import android.content.Context;
12+import android.content.ContentResolver;
13+import android.database.ContentObserver;
14+import android.net.Uri;
1215import android.os.Build;
16+import android.os.Handler;
17+import android.os.Looper;
18+import android.provider.Settings;
1319import android.view.accessibility.AccessibilityManager;
14201521import com.facebook.react.bridge.Callback;
@@ -36,21 +42,42 @@ private class ReactTouchExplorationStateChangeListener
36423743@Override
3844public void onTouchExplorationStateChanged(boolean enabled) {
39-updateAndSendChangeEvent(enabled);
45+updateAndSendTouchExplorationChangeEvent(enabled);
4046 }
4147 }
424849+// Listener that is notified when the global TRANSITION_ANIMATION_SCALE.
50+private final ContentObserver animationScaleObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
51+@Override
52+public void onChange(boolean selfChange) {
53+this.onChange(selfChange, null);
54+ }
55+56+@Override
57+public void onChange(boolean selfChange, Uri uri) {
58+if (getReactApplicationContext().hasActiveCatalystInstance()) {
59+AccessibilityInfoModule.this.updateAndSendReduceMotionChangeEvent();
60+ }
61+ }
62+ };
63+4364private @Nullable AccessibilityManager mAccessibilityManager;
4465private @Nullable ReactTouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
45-private boolean mEnabled = false;
66+private final ContentResolver mContentResolver;
67+private boolean mReduceMotionEnabled = false;
68+private boolean mTouchExplorationEnabled = false;
466947-private static final String EVENT_NAME = "touchExplorationDidChange";
70+private static final String REDUCE_MOTION_EVENT_NAME = "reduceMotionDidChange";
71+private static final String TOUCH_EXPLORATION_EVENT_NAME = "touchExplorationDidChange";
48724973public AccessibilityInfoModule(ReactApplicationContext context) {
5074super(context);
5175Context appContext = context.getApplicationContext();
5276mAccessibilityManager = (AccessibilityManager) appContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
53-mEnabled = mAccessibilityManager.isTouchExplorationEnabled();
77+mContentResolver = getReactApplicationContext().getContentResolver();
78+mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
79+mReduceMotionEnabled = this.getIsReduceMotionEnabledValue();
80+5481if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
5582mTouchExplorationStateChangeListener = new ReactTouchExplorationStateChangeListener();
5683 }
@@ -61,16 +88,41 @@ public String getName() {
6188return "AccessibilityInfo";
6289 }
639091+private boolean getIsReduceMotionEnabledValue() {
92+String value = Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 ? null
93+ : Settings.Global.getString(
94+mContentResolver,
95+Settings.Global.TRANSITION_ANIMATION_SCALE
96+ );
97+98+return value != null && value.equals("0.0");
99+ }
100+101+@ReactMethod
102+public void isReduceMotionEnabled(Callback successCallback) {
103+successCallback.invoke(mReduceMotionEnabled);
104+ }
105+64106@ReactMethod
65107public void isTouchExplorationEnabled(Callback successCallback) {
66-successCallback.invoke(mEnabled);
108+successCallback.invoke(mTouchExplorationEnabled);
109+ }
110+111+private void updateAndSendReduceMotionChangeEvent() {
112+boolean isReduceMotionEnabled = this.getIsReduceMotionEnabledValue();
113+114+if (mReduceMotionEnabled != isReduceMotionEnabled) {
115+mReduceMotionEnabled = isReduceMotionEnabled;
116+getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
117+ .emit(REDUCE_MOTION_EVENT_NAME, mReduceMotionEnabled);
118+ }
67119 }
6812069-private void updateAndSendChangeEvent(boolean enabled) {
70-if (mEnabled != enabled) {
71-mEnabled = enabled;
121+private void updateAndSendTouchExplorationChangeEvent(boolean enabled) {
122+if (mTouchExplorationEnabled != enabled) {
123+mTouchExplorationEnabled = enabled;
72124getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
73- .emit(EVENT_NAME, mEnabled);
125+ .emit(TOUCH_EXPLORATION_EVENT_NAME, mTouchExplorationEnabled);
74126 }
75127 }
76128@@ -80,7 +132,14 @@ public void onHostResume() {
80132mAccessibilityManager.addTouchExplorationStateChangeListener(
81133mTouchExplorationStateChangeListener);
82134 }
83-updateAndSendChangeEvent(mAccessibilityManager.isTouchExplorationEnabled());
135+136+if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
137+Uri transitionUri = Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE);
138+mContentResolver.registerContentObserver(transitionUri, false, animationScaleObserver);
139+ }
140+141+updateAndSendTouchExplorationChangeEvent(mAccessibilityManager.isTouchExplorationEnabled());
142+updateAndSendReduceMotionChangeEvent();
84143 }
8514486145@Override
@@ -89,12 +148,17 @@ public void onHostPause() {
89148mAccessibilityManager.removeTouchExplorationStateChangeListener(
90149mTouchExplorationStateChangeListener);
91150 }
151+152+if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
153+mContentResolver.unregisterContentObserver(animationScaleObserver);
154+ }
92155 }
9315694157@Override
95158public void initialize() {
96159getReactApplicationContext().addLifecycleEventListener(this);
97-updateAndSendChangeEvent(mAccessibilityManager.isTouchExplorationEnabled());
160+updateAndSendTouchExplorationChangeEvent(mAccessibilityManager.isTouchExplorationEnabled());
161+updateAndSendReduceMotionChangeEvent();
98162 }
99163100164@Override