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