• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.car.hvac;
18 
19 import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
20 import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS;
21 import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
22 
23 import android.car.Car;
24 import android.car.VehicleUnit;
25 import android.car.hardware.CarPropertyValue;
26 import android.car.hardware.property.CarPropertyManager;
27 import android.util.Log;
28 import android.view.View;
29 import android.view.ViewGroup;
30 
31 import com.android.systemui.car.CarServiceProvider;
32 import com.android.systemui.dagger.SysUISingleton;
33 import com.android.systemui.dagger.qualifiers.UiBackground;
34 import com.android.systemui.statusbar.policy.ConfigurationController;
35 
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.concurrent.Executor;
43 
44 import javax.inject.Inject;
45 
46 /**
47  * Manages the connection to the Car service and delegates value changes to the registered
48  * {@link TemperatureView}s
49  */
50 @SysUISingleton
51 public class HvacController implements ConfigurationController.ConfigurationListener {
52     public static final String TAG = "HvacController";
53     private static final boolean DEBUG = true;
54 
55     private final Executor mBackgroundExecutor;
56     private final CarServiceProvider mCarServiceProvider;
57     private final Set<TemperatureView> mRegisteredViews = new HashSet<>();
58 
59     private CarPropertyManager mCarPropertyManager;
60     private HashMap<Integer, List<TemperatureView>> mTempComponents = new HashMap<>();
61 
62     private final CarPropertyManager.CarPropertyEventCallback mHvacTemperatureSetCallback =
63             new CarPropertyManager.CarPropertyEventCallback() {
64                 @Override
65                 public void onChangeEvent(CarPropertyValue value) {
66                     try {
67                         int areaId = value.getAreaId();
68                         List<TemperatureView> temperatureViews = mTempComponents.get(areaId);
69                         if (temperatureViews != null && !temperatureViews.isEmpty()) {
70                             float newTemp = (float) value.getValue();
71                             if (DEBUG) {
72                                 Log.d(TAG, "onChangeEvent: " + areaId + ":" + value);
73                             }
74                             for (TemperatureView view : temperatureViews) {
75                                 view.setTemp(newTemp);
76                             }
77                         }
78                     } catch (Exception e) {
79                         Log.e(TAG, "Failed handling hvac change event", e);
80                     }
81                 }
82 
83                 @Override
84                 public void onErrorEvent(int propId, int zone) {
85                     Log.d(TAG, "HVAC error event, propertyId: " + propId + " zone: " + zone);
86                 }
87             };
88 
89     private final CarPropertyManager.CarPropertyEventCallback mTemperatureUnitChangeCallback =
90             new CarPropertyManager.CarPropertyEventCallback() {
91                 @Override
92                 public void onChangeEvent(CarPropertyValue value) {
93                     if (!mRegisteredViews.isEmpty()) {
94                         for (TemperatureView view : mRegisteredViews) {
95                             view.setDisplayInFahrenheit(
96                                     value.getValue().equals(VehicleUnit.FAHRENHEIT));
97                         }
98                     }
99                 }
100 
101                 @Override
102                 public void onErrorEvent(int propId, int zone) {
103                     Log.d(TAG, "HVAC error event, propertyId: " + propId + " zone: " + zone);
104                 }
105             };
106 
107     private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceLifecycleListener =
108             car -> {
109                 try {
110                     mCarPropertyManager = (CarPropertyManager) car.getCarManager(
111                             Car.PROPERTY_SERVICE);
112                     mCarPropertyManager.registerCallback(mHvacTemperatureSetCallback,
113                             HVAC_TEMPERATURE_SET, CarPropertyManager.SENSOR_RATE_ONCHANGE);
114                     mCarPropertyManager.registerCallback(mTemperatureUnitChangeCallback,
115                             HVAC_TEMPERATURE_DISPLAY_UNITS,
116                             CarPropertyManager.SENSOR_RATE_ONCHANGE);
117                     initComponents();
118                 } catch (Exception e) {
119                     Log.e(TAG, "Failed to correctly connect to HVAC", e);
120                 }
121             };
122 
123     @Inject
HvacController(CarServiceProvider carServiceProvider, @UiBackground Executor backgroundExecutor, ConfigurationController configurationController)124     public HvacController(CarServiceProvider carServiceProvider,
125             @UiBackground Executor backgroundExecutor,
126             ConfigurationController configurationController) {
127         mCarServiceProvider = carServiceProvider;
128         mBackgroundExecutor = backgroundExecutor;
129         configurationController.addCallback(this);
130     }
131 
132     /**
133      * Create connection to the Car service.
134      */
connectToCarService()135     public void connectToCarService() {
136         mCarServiceProvider.addListener(mCarServiceLifecycleListener);
137     }
138 
139     /**
140      * Add component to list and initialize it if the connection is up.
141      */
addHvacTextView(TemperatureView temperatureView)142     private void addHvacTextView(TemperatureView temperatureView) {
143         if (mRegisteredViews.contains(temperatureView)) {
144             return;
145         }
146 
147         int areaId = temperatureView.getAreaId();
148         if (!mTempComponents.containsKey(areaId)) {
149             mTempComponents.put(areaId, new ArrayList<>());
150         }
151         mTempComponents.get(areaId).add(temperatureView);
152         initComponent(temperatureView);
153 
154         mRegisteredViews.add(temperatureView);
155     }
156 
initComponents()157     private void initComponents() {
158         for (Map.Entry<Integer, List<TemperatureView>> next : mTempComponents.entrySet()) {
159             List<TemperatureView> temperatureViews = next.getValue();
160             for (TemperatureView view : temperatureViews) {
161                 initComponent(view);
162             }
163         }
164     }
165 
initComponent(TemperatureView view)166     private void initComponent(TemperatureView view) {
167         int zone = view.getAreaId();
168         if (DEBUG) {
169             Log.d(TAG, "initComponent: " + zone);
170         }
171 
172         try {
173             if (mCarPropertyManager != null && mCarPropertyManager.isPropertyAvailable(
174                     HVAC_TEMPERATURE_DISPLAY_UNITS, VEHICLE_AREA_TYPE_GLOBAL)) {
175                 if (mCarPropertyManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS,
176                         VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) {
177                     view.setDisplayInFahrenheit(true);
178                 }
179             }
180             if (mCarPropertyManager == null || !mCarPropertyManager.isPropertyAvailable(
181                     HVAC_TEMPERATURE_SET, zone)) {
182                 view.setTemp(Float.NaN);
183                 return;
184             }
185             view.setTemp(
186                     mCarPropertyManager.getFloatProperty(HVAC_TEMPERATURE_SET, zone));
187             view.setHvacController(this);
188         } catch (Exception e) {
189             view.setTemp(Float.NaN);
190             Log.e(TAG, "Failed to get value from hvac service", e);
191         }
192     }
193 
194     /**
195      * Removes all registered components. This is useful if you need to rebuild the UI since
196      * components self register.
197      */
removeAllComponents()198     public void removeAllComponents() {
199         mTempComponents.clear();
200         mRegisteredViews.clear();
201     }
202 
203     /**
204      * Iterate through a view, looking for {@link TemperatureView} instances and add them to the
205      * controller if found.
206      */
addTemperatureViewToController(View v)207     public void addTemperatureViewToController(View v) {
208         if (v instanceof TemperatureView) {
209             addHvacTextView((TemperatureView) v);
210         } else if (v instanceof ViewGroup) {
211             ViewGroup viewGroup = (ViewGroup) v;
212             for (int i = 0; i < viewGroup.getChildCount(); i++) {
213                 addTemperatureViewToController(viewGroup.getChildAt(i));
214             }
215         }
216     }
217 
218     /**
219      * Set the temperature in Celsius of the specified zone
220      */
setTemperature(float tempC, int zone)221     public void setTemperature(float tempC, int zone) {
222         if (mCarPropertyManager != null) {
223             // Internally, all temperatures are represented in floating point Celsius
224             mBackgroundExecutor.execute(
225                     () -> mCarPropertyManager.setFloatProperty(HVAC_TEMPERATURE_SET, zone, tempC));
226         }
227     }
228 
229     @Override
onLocaleListChanged()230     public void onLocaleListChanged() {
231         for (TemperatureView view : mRegisteredViews) {
232             view.onLocaleListChanged();
233         }
234     }
235 
236     /**
237      * Convert the given temperature in Celsius into Fahrenheit
238      *
239      * @param tempC - The temperature in Celsius
240      * @return Temperature in Fahrenheit.
241      */
convertToFahrenheit(float tempC)242     public static float convertToFahrenheit(float tempC) {
243         return (tempC * 9f / 5f) + 32;
244     }
245 
246     /**
247      * Convert the given temperature in Fahrenheit to Celsius
248      *
249      * @param tempF - The temperature in Fahrenheit.
250      * @return Temperature in Celsius.
251      */
convertToCelsius(float tempF)252     public static float convertToCelsius(float tempF) {
253         return (tempF - 32) * 5f / 9f;
254     }
255 }
256