1 /*
2  * Copyright (C) 2015 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.google.android.car.kitchensink.hvac;
18 
19 import static java.lang.Integer.toHexString;
20 
21 import android.car.VehicleAreaSeat;
22 import android.car.VehicleAreaWindow;
23 import android.car.VehicleUnit;
24 import android.car.hardware.CarPropertyConfig;
25 import android.car.hardware.CarPropertyValue;
26 import android.car.hardware.hvac.CarHvacManager;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.util.Log;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.widget.Button;
34 import android.widget.RadioButton;
35 import android.widget.RadioGroup;
36 import android.widget.TextView;
37 import android.widget.Toast;
38 import android.widget.ToggleButton;
39 
40 import androidx.fragment.app.Fragment;
41 
42 import com.google.android.car.kitchensink.KitchenSinkActivity;
43 import com.google.android.car.kitchensink.R;
44 
45 import java.util.List;
46 
47 public class HvacTestFragment extends Fragment {
48     private static final boolean DBG = true;
49     private static final String TAG = HvacTestFragment.class.getSimpleName();
50     private static final float TEMP_STEP = 0.5f;
51 
52     private RadioButton mRbFanPositionFace;
53     private RadioButton mRbFanPositionFloor;
54     private RadioButton mRbFanPositionFaceAndFloor;
55     private RadioButton mRbFanPositionDefrost;
56     private RadioButton mRbFanPositionDefrostAndFloor;
57     private ToggleButton mTbAc;
58     private ToggleButton mTbAuto;
59     private ToggleButton mTbDefrostFront;
60     private ToggleButton mTbDefrostRear;
61     private ToggleButton mTbDual;
62     private ToggleButton mTbMaxAc;
63     private ToggleButton mTbMaxDefrost;
64     private ToggleButton mTbRecirc;
65     private ToggleButton mTbAutoRecirc;
66     private ToggleButton mTbTempDisplayUnit;
67     private ToggleButton mTbPower;
68     private Button mTbPowerAndAc;
69     private TextView mTvFanSpeed;
70     private TextView mTvDTemp;
71     private TextView mTvPTemp;
72     private TextView mTvOutsideTemp;
73     private int[] mCurFanSpeeds;
74     private int[] mMinFanSpeeds;
75     private int[] mMaxFanSpeeds;
76     private float mCurDTemp;
77     private float mCurPTemp;
78     private float mMinDTemp;
79     private float mMinPTemp;
80     private float mMaxDTemp;
81     private float mMaxPTemp;
82     private CarHvacManager mCarHvacManager;
83     private int mZoneForSetTempD;
84     private int mZoneForSetTempP;
85     private int[] mZonesForFanSpeed;
86     private List<CarPropertyConfig> mCarPropertyConfigs;
87     private View mHvacView;
88 
89     private final CarHvacManager.CarHvacEventCallback mHvacCallback =
90             new CarHvacManager.CarHvacEventCallback() {
91                 @Override
92                 public void onChangeEvent(final CarPropertyValue value) {
93                     int zones = value.getAreaId();
94                     switch (value.getPropertyId()) {
95                         case CarHvacManager.ID_OUTSIDE_AIR_TEMP:
96                             mTvOutsideTemp.setText(String.valueOf(value.getValue()));
97                             break;
98                         case CarHvacManager.ID_ZONED_DUAL_ZONE_ON:
99                             mTbDual.setChecked((boolean) value.getValue());
100                             break;
101                         case CarHvacManager.ID_ZONED_AC_ON:
102                             mTbAc.setChecked((boolean) value.getValue());
103                             break;
104                         case CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON:
105                             mTbAuto.setChecked((boolean) value.getValue());
106                             break;
107                         case CarHvacManager.ID_ZONED_FAN_DIRECTION:
108                             switch ((int) value.getValue()) {
109                                 case CarHvacManager.FAN_DIRECTION_FACE:
110                                     mRbFanPositionFace.setChecked(true);
111                                     break;
112                                 case CarHvacManager.FAN_DIRECTION_FLOOR:
113                                     mRbFanPositionFloor.setChecked(true);
114                                     break;
115                                 case (CarHvacManager.FAN_DIRECTION_FACE |
116                                         CarHvacManager.FAN_DIRECTION_FLOOR):
117                                     mRbFanPositionFaceAndFloor.setChecked(true);
118                                     break;
119                                 case CarHvacManager.FAN_DIRECTION_DEFROST:
120                                     mRbFanPositionDefrost.setChecked(true);
121                                     break;
122                                 case (CarHvacManager.FAN_DIRECTION_DEFROST |
123                                         CarHvacManager.FAN_DIRECTION_FLOOR):
124                                     mRbFanPositionDefrostAndFloor.setChecked(true);
125                                     break;
126                                 default:
127                                     if (DBG) {
128                                         Log.e(TAG, "Unknown fan position: " + value.getValue());
129                                     }
130                                     break;
131                             }
132                             break;
133                         case CarHvacManager.ID_ZONED_MAX_AC_ON:
134                             mTbMaxAc.setChecked((boolean) value.getValue());
135                             break;
136                         case CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON:
137                             mTbRecirc.setChecked((boolean) value.getValue());
138                             break;
139                         case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT:
140                             for (int i = 0; i < mZonesForFanSpeed.length; i++) {
141                                 if (zones == mZonesForFanSpeed[i]) {
142                                     mCurFanSpeeds[i] = (int) value.getValue();
143                                     if (i == 0) {
144                                         mTvFanSpeed.setText(String.valueOf(mCurFanSpeeds[i]));
145                                     }
146                                     break;
147                                 }
148                             }
149                             break;
150                         case CarHvacManager.ID_ZONED_TEMP_SETPOINT:
151                             if ((zones & mZoneForSetTempD) == mZoneForSetTempD) {
152                                 mCurDTemp = (float) value.getValue();
153                                 mTvDTemp.setText(String.valueOf(mCurDTemp));
154                             }
155                             if ((zones & mZoneForSetTempP) == mZoneForSetTempP) {
156                                 mCurPTemp = (float) value.getValue();
157                                 mTvPTemp.setText(String.valueOf(mCurPTemp));
158                             }
159                             break;
160                         case CarHvacManager.ID_ZONED_MAX_DEFROST_ON:
161                             mTbMaxDefrost.setChecked((boolean) value.getValue());
162                             break;
163                         case CarHvacManager.ID_WINDOW_DEFROSTER_ON:
164                             if ((zones & VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD)
165                                     == VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD) {
166                                 mTbDefrostFront.setChecked((boolean) value.getValue());
167                             }
168                             if ((zones & VehicleAreaWindow.WINDOW_REAR_WINDSHIELD)
169                                     == VehicleAreaWindow.WINDOW_REAR_WINDSHIELD) {
170                                 mTbDefrostRear.setChecked((boolean) value.getValue());
171                             }
172                             break;
173                         case CarHvacManager.ID_ZONED_HVAC_AUTO_RECIRC_ON:
174                             mTbAutoRecirc.setChecked((boolean) value.getValue());
175                             break;
176                         case CarHvacManager.ID_TEMPERATURE_DISPLAY_UNITS:
177                             mTbTempDisplayUnit.setChecked(
178                                     ((Integer) value.getValue()).equals(VehicleUnit.FAHRENHEIT));
179                             break;
180                         case CarHvacManager.ID_ZONED_HVAC_POWER_ON:
181                             mTbPower.setChecked((boolean) value.getValue());
182                             break;
183                         default:
184                             Log.d(TAG, "onChangeEvent(): unknown property id = " + value
185                                     .getPropertyId());
186                     }
187                 }
188 
189                 @Override
190                 public void onErrorEvent(final int propertyId, final int zone) {
191                     Log.w(TAG, "Error:  propertyId=0x" + toHexString(propertyId)
192                             + ", zone=0x" + toHexString(zone));
193                 }
194             };
195 
196     @Override
onDestroy()197     public void onDestroy() {
198         super.onDestroy();
199         mCarHvacManager.unregisterCallback(mHvacCallback);
200     }
201 
202     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance)203     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
204         mHvacView = inflater.inflate(R.layout.hvac_test, container, false);
205         final Runnable r = () -> {
206             mCarHvacManager = ((KitchenSinkActivity) getActivity()).getHvacManager();
207             mCarHvacManager.registerCallback(mHvacCallback);
208             mCarPropertyConfigs = mCarHvacManager.getPropertyList();
209             for (CarPropertyConfig prop : mCarPropertyConfigs) {
210                 int propId = prop.getPropertyId();
211 
212                 if (DBG) {
213                     Log.d(TAG, prop.toString());
214                 }
215 
216                 switch (propId) {
217                     case CarHvacManager.ID_OUTSIDE_AIR_TEMP:
218                         // Nothing is done in this case.
219                         break;
220                     case CarHvacManager.ID_ZONED_DUAL_ZONE_ON:
221                         configureDualOn(mHvacView, prop);
222                         break;
223                     case CarHvacManager.ID_ZONED_AC_ON:
224                         configureAcOn(mHvacView, prop);
225                         break;
226                     case CarHvacManager.ID_ZONED_FAN_DIRECTION:
227                         configureFanPosition(mHvacView, prop);
228                         break;
229                     case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT:
230                         configureFanSpeed(mHvacView, prop);
231                         break;
232                     case CarHvacManager.ID_ZONED_TEMP_SETPOINT:
233                         configureTempSetpoint(mHvacView, prop);
234                         break;
235                     case CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON:
236                         configureAutoModeOn(mHvacView, prop);
237                         break;
238                     case CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON:
239                         configureRecircOn(mHvacView, prop);
240                         break;
241                     case CarHvacManager.ID_ZONED_MAX_AC_ON:
242                         configureMaxAcOn(mHvacView, prop);
243                         break;
244                     case CarHvacManager.ID_ZONED_MAX_DEFROST_ON:
245                         configureMaxDefrostOn(mHvacView, prop);
246                         break;
247                     case CarHvacManager.ID_WINDOW_DEFROSTER_ON:
248                         configureDefrosterOn(mHvacView, prop);
249                         break;
250                     case CarHvacManager.ID_ZONED_HVAC_AUTO_RECIRC_ON:
251                         configureAutoRecircOn(mHvacView, prop);
252                         break;
253                     case CarHvacManager.ID_TEMPERATURE_DISPLAY_UNITS:
254                         configureTempDisplayUnit(mHvacView, prop);
255                         break;
256                     case CarHvacManager.ID_ZONED_HVAC_POWER_ON:
257                         configurePowerOn(mHvacView, prop);
258                         configurePowerAndAcOn(mHvacView, prop);
259                         break;
260                     default:
261                         Log.w(TAG, "propertyId " + propId + " is not handled");
262                         break;
263                 }
264             }
265 
266             mTvFanSpeed = (TextView) mHvacView.findViewById(R.id.tvFanSpeed);
267             mTvFanSpeed.setText(String.valueOf(mCurFanSpeeds[0]));
268             mTvDTemp = (TextView) mHvacView.findViewById(R.id.tvDTemp);
269             mTvDTemp.setText(String.valueOf(mCurDTemp));
270             mTvPTemp = (TextView) mHvacView.findViewById(R.id.tvPTemp);
271             mTvPTemp.setText(String.valueOf(mCurPTemp));
272             mTvOutsideTemp = (TextView) mHvacView.findViewById(R.id.tvOutsideTemp);
273             mTvOutsideTemp.setText("N/A");
274         };
275 
276         ((KitchenSinkActivity) getActivity()).requestRefreshManager(r,
277                 new Handler(getContext().getMainLooper()));
278 
279         if (DBG) {
280             Log.d(TAG, "Starting HvacTestFragment");
281         }
282 
283         return mHvacView;
284     }
285 
configureDualOn(View v, CarPropertyConfig prop)286     private void configureDualOn(View v, CarPropertyConfig prop) {
287         int[] areaIds = prop.getAreaIds();
288         mTbDual = (ToggleButton) v.findViewById(R.id.tbDual);
289         mTbDual.setEnabled(true);
290 
291         mTbDual.setOnClickListener(view -> {
292             for (int i = 0; i < areaIds.length; i++) {
293                 setBooleanProperty(CarHvacManager.ID_ZONED_DUAL_ZONE_ON, areaIds[i],
294                         mTbDual.isChecked());
295             }
296         });
297     }
298 
configureAcOn(View v, CarPropertyConfig prop)299     private void configureAcOn(View v, CarPropertyConfig prop) {
300         int[] areaIds = prop.getAreaIds();
301         mTbAc = (ToggleButton) v.findViewById(R.id.tbAc);
302         mTbAc.setEnabled(true);
303 
304         mTbAc.setOnClickListener(view -> {
305             for (int i = 0; i < areaIds.length; i++) {
306                 setBooleanProperty(CarHvacManager.ID_ZONED_AC_ON, areaIds[i], mTbAc.isChecked());
307             }
308         });
309     }
310 
configureAutoModeOn(View v, CarPropertyConfig prop)311     private void configureAutoModeOn(View v, CarPropertyConfig prop) {
312         int[] areaIds = prop.getAreaIds();
313         mTbAuto = (ToggleButton) v.findViewById(R.id.tbAuto);
314         mTbAuto.setEnabled(true);
315 
316         mTbAuto.setOnClickListener(view -> {
317             for (int i = 0; i < areaIds.length; i++) {
318                 setBooleanProperty(CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON, areaIds[i],
319                         mTbAuto.isChecked());
320             }
321         });
322     }
323 
configureFanPosition(View v, CarPropertyConfig prop)324     private void configureFanPosition(View v, CarPropertyConfig prop) {
325         int[] areaIds = prop.getAreaIds();
326         RadioGroup rg = (RadioGroup) v.findViewById(R.id.rgFanPosition);
327         rg.setOnCheckedChangeListener((group, checkedId) -> {
328             int position;
329             switch (checkedId) {
330                 case R.id.rbPositionFace:
331                     position = CarHvacManager.FAN_DIRECTION_FACE;
332                     break;
333                 case R.id.rbPositionFloor:
334                     position = CarHvacManager.FAN_DIRECTION_FLOOR;
335                     break;
336                 case R.id.rbPositionFaceAndFloor:
337                     position = (CarHvacManager.FAN_DIRECTION_FACE |
338                             CarHvacManager.FAN_DIRECTION_FLOOR);
339                     break;
340                 case R.id.rbPositionDefrost:
341                     position = CarHvacManager.FAN_DIRECTION_DEFROST;
342                     break;
343                 case R.id.rbPositionDefrostAndFloor:
344                     position = (CarHvacManager.FAN_DIRECTION_DEFROST |
345                             CarHvacManager.FAN_DIRECTION_FLOOR);
346                     break;
347                 default:
348                     throw new IllegalStateException("Unexpected fan position: " + checkedId);
349             }
350             try {
351                 for (int i = 0; i < areaIds.length; i++) {
352                     if (mCarHvacManager.isPropertyAvailable(CarHvacManager.ID_ZONED_FAN_DIRECTION,
353                             mZonesForFanSpeed[i])) {
354                         mCarHvacManager.setIntProperty(CarHvacManager.ID_ZONED_FAN_DIRECTION,
355                                 areaIds[i], position);
356                     }
357                 }
358             } catch (IllegalStateException e) {
359                 Log.e(TAG, "Failed to set HVAC integer property", e);
360                 Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
361             }
362         });
363 
364         mRbFanPositionFace = (RadioButton) v.findViewById(R.id.rbPositionFace);
365         mRbFanPositionFace.setClickable(true);
366         mRbFanPositionFloor = (RadioButton) v.findViewById(R.id.rbPositionFloor);
367         mRbFanPositionFloor.setClickable(true);
368         mRbFanPositionFaceAndFloor = (RadioButton) v.findViewById(R.id.rbPositionFaceAndFloor);
369         mRbFanPositionFaceAndFloor.setClickable(true);
370         mRbFanPositionDefrost = (RadioButton) v.findViewById(R.id.rbPositionDefrost);
371         mRbFanPositionDefrost.setClickable(true);
372         mRbFanPositionDefrostAndFloor = (RadioButton) v.findViewById(
373                 R.id.rbPositionDefrostAndFloor);
374         mRbFanPositionDefrostAndFloor.setClickable(true);
375     }
376 
configureFanSpeed(View v, CarPropertyConfig prop)377     private void configureFanSpeed(View v, CarPropertyConfig prop) {
378         mZonesForFanSpeed = prop.getAreaIds();
379         mCurFanSpeeds = new int[mZonesForFanSpeed.length];
380         mMaxFanSpeeds = new int[mZonesForFanSpeed.length];
381         mMinFanSpeeds = new int[mZonesForFanSpeed.length];
382 
383         for (int i = 0; i < mZonesForFanSpeed.length; i++) {
384             mMaxFanSpeeds[i] = (Integer) prop.getMaxValue(mZonesForFanSpeed[i]);
385             mMinFanSpeeds[i] = (Integer) prop.getMinValue(mZonesForFanSpeed[i]);
386         }
387 
388         try {
389             for (int i = 0; i < mZonesForFanSpeed.length; i++) {
390                 mCurFanSpeeds[i] = mCarHvacManager.getIntProperty(
391                         CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, mZonesForFanSpeed[i]);
392             }
393         } catch (IllegalStateException e) {
394             Log.e(TAG, "Failed to get HVAC int property", e);
395         }
396 
397         Button btnFanSpeedUp = (Button) v.findViewById(R.id.btnFanSpeedUp);
398         btnFanSpeedUp.setEnabled(true);
399         btnFanSpeedUp.setOnClickListener(view -> changeFanSpeed(1));
400 
401         Button btnFanSpeedDn = (Button) v.findViewById(R.id.btnFanSpeedDn);
402         btnFanSpeedDn.setEnabled(true);
403         btnFanSpeedDn.setOnClickListener(view -> changeFanSpeed(-1));
404     }
405 
changeFanSpeed(int change)406     private void changeFanSpeed(int change) {
407         for (int i = 0; i < mZonesForFanSpeed.length; i++) {
408             int targetSpeed = mCurFanSpeeds[i] + change;
409             if (mMinFanSpeeds[i] <= targetSpeed && targetSpeed <= mMaxFanSpeeds[i]) {
410                 mCurFanSpeeds[i] = targetSpeed;
411                 if (i == 0) {
412                     mTvFanSpeed.setText(String.valueOf(mCurFanSpeeds[i]));
413                 }
414                 try {
415                     if (mCarHvacManager.isPropertyAvailable(
416                             CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, mZonesForFanSpeed[i])) {
417                         mCarHvacManager.setIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT,
418                                 mZonesForFanSpeed[i], mCurFanSpeeds[i]);
419                     }
420                 } catch (IllegalStateException e) {
421                     Log.e(TAG, "Failed to set HVAC int property", e);
422                     Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
423                 }
424             }
425         }
426     }
427 
configureTempSetpoint(View v, CarPropertyConfig prop)428     private void configureTempSetpoint(View v, CarPropertyConfig prop) {
429 
430         mZoneForSetTempD = 0;
431         if (prop.hasArea(VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_2_LEFT
432                 | VehicleAreaSeat.SEAT_ROW_2_CENTER)) {
433             mZoneForSetTempD = VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_2_LEFT
434                     | VehicleAreaSeat.SEAT_ROW_2_CENTER;
435         }
436         mZoneForSetTempP = 0;
437         if (prop.hasArea(VehicleAreaSeat.SEAT_ROW_1_RIGHT | VehicleAreaSeat.SEAT_ROW_2_RIGHT)) {
438             mZoneForSetTempP = VehicleAreaSeat.SEAT_ROW_1_RIGHT | VehicleAreaSeat.SEAT_ROW_2_RIGHT;
439         }
440         int[] areas = prop.getAreaIds();
441         if (mZoneForSetTempD == 0 && areas.length > 1) {
442             mZoneForSetTempD = areas[0];
443         }
444         if (mZoneForSetTempP == 0 && areas.length > 2) {
445             mZoneForSetTempP = areas[1];
446         }
447         Button btnDTempUp = (Button) v.findViewById(R.id.btnDTempUp);
448         if (mZoneForSetTempD != 0) {
449             mMaxDTemp = (Float) prop.getMaxValue(mZoneForSetTempD);
450             mMinDTemp = (Float) prop.getMinValue(mZoneForSetTempD);
451 
452 
453             try {
454                 mCurDTemp = mCarHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT,
455                         mZoneForSetTempD);
456                 if (mCurDTemp < mMinDTemp) {
457                     mCurDTemp = mMinDTemp;
458                 } else if (mCurDTemp > mMaxDTemp) {
459                     mCurDTemp = mMaxDTemp;
460                 }
461             } catch (IllegalStateException e) {
462                 Log.e(TAG, "Failed to get HVAC zoned temp property", e);
463             }
464             btnDTempUp.setEnabled(true);
465             btnDTempUp.setOnClickListener(view -> changeDriverTemperature(TEMP_STEP));
466 
467             Button btnDTempDn = (Button) v.findViewById(R.id.btnDTempDn);
468             btnDTempDn.setEnabled(true);
469             btnDTempDn.setOnClickListener(view -> changeDriverTemperature(-TEMP_STEP));
470         } else {
471             btnDTempUp.setEnabled(false);
472         }
473 
474         Button btnPTempUp = (Button) v.findViewById(R.id.btnPTempUp);
475         if (mZoneForSetTempP != 0) {
476             mMaxPTemp = (Float) prop.getMaxValue(mZoneForSetTempP);
477             mMinPTemp = (Float) prop.getMinValue(mZoneForSetTempP);
478 
479             try {
480                 mCurPTemp = mCarHvacManager.getFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT,
481                         mZoneForSetTempP);
482                 if (mCurPTemp < mMinPTemp) {
483                     mCurPTemp = mMinPTemp;
484                 } else if (mCurPTemp > mMaxPTemp) {
485                     mCurPTemp = mMaxPTemp;
486                 }
487             } catch (IllegalStateException e) {
488                 Log.e(TAG, "Failed to get HVAC zoned temp property", e);
489             }
490             btnPTempUp.setEnabled(true);
491             btnPTempUp.setOnClickListener(view -> changePassengerTemperature(TEMP_STEP));
492 
493             Button btnPTempDn = (Button) v.findViewById(R.id.btnPTempDn);
494             btnPTempDn.setEnabled(true);
495             btnPTempDn.setOnClickListener(view -> changePassengerTemperature(-TEMP_STEP));
496         } else {
497             btnPTempUp.setEnabled(false);
498         }
499 
500         Button btnATempUp = (Button) v.findViewById(R.id.btnATempUp);
501         if (mZoneForSetTempD != 0 && mZoneForSetTempP != 0) {
502             btnATempUp.setEnabled(true);
503             btnATempUp.setOnClickListener(view -> {
504                 changeDriverTemperature(TEMP_STEP);
505                 changePassengerTemperature(TEMP_STEP);
506             });
507 
508             Button btnATempDn = (Button) v.findViewById(R.id.btnATempDn);
509             btnATempDn.setEnabled(true);
510             btnATempDn.setOnClickListener(view -> {
511                 changeDriverTemperature(-TEMP_STEP);
512                 changePassengerTemperature(-TEMP_STEP);
513             });
514         } else {
515             btnATempUp.setEnabled(false);
516         }
517     }
518 
changeDriverTemperature(float tempStep)519     private void changeDriverTemperature(float tempStep) {
520         float targetTemp = mCurDTemp + tempStep;
521         if (mMinDTemp < targetTemp && targetTemp < mMaxDTemp) {
522             mCurDTemp = targetTemp;
523             mTvDTemp.setText(String.valueOf(mCurDTemp));
524             setFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, mZoneForSetTempD, mCurDTemp);
525         }
526     }
527 
changePassengerTemperature(float tempStep)528     private void changePassengerTemperature(float tempStep) {
529         float targetTemp = mCurPTemp + tempStep;
530         if (mMinPTemp < targetTemp && targetTemp < mMaxPTemp) {
531             mCurPTemp = targetTemp;
532             mTvPTemp.setText(String.valueOf(mCurPTemp));
533             setFloatProperty(CarHvacManager.ID_ZONED_TEMP_SETPOINT, mZoneForSetTempP, mCurPTemp);
534         }
535     }
536 
configureDefrosterOn(View v, CarPropertyConfig prop1)537     private void configureDefrosterOn(View v, CarPropertyConfig prop1) {
538         if (prop1.hasArea(VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD)) {
539             mTbDefrostFront = (ToggleButton) v.findViewById(R.id.tbDefrostFront);
540             mTbDefrostFront.setEnabled(true);
541             mTbDefrostFront.setOnClickListener(view -> {
542                 setBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON,
543                         VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD, mTbDefrostFront.isChecked());
544             });
545         }
546         if (prop1.hasArea(VehicleAreaWindow.WINDOW_REAR_WINDSHIELD)) {
547             mTbDefrostRear = (ToggleButton) v.findViewById(R.id.tbDefrostRear);
548             mTbDefrostRear.setEnabled(true);
549             mTbDefrostRear.setOnClickListener(view -> {
550                 setBooleanProperty(CarHvacManager.ID_WINDOW_DEFROSTER_ON,
551                         VehicleAreaWindow.WINDOW_REAR_WINDSHIELD, mTbDefrostRear.isChecked());
552             });
553         }
554     }
555 
configureRecircOn(View v, CarPropertyConfig prop)556     private void configureRecircOn(View v, CarPropertyConfig prop) {
557         int[] areaIds = prop.getAreaIds();
558         mTbRecirc = (ToggleButton) v.findViewById(R.id.tbRecirc);
559         mTbRecirc.setEnabled(true);
560 
561         mTbRecirc.setOnClickListener(view -> {
562             for (int i = 0; i < areaIds.length; i++) {
563                 setBooleanProperty(CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON, areaIds[i],
564                         mTbRecirc.isChecked());
565             }
566         });
567     }
568 
configureMaxAcOn(View v, CarPropertyConfig prop)569     private void configureMaxAcOn(View v, CarPropertyConfig prop) {
570         int[] areaIds = prop.getAreaIds();
571         mTbMaxAc = (ToggleButton) v.findViewById(R.id.tbMaxAc);
572         mTbMaxAc.setEnabled(true);
573 
574         mTbMaxAc.setOnClickListener(view -> {
575             for (int i = 0; i < areaIds.length; i++) {
576                 setBooleanProperty(CarHvacManager.ID_ZONED_MAX_AC_ON, areaIds[i],
577                         mTbMaxAc.isChecked());
578             }
579         });
580     }
581 
configureMaxDefrostOn(View v, CarPropertyConfig prop)582     private void configureMaxDefrostOn(View v, CarPropertyConfig prop) {
583         int[] areaIds = prop.getAreaIds();
584         mTbMaxDefrost = (ToggleButton) v.findViewById(R.id.tbMaxDefrost);
585         mTbMaxDefrost.setEnabled(true);
586 
587         mTbMaxDefrost.setOnClickListener(view -> {
588             for (int i = 0; i < areaIds.length; i++) {
589                 setBooleanProperty(CarHvacManager.ID_ZONED_MAX_DEFROST_ON, areaIds[i],
590                         mTbMaxDefrost.isChecked());
591             }
592         });
593     }
594 
configureAutoRecircOn(View v, CarPropertyConfig prop)595     private void configureAutoRecircOn(View v, CarPropertyConfig prop) {
596         int[] areaIds = prop.getAreaIds();
597         mTbAutoRecirc = (ToggleButton) v.findViewById(R.id.tbAutoRecirc);
598         mTbAutoRecirc.setEnabled(true);
599 
600         mTbAutoRecirc.setOnClickListener(view -> {
601             for (int i = 0; i < areaIds.length; i++) {
602                 setBooleanProperty(CarHvacManager.ID_ZONED_HVAC_AUTO_RECIRC_ON, areaIds[i],
603                         mTbAutoRecirc.isChecked());
604             }
605         });
606     }
607 
configureTempDisplayUnit(View v, CarPropertyConfig prop)608     private void configureTempDisplayUnit(View v, CarPropertyConfig prop) {
609         int areaId = prop.getFirstAndOnlyAreaId();
610         mTbTempDisplayUnit = (ToggleButton) v.findViewById(R.id.tbTempDisplayUnit);
611         mTbTempDisplayUnit.setEnabled(true);
612 
613         mTbTempDisplayUnit.setOnClickListener(view -> {
614             int unit =
615                     (mTbTempDisplayUnit.isChecked() ? VehicleUnit.FAHRENHEIT : VehicleUnit.CELSIUS);
616             setIntProperty(CarHvacManager.ID_TEMPERATURE_DISPLAY_UNITS, areaId, unit);
617         });
618     }
619 
configurePowerOn(View v, CarPropertyConfig prop)620     private void configurePowerOn(View v, CarPropertyConfig prop) {
621         int[] areaIds = prop.getAreaIds();
622         mTbPower = (ToggleButton) v.findViewById(R.id.tbPower);
623         mTbPower.setEnabled(true);
624 
625         mTbPower.setOnClickListener(view -> {
626             for (int i = 0; i < areaIds.length; i++) {
627                 setBooleanProperty(CarHvacManager.ID_ZONED_HVAC_POWER_ON, areaIds[i],
628                         mTbPower.isChecked());
629             }
630         });
631     }
632 
configurePowerAndAcOn(View v, CarPropertyConfig prop)633     private void configurePowerAndAcOn(View v, CarPropertyConfig prop) {
634         int[] areaIds = prop.getAreaIds();
635         mTbPowerAndAc = (Button) v.findViewById(R.id.tbPowerAndAc);
636         mTbPowerAndAc.setEnabled(true);
637 
638         mTbPowerAndAc.setOnClickListener(view -> {
639             for (int i = 0; i < areaIds.length; i++) {
640                 setBooleanProperty(CarHvacManager.ID_ZONED_HVAC_POWER_ON, areaIds[i], true);
641                 setBooleanProperty(CarHvacManager.ID_ZONED_AC_ON, areaIds[i], true);
642             }
643         });
644     }
645 
setBooleanProperty(int propertyId, int areaId, boolean value)646     private void setBooleanProperty(int propertyId, int areaId, boolean value) {
647         try {
648             mCarHvacManager.setBooleanProperty(propertyId, areaId, value);
649         } catch (IllegalStateException e) {
650             Log.e(TAG, "Failed to set boolean property", e);
651             Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
652         }
653     }
654 
setIntProperty(int propertyId, int areaId, int value)655     private void setIntProperty(int propertyId, int areaId, int value) {
656         try {
657             mCarHvacManager.setIntProperty(propertyId, areaId, value);
658         } catch (IllegalStateException e) {
659             Log.e(TAG, "Failed to set int property", e);
660             Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
661         }
662     }
663 
setFloatProperty(int propertyId, int areaId, float value)664     private void setFloatProperty(int propertyId, int areaId, float value) {
665         try {
666             mCarHvacManager.setFloatProperty(propertyId, areaId, value);
667         } catch (IllegalStateException e) {
668             Log.e(TAG, "Failed to set float property", e);
669             Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
670         }
671     }
672 }
673