1 /*
2  * Copyright (C) 2021 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.VehiclePropertyIds.HVAC_SEAT_TEMPERATURE;
20 
21 import android.car.hardware.CarPropertyValue;
22 import android.content.Context;
23 import android.content.res.TypedArray;
24 import android.graphics.drawable.Drawable;
25 import android.os.Build;
26 import android.util.AttributeSet;
27 import android.util.Log;
28 import android.util.SparseArray;
29 import android.widget.ImageButton;
30 
31 import androidx.annotation.ArrayRes;
32 
33 import com.android.systemui.R;
34 
35 /**
36  * An image button that allows for multiple seat heating/cooling states based on
37  * {@link R.integer.hvac_seat_heat_level_count}.
38  */
39 public class SeatTemperatureLevelButton extends ImageButton implements HvacView {
40     private static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG;
41     private static final String TAG = "SeatTemperatureLevelButton";
42 
43     private static final int INVALID_ID = -1;
44     private static final int HEATING = 1;
45     private static final int COOLING = -1;
46 
47     private final SparseArray<Drawable> mIcons = new SparseArray<>();
48 
49     private int mAreaId;
50     private HvacPropertySetter mHvacPropertySetter;
51     private Drawable mCurrentIcon;
52     private int mCurrentLevel;
53     private int mTotalLevelCount;
54     private int mTemperatureLevelType;
55 
SeatTemperatureLevelButton(Context context)56     public SeatTemperatureLevelButton(Context context) {
57         super(context);
58     }
59 
SeatTemperatureLevelButton(Context context, AttributeSet attrs)60     public SeatTemperatureLevelButton(Context context, AttributeSet attrs) {
61         super(context, attrs);
62         parseAttributes(attrs);
63     }
64 
SeatTemperatureLevelButton(Context context, AttributeSet attrs, int defStyleAttr)65     public SeatTemperatureLevelButton(Context context, AttributeSet attrs, int defStyleAttr) {
66         super(context, attrs, defStyleAttr);
67         parseAttributes(attrs);
68     }
69 
70     @Override
onFinishInflate()71     public void onFinishInflate() {
72         super.onFinishInflate();
73         setOnClickListener(v -> {
74             if (mHvacPropertySetter != null) {
75                 mHvacPropertySetter.setHvacProperty(getHvacPropertyToView(), mAreaId,
76                         mTemperatureLevelType * (mCurrentLevel + 1) % mTotalLevelCount);
77             }
78         });
79         setOnLongClickListener(v -> {
80             if (mHvacPropertySetter != null) {
81                 mHvacPropertySetter.setHvacProperty(getHvacPropertyToView(), mAreaId,
82                         mTemperatureLevelType * (mCurrentLevel == 0 ? mTotalLevelCount - 1 : 0));
83                 return true;
84             }
85             return false;
86         });
87         updateIcon();
88     }
89 
90     @Override
setHvacPropertySetter(HvacPropertySetter hvacPropertySetter)91     public void setHvacPropertySetter(HvacPropertySetter hvacPropertySetter) {
92         mHvacPropertySetter = hvacPropertySetter;
93     }
94 
95     @Override
onPropertyChanged(CarPropertyValue value)96     public void onPropertyChanged(CarPropertyValue value) {
97         if (value == null) {
98             if (DEBUG) {
99                 Log.w(TAG, "onPropertyChanged: received null value");
100             }
101             return;
102         }
103 
104         if (DEBUG) {
105             Log.w(TAG, "onPropertyChanged: property id: " + value.getPropertyId());
106             Log.w(TAG, "onPropertyChanged: area id: " + value.getAreaId());
107             Log.w(TAG, "onPropertyChanged: value: " + value.getValue());
108         }
109 
110         if (value.getPropertyId() == getHvacPropertyToView() && value.getAreaId() == getAreaId()) {
111             mCurrentLevel = (int) value.getValue();
112             if (mTemperatureLevelType == COOLING && mCurrentLevel > 0) {
113                 mCurrentLevel = 0;
114             } else if (mTemperatureLevelType == HEATING && mCurrentLevel < 0) {
115                 mCurrentLevel = 0;
116             }
117 
118             mCurrentLevel = Math.abs(mCurrentLevel);
119 
120             mCurrentIcon = mIcons.get(mCurrentLevel);
121             updateIcon();
122         }
123     }
124 
125     @Override
getHvacPropertyToView()126     public @HvacController.HvacProperty Integer getHvacPropertyToView() {
127         return HVAC_SEAT_TEMPERATURE;
128     }
129 
130     @Override
getAreaId()131     public @HvacController.AreaId Integer getAreaId() {
132         return mAreaId;
133     }
134 
135     @Override
onHvacTemperatureUnitChanged(boolean usesFahrenheit)136     public void onHvacTemperatureUnitChanged(boolean usesFahrenheit) {
137         // no-op.
138     }
139 
140     @Override
onLocaleListChanged()141     public void onLocaleListChanged() {
142         // no-op.
143     }
144 
updateIcon()145     private void updateIcon() {
146         mContext.getMainThreadHandler().post(() -> {
147             setSelected(mCurrentLevel != 0);
148             setImageDrawable(mCurrentIcon);
149         });
150     }
151 
parseAttributes(AttributeSet attrs)152     private void parseAttributes(AttributeSet attrs) {
153         mIcons.clear();
154 
155         mTotalLevelCount = mContext.getResources().getInteger(R.integer.hvac_seat_heat_level_count);
156 
157         TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.HvacView);
158         mAreaId = typedArray.getInt(R.styleable.HvacView_hvacAreaId, INVALID_ID);
159         typedArray.recycle();
160 
161         typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.SeatTemperatureLevelButton);
162         mTemperatureLevelType = typedArray.getInt(
163                 R.styleable.SeatTemperatureLevelButton_seatTemperatureType, HEATING);
164 
165         @ArrayRes int drawableListRes = typedArray.getResourceId(
166                 R.styleable.SeatTemperatureLevelButton_seatTemperatureIconDrawableList,
167                 R.array.hvac_heated_seat_default_icons);
168 
169         TypedArray seatTemperatureIcons = mContext.getResources().obtainTypedArray(drawableListRes);
170         if (seatTemperatureIcons.length() != mTotalLevelCount) {
171             throw new IllegalArgumentException(
172                     "R.styeable.SeatHeatLevelButton_seatHeaterIconDrawableList should have the "
173                             + "same length as R.integer.hvac_seat_heat_level_count");
174         }
175 
176         for (int i = 0; i < mTotalLevelCount; i++) {
177             mIcons.set(i, seatTemperatureIcons.getDrawable(i));
178         }
179         seatTemperatureIcons.recycle();
180         typedArray.recycle();
181     }
182 }
183