1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.util.sensors;
18 
19 import android.content.res.Resources;
20 import android.hardware.Sensor;
21 import android.hardware.SensorManager;
22 import android.text.TextUtils;
23 import android.util.Log;
24 
25 import androidx.annotation.NonNull;
26 
27 import com.android.systemui.dagger.qualifiers.Main;
28 import com.android.systemui.res.R;
29 import com.android.systemui.statusbar.policy.DevicePostureController;
30 import com.android.systemui.util.concurrency.DelayableExecutor;
31 
32 import dagger.Lazy;
33 import dagger.Module;
34 import dagger.Provides;
35 
36 import java.util.Arrays;
37 import java.util.HashMap;
38 import java.util.Map;
39 
40 /**
41  * Dagger module for Sensor related classes.
42  */
43 @Module
44 public class SensorModule {
45     @Provides
46     @PrimaryProxSensor
providePrimaryProximitySensor( SensorManager sensorManager, ThresholdSensorImpl.Builder thresholdSensorBuilder )47     static ThresholdSensor providePrimaryProximitySensor(
48             SensorManager sensorManager,
49             ThresholdSensorImpl.Builder thresholdSensorBuilder
50     ) {
51         try {
52             return thresholdSensorBuilder
53                     .setSensorDelay(SensorManager.SENSOR_DELAY_NORMAL)
54                     .setSensorResourceId(R.string.proximity_sensor_type, true)
55                     .setThresholdResourceId(R.dimen.proximity_sensor_threshold)
56                     .setThresholdLatchResourceId(R.dimen.proximity_sensor_threshold_latch)
57                     .build();
58         } catch (IllegalStateException e) {
59             Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY,
60                     true);
61             return thresholdSensorBuilder
62                     .setSensor(defaultSensor)
63                     .setThresholdValue(defaultSensor != null ? defaultSensor.getMaximumRange() : 0)
64                     .build();
65         }
66     }
67 
68     @Provides
69     @SecondaryProxSensor
provideSecondaryProximitySensor( ThresholdSensorImpl.Builder thresholdSensorBuilder )70     static ThresholdSensor provideSecondaryProximitySensor(
71             ThresholdSensorImpl.Builder thresholdSensorBuilder
72     ) {
73         try {
74             return thresholdSensorBuilder
75                     .setSensorResourceId(R.string.proximity_sensor_secondary_type, true)
76                     .setThresholdResourceId(R.dimen.proximity_sensor_secondary_threshold)
77                     .setThresholdLatchResourceId(R.dimen.proximity_sensor_secondary_threshold_latch)
78                     .build();
79         } catch (IllegalStateException e) {
80             return thresholdSensorBuilder.setSensor(null).setThresholdValue(0).build();
81         }
82     }
83 
84     /**
85      * If postures are supported on the device, returns a posture dependent proximity sensor
86      * which switches proximity sensors based on the current posture.
87      *
88      * If postures are not supported the regular {@link ProximitySensorImpl} will be returned.
89      */
90     @Provides
provideProximitySensor( @ain Resources resources, Lazy<PostureDependentProximitySensor> postureDependentProximitySensorProvider, Lazy<ProximitySensorImpl> proximitySensorProvider )91     static ProximitySensor provideProximitySensor(
92             @Main Resources resources,
93             Lazy<PostureDependentProximitySensor> postureDependentProximitySensorProvider,
94             Lazy<ProximitySensorImpl> proximitySensorProvider
95     ) {
96         if (hasPostureSupport(
97                 resources.getStringArray(R.array.proximity_sensor_posture_mapping))) {
98             return postureDependentProximitySensorProvider.get();
99         } else  {
100             return proximitySensorProvider.get();
101         }
102     }
103 
104     @Provides
provideProximityCheck( ProximitySensor proximitySensor, @Main DelayableExecutor delayableExecutor )105     static ProximityCheck provideProximityCheck(
106             ProximitySensor proximitySensor,
107             @Main DelayableExecutor delayableExecutor
108     ) {
109         return new ProximityCheck(
110                 proximitySensor,
111                 delayableExecutor
112         );
113     }
114 
115     @Provides
116     @PrimaryProxSensor
117     @NonNull
providePostureToProximitySensorMapping( ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory, @Main Resources resources )118     static ThresholdSensor[] providePostureToProximitySensorMapping(
119             ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
120             @Main Resources resources
121     ) {
122         return createPostureToSensorMapping(
123                 thresholdSensorImplBuilderFactory,
124                 resources.getStringArray(R.array.proximity_sensor_posture_mapping),
125                 R.dimen.proximity_sensor_threshold,
126                 R.dimen.proximity_sensor_threshold_latch
127         );
128     }
129 
130     @Provides
131     @SecondaryProxSensor
132     @NonNull
providePostureToSecondaryProximitySensorMapping( ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory, @Main Resources resources )133     static ThresholdSensor[] providePostureToSecondaryProximitySensorMapping(
134             ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
135             @Main Resources resources
136     ) {
137         return createPostureToSensorMapping(
138                 thresholdSensorImplBuilderFactory,
139                 resources.getStringArray(R.array.proximity_sensor_secondary_posture_mapping),
140                 R.dimen.proximity_sensor_secondary_threshold,
141                 R.dimen.proximity_sensor_secondary_threshold_latch
142         );
143     }
144 
145     /**
146      * Builds sensors to use per posture.
147      *
148      * @param sensorTypes an array where the index represents
149      *                    {@link DevicePostureController.DevicePostureInt} and the value
150      *                    at the given index is the sensorType. Empty values represent
151      *                    no sensor desired.
152      * @param proximitySensorThresholdResourceId resource id for the threshold for all sensor
153      *                                           postures. This currently only supports one value.
154      *                                           This needs to be updated in the future if postures
155      *                                           use different sensors with differing thresholds.
156      * @param proximitySensorThresholdLatchResourceId resource id for the latch for all sensor
157      *                                                postures. This currently only supports one
158      *                                                value. This needs to be updated in the future
159      *                                                if postures use different sensors with
160      *                                                differing latches.
161      * @return an array where the index represents the device posture
162      * {@link DevicePostureController.DevicePostureInt} and the value at the index is the sensor to
163      * use when the device is in that posture.
164      */
165     @NonNull
createPostureToSensorMapping( ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory, String[] sensorTypes, int proximitySensorThresholdResourceId, int proximitySensorThresholdLatchResourceId )166     private static ThresholdSensor[] createPostureToSensorMapping(
167             ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
168             String[] sensorTypes,
169             int proximitySensorThresholdResourceId,
170             int proximitySensorThresholdLatchResourceId
171 
172     ) {
173         ThresholdSensor noProxSensor = thresholdSensorImplBuilderFactory
174                 .createBuilder()
175                 .setSensor(null).setThresholdValue(0).build();
176 
177 
178         // length and index of sensorMap correspond to DevicePostureController.DevicePostureInt:
179         final ThresholdSensor[] sensorMap =
180                 new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE];
181         Arrays.fill(sensorMap, noProxSensor);
182 
183         if (!hasPostureSupport(sensorTypes)) {
184             Log.e("SensorModule", "config doesn't support postures,"
185                     + " but attempting to retrieve proxSensorMapping");
186             return sensorMap;
187         }
188 
189         // Map of sensorType => Sensor, so we reuse the same sensor if it's the same between
190         // postures
191         Map<String, ThresholdSensor> typeToSensorMap = new HashMap<>();
192         for (int i = 0; i < sensorTypes.length; i++) {
193             try {
194                 final String sensorType = sensorTypes[i];
195                 if (typeToSensorMap.containsKey(sensorType)) {
196                     sensorMap[i] = typeToSensorMap.get(sensorType);
197                 } else {
198                     sensorMap[i] = thresholdSensorImplBuilderFactory
199                             .createBuilder()
200                             .setSensorType(sensorTypes[i], true)
201                             .setThresholdResourceId(proximitySensorThresholdResourceId)
202                             .setThresholdLatchResourceId(proximitySensorThresholdLatchResourceId)
203                             .build();
204                     typeToSensorMap.put(sensorType, sensorMap[i]);
205                 }
206             } catch (IllegalStateException e) {
207                 // do nothing, sensor at this posture is already set to noProxSensor
208             }
209         }
210 
211         return sensorMap;
212     }
213 
214     /**
215      * Returns true if there's at least one non-empty sensor type in the given array.
216      */
hasPostureSupport(String[] postureToSensorTypeMapping)217     private static boolean hasPostureSupport(String[] postureToSensorTypeMapping) {
218         if (postureToSensorTypeMapping == null || postureToSensorTypeMapping.length == 0) {
219             return false;
220         }
221 
222         for (String sensorType : postureToSensorTypeMapping) {
223             if (!TextUtils.isEmpty(sensorType)) {
224                 return true;
225             }
226         }
227 
228         return false;
229     }
230 }
231