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 com.google.common.base.Preconditions.checkArgument; 6 import static com.google.common.base.Preconditions.checkNotNull; 7 8 import android.hardware.Sensor; 9 import android.hardware.SensorDirectChannel; 10 import android.hardware.SensorEvent; 11 import android.hardware.SensorEventListener; 12 import android.hardware.SensorEventListener2; 13 import android.hardware.SensorManager; 14 import android.os.Handler; 15 import android.os.Looper; 16 import android.os.MemoryFile; 17 import com.google.common.collect.HashMultimap; 18 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.Multimap; 20 import com.google.common.collect.Multimaps; 21 import java.util.ArrayList; 22 import java.util.HashMap; 23 import java.util.List; 24 import java.util.Map; 25 import org.robolectric.annotation.Implementation; 26 import org.robolectric.annotation.Implements; 27 import org.robolectric.annotation.RealObject; 28 import org.robolectric.util.ReflectionHelpers; 29 import org.robolectric.util.ReflectionHelpers.ClassParameter; 30 31 @Implements(value = SensorManager.class, looseSignatures = true) 32 public class ShadowSensorManager { 33 public boolean forceListenersToFail = false; 34 private final Map<Integer, Sensor> sensorMap = new HashMap<>(); 35 private final Multimap<SensorEventListener, Sensor> listeners = 36 Multimaps.synchronizedMultimap(HashMultimap.<SensorEventListener, Sensor>create()); 37 38 @RealObject private SensorManager realObject; 39 40 /** 41 * Provide a Sensor for the indicated sensor type. 42 * 43 * @param sensorType from Sensor constants 44 * @param sensor Sensor instance 45 * @deprecated Use {@link ShadowSensor#newInstance(int)} to construct your {@link Sensor} and add 46 * to the {@link SensorManager} using {@link #addSensor(Sensor)} instead. This method will be 47 * removed at some point allowing us to use more of the real {@link SensorManager} code. 48 */ 49 @Deprecated addSensor(int sensorType, Sensor sensor)50 public void addSensor(int sensorType, Sensor sensor) { 51 checkNotNull(sensor); 52 sensorMap.put(sensorType, sensor); 53 } 54 55 /** Adds a {@link Sensor} to the {@link SensorManager} */ addSensor(Sensor sensor)56 public void addSensor(Sensor sensor) { 57 checkNotNull(sensor); 58 sensorMap.put(sensor.getType(), sensor); 59 } 60 removeSensor(Sensor sensor)61 public void removeSensor(Sensor sensor) { 62 checkNotNull(sensor); 63 sensorMap.remove(sensor.getType()); 64 } 65 66 @Implementation getDefaultSensor(int type)67 protected Sensor getDefaultSensor(int type) { 68 return sensorMap.get(type); 69 } 70 71 @Implementation getSensorList(int type)72 public List<Sensor> getSensorList(int type) { 73 List<Sensor> sensorList = new ArrayList<>(); 74 Sensor sensor = sensorMap.get(type); 75 if (sensor != null) { 76 sensorList.add(sensor); 77 } 78 return sensorList; 79 } 80 81 /** @param handler is ignored. */ 82 @Implementation registerListener( SensorEventListener listener, Sensor sensor, int rate, Handler handler)83 protected boolean registerListener( 84 SensorEventListener listener, Sensor sensor, int rate, Handler handler) { 85 return registerListener(listener, sensor, rate); 86 } 87 88 /** 89 * @param maxLatency is ignored. 90 */ 91 @Implementation(minSdk = KITKAT) registerListener( SensorEventListener listener, Sensor sensor, int rate, int maxLatency)92 protected boolean registerListener( 93 SensorEventListener listener, Sensor sensor, int rate, int maxLatency) { 94 return registerListener(listener, sensor, rate); 95 } 96 97 /** 98 * @param maxLatency is ignored. 99 * @param handler is ignored 100 */ 101 @Implementation(minSdk = KITKAT) registerListener( SensorEventListener listener, Sensor sensor, int rate, int maxLatency, Handler handler)102 protected boolean registerListener( 103 SensorEventListener listener, Sensor sensor, int rate, int maxLatency, Handler handler) { 104 return registerListener(listener, sensor, rate); 105 } 106 107 @Implementation registerListener(SensorEventListener listener, Sensor sensor, int rate)108 protected boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) { 109 if (forceListenersToFail) { 110 return false; 111 } 112 listeners.put(listener, sensor); 113 return true; 114 } 115 116 @Implementation unregisterListener(SensorEventListener listener, Sensor sensor)117 protected void unregisterListener(SensorEventListener listener, Sensor sensor) { 118 listeners.remove(listener, sensor); 119 } 120 121 @Implementation unregisterListener(SensorEventListener listener)122 protected void unregisterListener(SensorEventListener listener) { 123 listeners.removeAll(listener); 124 } 125 126 /** Tests if the sensor manager has a registration for the given listener. */ hasListener(SensorEventListener listener)127 public boolean hasListener(SensorEventListener listener) { 128 return listeners.containsKey(listener); 129 } 130 131 /** Tests if the sensor manager has a registration for the given listener for the given sensor. */ hasListener(SensorEventListener listener, Sensor sensor)132 public boolean hasListener(SensorEventListener listener, Sensor sensor) { 133 return listeners.containsEntry(listener, sensor); 134 } 135 136 /** 137 * Returns the list of {@link SensorEventListener}s registered on this SensorManager. Note that 138 * the list is unmodifiable, any attempt to modify it will throw an exception. 139 */ getListeners()140 public List<SensorEventListener> getListeners() { 141 return ImmutableList.copyOf(listeners.keySet()); 142 } 143 144 /** Propagates the {@code event} to all registered listeners. */ sendSensorEventToListeners(SensorEvent event)145 public void sendSensorEventToListeners(SensorEvent event) { 146 for (SensorEventListener listener : getListeners()) { 147 listener.onSensorChanged(event); 148 } 149 } 150 151 @Implementation(minSdk = KITKAT) flush(SensorEventListener listener)152 protected boolean flush(SensorEventListener listener) { 153 // ShadowSensorManager doesn't queue up any sensor events, so nothing actually needs to be 154 // flushed. Just call onFlushCompleted for each sensor that would have been flushed. 155 new Handler(Looper.getMainLooper()) 156 .post( 157 () -> { 158 // Go through each sensor that the listener is registered for, and call 159 // onFlushCompleted on each listener registered for that sensor. 160 for (Sensor sensor : listeners.get(listener)) { 161 for (SensorEventListener registeredListener : getListeners()) { 162 if ((registeredListener instanceof SensorEventListener2) 163 && listeners.containsEntry(registeredListener, sensor)) { 164 ((SensorEventListener2) registeredListener).onFlushCompleted(sensor); 165 } 166 } 167 } 168 }); 169 return listeners.containsKey(listener); 170 } 171 createSensorEvent()172 public SensorEvent createSensorEvent() { 173 return ReflectionHelpers.callConstructor(SensorEvent.class); 174 } 175 176 /** 177 * Creates a {@link SensorEvent} with the given value array size, which the caller should set 178 * based on the type of {@link Sensor} which is being emulated. 179 * 180 * <p>Callers can then specify individual values for the event. For example, for a proximity event 181 * a caller may wish to specify the distance value: 182 * 183 * <pre>{@code 184 * event.values[0] = distance; 185 * }</pre> 186 * 187 * <p>See {@link SensorEvent#values} for more information about values. 188 */ createSensorEvent(int valueArraySize)189 public static SensorEvent createSensorEvent(int valueArraySize) { 190 return createSensorEvent(valueArraySize, Sensor.TYPE_GRAVITY); 191 } 192 193 /** 194 * Creates a {@link SensorEvent} for the given {@link Sensor} type with the given value array 195 * size, which the caller should set based on the type of sensor which is being emulated. 196 * 197 * <p>Callers can then specify individual values for the event. For example, for a proximity event 198 * a caller may wish to specify the distance value: 199 * 200 * <pre>{@code 201 * event.values[0] = distance; 202 * }</pre> 203 * 204 * <p>See {@link SensorEvent#values} for more information about values. 205 */ createSensorEvent(int valueArraySize, int sensorType)206 public static SensorEvent createSensorEvent(int valueArraySize, int sensorType) { 207 checkArgument(valueArraySize > 0); 208 ClassParameter<Integer> valueArraySizeParam = new ClassParameter<>(int.class, valueArraySize); 209 SensorEvent sensorEvent = 210 ReflectionHelpers.callConstructor(SensorEvent.class, valueArraySizeParam); 211 sensorEvent.sensor = ShadowSensor.newInstance(sensorType); 212 return sensorEvent; 213 } 214 215 @Implementation(minSdk = O) createDirectChannel(MemoryFile mem)216 protected Object createDirectChannel(MemoryFile mem) { 217 return ReflectionHelpers.callConstructor(SensorDirectChannel.class, 218 ClassParameter.from(SensorManager.class, realObject), 219 ClassParameter.from(int.class, 0), 220 ClassParameter.from(int.class, SensorDirectChannel.TYPE_MEMORY_FILE), 221 ClassParameter.from(long.class, mem.length())); 222 } 223 } 224