1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.O; 4 import static com.google.common.base.Preconditions.checkArgument; 5 6 import android.hardware.Sensor; 7 import android.hardware.SensorDirectChannel; 8 import android.hardware.SensorEvent; 9 import android.hardware.SensorEventListener; 10 import android.hardware.SensorManager; 11 import android.os.Handler; 12 import android.os.MemoryFile; 13 import java.util.ArrayList; 14 import java.util.Collections; 15 import java.util.HashMap; 16 import java.util.List; 17 import java.util.Map; 18 import org.robolectric.annotation.Implementation; 19 import org.robolectric.annotation.Implements; 20 import org.robolectric.annotation.RealObject; 21 import org.robolectric.util.ReflectionHelpers; 22 import org.robolectric.util.ReflectionHelpers.ClassParameter; 23 24 @Implements(value = SensorManager.class, looseSignatures = true) 25 public class ShadowSensorManager { 26 public boolean forceListenersToFail = false; 27 private final Map<Integer, Sensor> sensorMap = new HashMap<>(); 28 private final ArrayList<SensorEventListener> listeners = new ArrayList<>(); 29 30 @RealObject private SensorManager realObject; 31 32 /** 33 * Provide a Sensor for the indicated sensor type. 34 * 35 * @param sensorType from Sensor constants 36 * @param sensor Sensor instance 37 * @deprecated Use {@link ShadowSensor#newInstance(int)} to construct your {@link Sensor} and add 38 * to the {@link SensorManager} using {@link #addSensor(Sensor)} instead. This method will be 39 * removed at some point allowing us to use more of the real {@link SensorManager} code. 40 */ 41 @Deprecated addSensor(int sensorType, Sensor sensor)42 public void addSensor(int sensorType, Sensor sensor) { 43 sensorMap.put(sensorType, sensor); 44 } 45 46 /** Adds a {@link Sensor} to the {@link SensorManager} */ addSensor(Sensor sensor)47 public void addSensor(Sensor sensor) { 48 sensorMap.put(sensor.getType(), sensor); 49 } 50 51 @Implementation getDefaultSensor(int type)52 protected Sensor getDefaultSensor(int type) { 53 return sensorMap.get(type); 54 } 55 56 /** @param handler is ignored. */ 57 @Implementation registerListener( SensorEventListener listener, Sensor sensor, int rate, Handler handler)58 protected boolean registerListener( 59 SensorEventListener listener, Sensor sensor, int rate, Handler handler) { 60 return registerListener(listener, sensor, rate); 61 } 62 63 @Implementation registerListener(SensorEventListener listener, Sensor sensor, int rate)64 protected boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) { 65 if (forceListenersToFail) { 66 return false; 67 } 68 if (!listeners.contains(listener)) { 69 listeners.add(listener); 70 } 71 return true; 72 } 73 74 @Implementation unregisterListener(SensorEventListener listener, Sensor sensor)75 protected void unregisterListener(SensorEventListener listener, Sensor sensor) { 76 listeners.remove(listener); 77 } 78 79 @Implementation unregisterListener(SensorEventListener listener)80 protected void unregisterListener(SensorEventListener listener) { 81 listeners.remove(listener); 82 } 83 hasListener(SensorEventListener listener)84 public boolean hasListener(SensorEventListener listener) { 85 return listeners.contains(listener); 86 } 87 88 /** 89 * Returns the list of {@link SensorEventListener}s registered on this SensorManager. Note that 90 * the list is unmodifiable, any attempt to modify it will throw an exception. 91 */ getListeners()92 public List<SensorEventListener> getListeners() { 93 return Collections.unmodifiableList(listeners); 94 } 95 96 /** Propagates the {@code event} to all registered listeners. */ sendSensorEventToListeners(SensorEvent event)97 public void sendSensorEventToListeners(SensorEvent event) { 98 for (SensorEventListener listener : listeners) { 99 listener.onSensorChanged(event); 100 } 101 } 102 createSensorEvent()103 public SensorEvent createSensorEvent() { 104 return ReflectionHelpers.callConstructor(SensorEvent.class); 105 } 106 107 /** 108 * Creates a {@link SensorEvent} with the given value array size, which the caller should set 109 * based on the type of {@link Sensor} which is being emulated. 110 * 111 * <p>Callers can then specify individual values for the event. For example, for a proximity event 112 * a caller may wish to specify the distance value: 113 * 114 * <pre>{@code 115 * event.values[0] = distance; 116 * }</pre> 117 * 118 * <p>See {@link SensorEvent#values} for more information about values. 119 */ createSensorEvent(int valueArraySize)120 public static SensorEvent createSensorEvent(int valueArraySize) { 121 checkArgument(valueArraySize > 0); 122 ClassParameter<Integer> valueArraySizeParam = new ClassParameter<>(int.class, valueArraySize); 123 return ReflectionHelpers.callConstructor(SensorEvent.class, valueArraySizeParam); 124 } 125 126 @Implementation(minSdk = O) createDirectChannel(MemoryFile mem)127 protected Object createDirectChannel(MemoryFile mem) { 128 return ReflectionHelpers.callConstructor(SensorDirectChannel.class, 129 ClassParameter.from(SensorManager.class, realObject), 130 ClassParameter.from(int.class, 0), 131 ClassParameter.from(int.class, SensorDirectChannel.TYPE_MEMORY_FILE), 132 ClassParameter.from(long.class, mem.length())); 133 } 134 } 135