1 /* 2 * Copyright (C) 2018 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.statusbar.car.hvac; 18 19 import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; 20 import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS; 21 22 import android.car.Car; 23 import android.car.Car.CarServiceLifecycleListener; 24 import android.car.VehicleUnit; 25 import android.car.hardware.CarPropertyValue; 26 import android.car.hardware.hvac.CarHvacManager; 27 import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback; 28 import android.content.Context; 29 import android.util.Log; 30 31 import com.android.systemui.CarSystemUIFactory; 32 import com.android.systemui.SystemUIFactory; 33 34 import java.util.ArrayList; 35 import java.util.HashMap; 36 import java.util.Iterator; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Objects; 40 41 /** 42 * Manages the connection to the Car service and delegates value changes to the registered 43 * {@link TemperatureView}s 44 */ 45 public class HvacController { 46 public static final String TAG = "HvacController"; 47 48 private Context mContext; 49 private CarHvacManager mHvacManager; 50 private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>(); 51 52 /** 53 * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to 54 * match. 55 */ 56 private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() { 57 @Override 58 public void onChangeEvent(final CarPropertyValue val) { 59 try { 60 int areaId = val.getAreaId(); 61 int propertyId = val.getPropertyId(); 62 List<TemperatureView> temperatureViews = mTempComponents.get( 63 new HvacKey(propertyId, areaId)); 64 if (temperatureViews != null && !temperatureViews.isEmpty()) { 65 float value = (float) val.getValue(); 66 for (TemperatureView tempView : temperatureViews) { 67 tempView.setTemp(value); 68 } 69 } // else the data is not of interest 70 } catch (Exception e) { 71 // catch all so we don't take down the sysui if a new data type is 72 // introduced. 73 Log.e(TAG, "Failed handling hvac change event", e); 74 } 75 } 76 77 @Override 78 public void onErrorEvent(final int propertyId, final int zone) { 79 Log.d(TAG, "HVAC error event, propertyId: " + propertyId 80 + " zone: " + zone); 81 } 82 }; 83 84 private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { 85 if (!ready) { 86 return; 87 } 88 try { 89 mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE); 90 mHvacManager.registerCallback(mHardwareCallback); 91 initComponents(); 92 } catch (Exception e) { 93 Log.e(TAG, "Failed to correctly connect to HVAC", e); 94 } 95 }; 96 HvacController(Context context)97 public HvacController(Context context) { 98 mContext = context; 99 } 100 101 /** 102 * Create connection to the Car service. Note: call backs from the Car service 103 * ({@link CarHvacManager}) will happen on the same thread this method was called from. 104 */ connectToCarService()105 public void connectToCarService() { 106 ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) 107 .addListener(mCarServiceLifecycleListener); 108 } 109 110 /** 111 * Add component to list and initialize it if the connection is up. 112 */ addHvacTextView(TemperatureView temperatureView)113 public void addHvacTextView(TemperatureView temperatureView) { 114 115 HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId()); 116 if (!mTempComponents.containsKey(hvacKey)) { 117 mTempComponents.put(hvacKey, new ArrayList<>()); 118 } 119 mTempComponents.get(hvacKey).add(temperatureView); 120 initComponent(temperatureView); 121 } 122 initComponents()123 private void initComponents() { 124 Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator = 125 mTempComponents.entrySet().iterator(); 126 while (iterator.hasNext()) { 127 Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next(); 128 List<TemperatureView> temperatureViews = next.getValue(); 129 for (TemperatureView view : temperatureViews) { 130 initComponent(view); 131 } 132 } 133 } 134 initComponent(TemperatureView view)135 private void initComponent(TemperatureView view) { 136 int id = view.getPropertyId(); 137 int zone = view.getAreaId(); 138 139 try { 140 if (mHvacManager != null 141 && mHvacManager.isPropertyAvailable(HVAC_TEMPERATURE_DISPLAY_UNITS, 142 VEHICLE_AREA_TYPE_GLOBAL)) { 143 if (mHvacManager.getIntProperty(HVAC_TEMPERATURE_DISPLAY_UNITS, 144 VEHICLE_AREA_TYPE_GLOBAL) == VehicleUnit.FAHRENHEIT) { 145 view.setDisplayInFahrenheit(true); 146 } 147 148 } 149 if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) { 150 view.setTemp(Float.NaN); 151 return; 152 } 153 view.setTemp(mHvacManager.getFloatProperty(id, zone)); 154 } catch (Exception e) { 155 view.setTemp(Float.NaN); 156 Log.e(TAG, "Failed to get value from hvac service", e); 157 } 158 } 159 160 /** 161 * Removes all registered components. This is useful if you need to rebuild the UI since 162 * components self register. 163 */ removeAllComponents()164 public void removeAllComponents() { 165 mTempComponents.clear(); 166 } 167 168 /** 169 * Key for storing {@link TemperatureView}s in a hash map 170 */ 171 private static class HvacKey { 172 173 int mPropertyId; 174 int mAreaId; 175 HvacKey(int propertyId, int areaId)176 private HvacKey(int propertyId, int areaId) { 177 mPropertyId = propertyId; 178 mAreaId = areaId; 179 } 180 181 @Override equals(Object o)182 public boolean equals(Object o) { 183 if (this == o) return true; 184 if (o == null || getClass() != o.getClass()) return false; 185 HvacKey hvacKey = (HvacKey) o; 186 return mPropertyId == hvacKey.mPropertyId 187 && mAreaId == hvacKey.mAreaId; 188 } 189 190 @Override hashCode()191 public int hashCode() { 192 return Objects.hash(mPropertyId, mAreaId); 193 } 194 } 195 } 196