Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static android.os.Build.VERSION_CODES.KITKAT;
      4 import static android.os.Build.VERSION_CODES.O;
      5 import static android.os.Build.VERSION_CODES.O_MR1;
      6 import static org.robolectric.RuntimeEnvironment.getApiLevel;
      7 
      8 import android.accessibilityservice.AccessibilityServiceInfo;
      9 import android.content.Context;
     10 import android.content.pm.ServiceInfo;
     11 import android.os.Handler;
     12 import android.os.Looper;
     13 import android.os.Message;
     14 import android.util.Log;
     15 import android.view.accessibility.AccessibilityManager;
     16 import android.view.accessibility.IAccessibilityManager;
     17 import java.util.List;
     18 import org.robolectric.annotation.HiddenApi;
     19 import org.robolectric.annotation.Implementation;
     20 import org.robolectric.annotation.Implements;
     21 import org.robolectric.annotation.RealObject;
     22 import org.robolectric.annotation.Resetter;
     23 import org.robolectric.shadow.api.Shadow;
     24 import org.robolectric.util.ReflectionHelpers;
     25 import org.robolectric.util.ReflectionHelpers.ClassParameter;
     26 
     27 @Implements(AccessibilityManager.class)
     28 public class ShadowAccessibilityManager {
     29   private static AccessibilityManager sInstance;
     30   private static final Object sInstanceSync = new Object();
     31 
     32   @RealObject AccessibilityManager realAccessibilityManager;
     33   private boolean enabled;
     34   private List<AccessibilityServiceInfo> installedAccessibilityServiceList;
     35   private List<AccessibilityServiceInfo> enabledAccessibilityServiceList;
     36   private List<ServiceInfo> accessibilityServiceList;
     37   private boolean touchExplorationEnabled;
     38 
     39   private static boolean isAccessibilityButtonSupported = true;
     40 
     41   @Resetter
     42   public static void reset() {
     43     synchronized (sInstanceSync) {
     44       sInstance = null;
     45     }
     46     isAccessibilityButtonSupported = true;
     47   }
     48 
     49   @HiddenApi
     50   @Implementation
     51   public static AccessibilityManager getInstance(Context context) throws Exception {
     52     synchronized (sInstanceSync) {
     53       if (sInstance == null) {
     54           sInstance = createInstance(context);
     55       }
     56     }
     57     return sInstance;
     58   }
     59 
     60   private static AccessibilityManager createInstance(Context context) throws Exception {
     61     if (getApiLevel() >= KITKAT) {
     62       AccessibilityManager accessibilityManager = Shadow.newInstance(AccessibilityManager.class,
     63           new Class[]{Context.class, IAccessibilityManager.class, int.class},
     64           new Object[]{context, ReflectionHelpers.createNullProxy(IAccessibilityManager.class), 0});
     65       ReflectionHelpers.setField(accessibilityManager, "mHandler", new MyHandler(context.getMainLooper(), accessibilityManager));
     66       return accessibilityManager;
     67     } else {
     68       AccessibilityManager accessibilityManager = Shadow.newInstance(AccessibilityManager.class, new Class[0], new Object[0]);
     69       ReflectionHelpers.setField(accessibilityManager, "mHandler", new MyHandler(context.getMainLooper(), accessibilityManager));
     70       return accessibilityManager;
     71     }
     72   }
     73 
     74   @Implementation
     75   protected boolean addAccessibilityStateChangeListener(
     76       AccessibilityManager.AccessibilityStateChangeListener listener) {
     77     return true;
     78   }
     79 
     80   @Implementation
     81   protected boolean removeAccessibilityStateChangeListener(
     82       AccessibilityManager.AccessibilityStateChangeListener listener) {
     83     return true;
     84   }
     85 
     86   @Implementation
     87   protected List<ServiceInfo> getAccessibilityServiceList() {
     88     return accessibilityServiceList;
     89   }
     90 
     91   public void setAccessibilityServiceList(List<ServiceInfo> accessibilityServiceList) {
     92     this.accessibilityServiceList = accessibilityServiceList;
     93   }
     94 
     95   @Implementation
     96   protected List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
     97       int feedbackTypeFlags) {
     98     return enabledAccessibilityServiceList;
     99   }
    100 
    101   public void setEnabledAccessibilityServiceList(List<AccessibilityServiceInfo> enabledAccessibilityServiceList) {
    102     this.enabledAccessibilityServiceList = enabledAccessibilityServiceList;
    103   }
    104 
    105   @Implementation
    106   protected List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
    107     return installedAccessibilityServiceList;
    108   }
    109 
    110   public void setInstalledAccessibilityServiceList(List<AccessibilityServiceInfo> installedAccessibilityServiceList) {
    111     this.installedAccessibilityServiceList = installedAccessibilityServiceList;
    112   }
    113 
    114   @Implementation
    115   protected boolean isEnabled() {
    116     return enabled;
    117   }
    118 
    119   public void setEnabled(boolean enabled) {
    120     this.enabled = enabled;
    121     ReflectionHelpers.setField(realAccessibilityManager, "mIsEnabled", enabled);
    122   }
    123 
    124   @Implementation
    125   protected boolean isTouchExplorationEnabled() {
    126     return touchExplorationEnabled;
    127   }
    128 
    129   public void setTouchExplorationEnabled(boolean touchExplorationEnabled) {
    130     this.touchExplorationEnabled = touchExplorationEnabled;
    131   }
    132 
    133   /**
    134    * Returns {@code true} by default, or the value specified via {@link
    135    * #setAccessibilityButtonSupported(boolean)}
    136    */
    137   @Implementation(minSdk = O_MR1)
    138   protected static boolean isAccessibilityButtonSupported() {
    139     return isAccessibilityButtonSupported;
    140   }
    141 
    142   @HiddenApi
    143   @Implementation(minSdk = O)
    144   protected void performAccessibilityShortcut() {
    145     setEnabled(true);
    146     setTouchExplorationEnabled(true);
    147   }
    148 
    149   /**
    150    * Sets that the system navigation area is supported accessibility button; controls the return
    151    * value of {@link AccessibilityManager#isAccessibilityButtonSupported()}.
    152    */
    153   public static void setAccessibilityButtonSupported(boolean supported) {
    154     isAccessibilityButtonSupported = supported;
    155   }
    156 
    157   static class MyHandler extends Handler {
    158     private static final int DO_SET_STATE = 10;
    159     private final AccessibilityManager accessibilityManager;
    160 
    161     MyHandler(Looper mainLooper, AccessibilityManager accessibilityManager) {
    162       super(mainLooper);
    163       this.accessibilityManager = accessibilityManager;
    164     }
    165 
    166     @Override
    167     public void handleMessage(Message message) {
    168       switch (message.what) {
    169         case DO_SET_STATE:
    170           ReflectionHelpers.callInstanceMethod(accessibilityManager, "setState", ClassParameter.from(int.class, message.arg1));
    171           return;
    172         default:
    173           Log.w("AccessibilityManager", "Unknown message type: " + message.what);
    174       }
    175     }
    176   }
    177 }
    178