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