@@ -46,6 +46,19 @@ public void onTouchExplorationStateChanged(boolean enabled) {
46
46
}
47
47
}
48
48
49
+ // Android can listen for accessibility service enable with `accessibilityStateChange`, but
50
+ // `accessibilityState` conflicts with React Native props and confuses developers. Therefore, the
51
+ // name `accessibilityServiceChange` is used here instead.
52
+ @ TargetApi (Build .VERSION_CODES .LOLLIPOP )
53
+ private class ReactAccessibilityServiceChangeListener
54
+ implements AccessibilityManager .AccessibilityStateChangeListener {
55
+
56
+ @ Override
57
+ public void onAccessibilityStateChanged (boolean enabled ) {
58
+ updateAndSendAccessibilityServiceChangeEvent (enabled );
59
+ }
60
+ }
61
+
49
62
// Listener that is notified when the global TRANSITION_ANIMATION_SCALE.
50
63
private final ContentObserver animationScaleObserver =
51
64
new ContentObserver (new Handler (Looper .getMainLooper ())) {
@@ -64,13 +77,16 @@ public void onChange(boolean selfChange, Uri uri) {
64
77
65
78
private @ Nullable AccessibilityManager mAccessibilityManager ;
66
79
private @ Nullable ReactTouchExplorationStateChangeListener mTouchExplorationStateChangeListener ;
80
+ private @ Nullable ReactAccessibilityServiceChangeListener mAccessibilityServiceChangeListener ;
67
81
private final ContentResolver mContentResolver ;
68
82
private boolean mReduceMotionEnabled = false ;
69
83
private boolean mTouchExplorationEnabled = false ;
84
+ private boolean mAccessibilityServiceEnabled = false ;
70
85
private int mRecommendedTimeout ;
71
86
72
87
private static final String REDUCE_MOTION_EVENT_NAME = "reduceMotionDidChange" ;
73
88
private static final String TOUCH_EXPLORATION_EVENT_NAME = "touchExplorationDidChange" ;
89
+ private static final String ACCESSIBILITY_SERVICE_EVENT_NAME = "accessibilityServiceDidChange" ;
74
90
75
91
public AccessibilityInfoModule (ReactApplicationContext context ) {
76
92
super (context );
@@ -79,8 +95,10 @@ public AccessibilityInfoModule(ReactApplicationContext context) {
79
95
(AccessibilityManager ) appContext .getSystemService (Context .ACCESSIBILITY_SERVICE );
80
96
mContentResolver = getReactApplicationContext ().getContentResolver ();
81
97
mTouchExplorationEnabled = mAccessibilityManager .isTouchExplorationEnabled ();
98
+ mAccessibilityServiceEnabled = mAccessibilityManager .isEnabled ();
82
99
mReduceMotionEnabled = this .getIsReduceMotionEnabledValue ();
83
100
mTouchExplorationStateChangeListener = new ReactTouchExplorationStateChangeListener ();
101
+ mAccessibilityServiceChangeListener = new ReactAccessibilityServiceChangeListener ();
84
102
}
85
103
86
104
@ Override
@@ -106,6 +124,11 @@ public void isTouchExplorationEnabled(Callback successCallback) {
106
124
successCallback .invoke (mTouchExplorationEnabled );
107
125
}
108
126
127
+ @ Override
128
+ public void isAccessibilityServiceEnabled (Callback successCallback ) {
129
+ successCallback .invoke (mAccessibilityServiceEnabled );
130
+ }
131
+
109
132
private void updateAndSendReduceMotionChangeEvent () {
110
133
boolean isReduceMotionEnabled = this .getIsReduceMotionEnabledValue ();
111
134
@@ -134,16 +157,31 @@ private void updateAndSendTouchExplorationChangeEvent(boolean enabled) {
134
157
}
135
158
}
136
159
160
+ private void updateAndSendAccessibilityServiceChangeEvent (boolean enabled ) {
161
+ if (mAccessibilityServiceEnabled != enabled ) {
162
+ mAccessibilityServiceEnabled = enabled ;
163
+
164
+ ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn ();
165
+ if (reactApplicationContext != null ) {
166
+ getReactApplicationContext ()
167
+ .getJSModule (DeviceEventManagerModule .RCTDeviceEventEmitter .class )
168
+ .emit (ACCESSIBILITY_SERVICE_EVENT_NAME , mAccessibilityServiceEnabled );
169
+ }
170
+ }
171
+ }
172
+
137
173
@ Override
138
174
@ TargetApi (Build .VERSION_CODES .LOLLIPOP )
139
175
public void onHostResume () {
140
176
mAccessibilityManager .addTouchExplorationStateChangeListener (
141
177
mTouchExplorationStateChangeListener );
178
+ mAccessibilityManager .addAccessibilityStateChangeListener (mAccessibilityServiceChangeListener );
142
179
143
180
Uri transitionUri = Settings .Global .getUriFor (Settings .Global .TRANSITION_ANIMATION_SCALE );
144
181
mContentResolver .registerContentObserver (transitionUri , false , animationScaleObserver );
145
182
146
183
updateAndSendTouchExplorationChangeEvent (mAccessibilityManager .isTouchExplorationEnabled ());
184
+ updateAndSendAccessibilityServiceChangeEvent (mAccessibilityManager .isEnabled ());
147
185
updateAndSendReduceMotionChangeEvent ();
148
186
}
149
187
@@ -152,6 +190,8 @@ public void onHostResume() {
152
190
public void onHostPause () {
153
191
mAccessibilityManager .removeTouchExplorationStateChangeListener (
154
192
mTouchExplorationStateChangeListener );
193
+ mAccessibilityManager .removeAccessibilityStateChangeListener (
194
+ mAccessibilityServiceChangeListener );
155
195
156
196
mContentResolver .unregisterContentObserver (animationScaleObserver );
157
197
}
@@ -160,6 +200,7 @@ public void onHostPause() {
160
200
public void initialize () {
161
201
getReactApplicationContext ().addLifecycleEventListener (this );
162
202
updateAndSendTouchExplorationChangeEvent (mAccessibilityManager .isTouchExplorationEnabled ());
203
+ updateAndSendAccessibilityServiceChangeEvent (mAccessibilityManager .isEnabled ());
163
204
updateAndSendReduceMotionChangeEvent ();
164
205
}
165
206
0 commit comments