blob: 1e2ecb800ea0b291e634a205b101ed823da704a1 [file] [log] [blame]
package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Build.VERSION_CODES.O_MR1;
import static org.robolectric.RuntimeEnvironment.getApiLevel;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import java.util.List;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
@Implements(AccessibilityManager.class)
public class ShadowAccessibilityManager {
private static AccessibilityManager sInstance;
private static final Object sInstanceSync = new Object();
@RealObject AccessibilityManager realAccessibilityManager;
private boolean enabled;
private List<AccessibilityServiceInfo> installedAccessibilityServiceList;
private List<AccessibilityServiceInfo> enabledAccessibilityServiceList;
private List<ServiceInfo> accessibilityServiceList;
private boolean touchExplorationEnabled;
private static boolean isAccessibilityButtonSupported = true;
@Resetter
public static void reset() {
synchronized (sInstanceSync) {
sInstance = null;
}
isAccessibilityButtonSupported = true;
}
@HiddenApi
@Implementation
public static AccessibilityManager getInstance(Context context) throws Exception {
synchronized (sInstanceSync) {
if (sInstance == null) {
sInstance = createInstance(context);
}
}
return sInstance;
}
private static AccessibilityManager createInstance(Context context) throws Exception {
if (getApiLevel() >= KITKAT) {
AccessibilityManager accessibilityManager = Shadow.newInstance(AccessibilityManager.class,
new Class[]{Context.class, IAccessibilityManager.class, int.class},
new Object[]{context, ReflectionHelpers.createNullProxy(IAccessibilityManager.class), 0});
ReflectionHelpers.setField(accessibilityManager, "mHandler", new MyHandler(context.getMainLooper(), accessibilityManager));
return accessibilityManager;
} else {
AccessibilityManager accessibilityManager = Shadow.newInstance(AccessibilityManager.class, new Class[0], new Object[0]);
ReflectionHelpers.setField(accessibilityManager, "mHandler", new MyHandler(context.getMainLooper(), accessibilityManager));
return accessibilityManager;
}
}
@Implementation
protected boolean addAccessibilityStateChangeListener(
AccessibilityManager.AccessibilityStateChangeListener listener) {
return true;
}
@Implementation
protected boolean removeAccessibilityStateChangeListener(
AccessibilityManager.AccessibilityStateChangeListener listener) {
return true;
}
@Implementation
protected List<ServiceInfo> getAccessibilityServiceList() {
return accessibilityServiceList;
}
public void setAccessibilityServiceList(List<ServiceInfo> accessibilityServiceList) {
this.accessibilityServiceList = accessibilityServiceList;
}
@Implementation
protected List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
int feedbackTypeFlags) {
return enabledAccessibilityServiceList;
}
public void setEnabledAccessibilityServiceList(List<AccessibilityServiceInfo> enabledAccessibilityServiceList) {
this.enabledAccessibilityServiceList = enabledAccessibilityServiceList;
}
@Implementation
protected List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
return installedAccessibilityServiceList;
}
public void setInstalledAccessibilityServiceList(List<AccessibilityServiceInfo> installedAccessibilityServiceList) {
this.installedAccessibilityServiceList = installedAccessibilityServiceList;
}
@Implementation
protected boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
ReflectionHelpers.setField(realAccessibilityManager, "mIsEnabled", enabled);
}
@Implementation
protected boolean isTouchExplorationEnabled() {
return touchExplorationEnabled;
}
public void setTouchExplorationEnabled(boolean touchExplorationEnabled) {
this.touchExplorationEnabled = touchExplorationEnabled;
}
/**
* Returns {@code true} by default, or the value specified via {@link
* #setAccessibilityButtonSupported(boolean)}
*/
@Implementation(minSdk = O_MR1)
protected static boolean isAccessibilityButtonSupported() {
return isAccessibilityButtonSupported;
}
@HiddenApi
@Implementation(minSdk = O)
protected void performAccessibilityShortcut() {
setEnabled(true);
setTouchExplorationEnabled(true);
}
/**
* Sets that the system navigation area is supported accessibility button; controls the return
* value of {@link AccessibilityManager#isAccessibilityButtonSupported()}.
*/
public static void setAccessibilityButtonSupported(boolean supported) {
isAccessibilityButtonSupported = supported;
}
static class MyHandler extends Handler {
private static final int DO_SET_STATE = 10;
private final AccessibilityManager accessibilityManager;
MyHandler(Looper mainLooper, AccessibilityManager accessibilityManager) {
super(mainLooper);
this.accessibilityManager = accessibilityManager;
}
@Override
public void handleMessage(Message message) {
switch (message.what) {
case DO_SET_STATE:
ReflectionHelpers.callInstanceMethod(accessibilityManager, "setState", ClassParameter.from(int.class, message.arg1));
return;
default:
Log.w("AccessibilityManager", "Unknown message type: " + message.what);
}
}
}
}