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 import static com.google.common.base.Preconditions.checkNotNull; 6 7 import android.hardware.Sensor; 8 import android.hardware.SensorDirectChannel; 9 import android.hardware.SensorEvent; 10 import android.hardware.SensorEventListener; 11 import android.hardware.SensorEventListener2; 12 import android.hardware.SensorManager; 13 import android.os.Handler; 14 import android.os.Looper; 15 import android.os.MemoryFile; 16 import com.google.common.collect.HashMultimap; 17 import com.google.common.collect.ImmutableList; 18 import com.google.common.collect.Multimap; 19 import com.google.common.collect.Multimaps; 20 import java.util.Collection; 21 import java.util.List; 22 import java.util.Map.Entry; 23 import java.util.stream.Collectors; 24 import org.robolectric.annotation.Implementation; 25 import org.robolectric.annotation.Implements; 26 import org.robolectric.annotation.RealObject; 27 import org.robolectric.util.ReflectionHelpers; 28 import org.robolectric.util.ReflectionHelpers.ClassParameter; 29 30 @Implements(value = SensorManager.class, looseSignatures = true) 31 public class ShadowSensorManager { 32 public boolean forceListenersToFail = false; 33 private final Multimap<Integer, Sensor> sensorMap = 34 Multimaps.synchronizedMultimap(HashMultimap.create()); 35 private final Multimap<SensorEventListener, Sensor> listeners = 36 Multimaps.synchronizedMultimap(HashMultimap.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.get(sensor.getType()).remove(sensor); 64 } 65 66 @Implementation getDefaultSensor(int type)67 protected Sensor getDefaultSensor(int type) { 68 Collection<Sensor> sensorsForType = sensorMap.get(type); 69 if (sensorsForType.isEmpty()) { 70 return null; 71 } 72 73 return ((Sensor) sensorsForType.toArray()[0]); 74 } 75 76 @Implementation getSensorList(int type)77 public List<Sensor> getSensorList(int type) { 78 if (type == Sensor.TYPE_ALL) { 79 return ImmutableList.copyOf(sensorMap.values()); 80 } 81 82 return ImmutableList.copyOf(sensorMap.get(type)); 83 } 84 85 /** @param handler is ignored. */ 86 @Implementation registerListener( SensorEventListener listener, Sensor sensor, int rate, Handler handler)87 protected boolean registerListener( 88 SensorEventListener listener, Sensor sensor, int rate, Handler handler) { 89 return registerListener(listener, sensor, rate); 90 } 91 92 /** 93 * @param maxLatency is ignored. 94 */ 95 @Implementation registerListener( SensorEventListener listener, Sensor sensor, int rate, int maxLatency)96 protected boolean registerListener( 97 SensorEventListener listener, Sensor sensor, int rate, int maxLatency) { 98 return registerListener(listener, sensor, rate); 99 } 100 101 /** 102 * @param maxLatency is ignored. 103 * @param handler is ignored 104 */ 105 @Implementation registerListener( SensorEventListener listener, Sensor sensor, int rate, int maxLatency, Handler handler)106 protected boolean registerListener( 107 SensorEventListener listener, Sensor sensor, int rate, int maxLatency, Handler handler) { 108 return registerListener(listener, sensor, rate); 109 } 110 111 @Implementation registerListener(SensorEventListener listener, Sensor sensor, int rate)112 protected boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) { 113 if (forceListenersToFail) { 114 return false; 115 } 116 listeners.put(listener, sensor); 117 return true; 118 } 119 120 @Implementation unregisterListener(SensorEventListener listener, Sensor sensor)121 protected void unregisterListener(SensorEventListener listener, Sensor sensor) { 122 listeners.remove(listener, sensor); 123 } 124 125 @Implementation unregisterListener(SensorEventListener listener)126 protected void unregisterListener(SensorEventListener listener) { 127 listeners.removeAll(listener); 128 } 129 130 /** Tests if the sensor manager has a registration for the given listener. */ hasListener(SensorEventListener listener)131 public boolean hasListener(SensorEventListener listener) { 132 return listeners.containsKey(listener); 133 } 134 135 /** Tests if the sensor manager has a registration for the given listener for the given sensor. */ hasListener(SensorEventListener listener, Sensor sensor)136 public boolean hasListener(SensorEventListener listener, Sensor sensor) { 137 return listeners.containsEntry(listener, sensor); 138 } 139 140 /** 141 * Returns the list of {@link SensorEventListener}s registered on this SensorManager. Note that 142 * the list is unmodifiable, any attempt to modify it will throw an exception. 143 */ getListeners()144 public List<SensorEventListener> getListeners() { 145 return ImmutableList.copyOf(listeners.keySet()); 146 } 147 148 /** Propagates the {@code event} to all registered listeners. */ sendSensorEventToListeners(SensorEvent event)149 public void sendSensorEventToListeners(SensorEvent event) { 150 for (SensorEventListener listener : getListeners()) { 151 listener.onSensorChanged(event); 152 } 153 } 154 155 /** Propagates the {@code event} to only registered listeners of the given sensor. */ 156 @SuppressWarnings("JdkCollectors") // toImmutableList is only supported in Java 8+. sendSensorEventToListeners(SensorEvent event, Sensor sensor)157 public void sendSensorEventToListeners(SensorEvent event, Sensor sensor) { 158 List<SensorEventListener> listenersRegisteredToSensor = 159 listeners.entries().stream() 160 .filter(entry -> entry.getValue() == sensor) 161 .map(Entry::getKey) 162 .collect(Collectors.toList()); 163 164 for (SensorEventListener listener : listenersRegisteredToSensor) { 165 listener.onSensorChanged(event); 166 } 167 } 168 169 @Implementation flush(SensorEventListener listener)170 protected boolean flush(SensorEventListener listener) { 171 // ShadowSensorManager doesn't queue up any sensor events, so nothing actually needs to be 172 // flushed. Just call onFlushCompleted for each sensor that would have been flushed. 173 new Handler(Looper.getMainLooper()) 174 .post( 175 () -> { 176 // Go through each sensor that the listener is registered for, and call 177 // onFlushCompleted on each listener registered for that sensor. 178 for (Sensor sensor : listeners.get(listener)) { 179 for (SensorEventListener registeredListener : getListeners()) { 180 if ((registeredListener instanceof SensorEventListener2) 181 && listeners.containsEntry(registeredListener, sensor)) { 182 ((SensorEventListener2) registeredListener).onFlushCompleted(sensor); 183 } 184 } 185 } 186 }); 187 return listeners.containsKey(listener); 188 } 189 190 /** 191 * @deprecated Use {@code {@link SensorEventBuilder#newBuilder()}} instead. 192 */ 193 @Deprecated createSensorEvent()194 public SensorEvent createSensorEvent() { 195 return ReflectionHelpers.callConstructor(SensorEvent.class); 196 } 197 198 /** 199 * Creates a {@link SensorEvent} with the given value array size, which the caller should set 200 * based on the type of {@link Sensor} which is being emulated. 201 * 202 * <p>Callers can then specify individual values for the event. For example, for a proximity event 203 * a caller may wish to specify the distance value: 204 * 205 * <pre>{@code 206 * event.values[0] = distance; 207 * }</pre> 208 * 209 * <p>See {@link SensorEvent#values} for more information about values. 210 * 211 * @deprecated Use {@code {@link SensorEventBuilder#newBuilder()}} instead. 212 */ 213 @Deprecated createSensorEvent(int valueArraySize)214 public static SensorEvent createSensorEvent(int valueArraySize) { 215 return createSensorEvent(valueArraySize, Sensor.TYPE_GRAVITY); 216 } 217 218 /** 219 * Creates a {@link SensorEvent} for the given {@link Sensor} type with the given value array 220 * size, which the caller should set based on the type of sensor which is being emulated. 221 * 222 * <p>Callers can then specify individual values for the event. For example, for a proximity event 223 * a caller may wish to specify the distance value: 224 * 225 * <pre>{@code 226 * event.values[0] = distance; 227 * }</pre> 228 * 229 * <p>See {@link SensorEvent#values} for more information about values. 230 * 231 * @deprecated Use {@code {@link SensorEventBuilder#newBuilder()}} instead. 232 */ 233 @Deprecated createSensorEvent(int valueArraySize, int sensorType)234 public static SensorEvent createSensorEvent(int valueArraySize, int sensorType) { 235 checkArgument(valueArraySize > 0); 236 ClassParameter<Integer> valueArraySizeParam = new ClassParameter<>(int.class, valueArraySize); 237 SensorEvent sensorEvent = 238 ReflectionHelpers.callConstructor(SensorEvent.class, valueArraySizeParam); 239 sensorEvent.sensor = ShadowSensor.newInstance(sensorType); 240 return sensorEvent; 241 } 242 243 @Implementation(minSdk = O) createDirectChannel(MemoryFile mem)244 protected Object createDirectChannel(MemoryFile mem) { 245 return ReflectionHelpers.callConstructor(SensorDirectChannel.class, 246 ClassParameter.from(SensorManager.class, realObject), 247 ClassParameter.from(int.class, 0), 248 ClassParameter.from(int.class, SensorDirectChannel.TYPE_MEMORY_FILE), 249 ClassParameter.from(long.class, mem.length())); 250 } 251 } 252