• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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