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