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.referenceui;
18 
19 import static android.car.VehiclePropertyIds.HVAC_AUTO_ON;
20 import static android.car.VehiclePropertyIds.HVAC_FAN_SPEED;
21 import static android.car.VehiclePropertyIds.HVAC_POWER_ON;
22 
23 import android.car.hardware.CarPropertyValue;
24 import android.content.Context;
25 import android.content.res.Resources;
26 import android.graphics.Color;
27 import android.graphics.drawable.GradientDrawable;
28 import android.util.AttributeSet;
29 import android.widget.RelativeLayout;
30 import android.widget.TextView;
31 
32 import com.android.systemui.R;
33 import com.android.systemui.car.hvac.HvacController;
34 import com.android.systemui.car.hvac.HvacPropertySetter;
35 import com.android.systemui.car.hvac.HvacView;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 public class FanSpeedBar extends RelativeLayout implements HvacView {
41 
42     private static final int BAR_SEGMENT_ANIMATION_DELAY_MS = 50;
43     private static final int BAR_SEGMENT_ANIMATION_PERIOD_MS = 100;
44 
45     private HvacPropertySetter mHvacPropertySetter;
46 
47     private int mHvacGlobalAreaId;
48 
49     private int mButtonActiveTextColor;
50     private int mButtonInactiveTextColor;
51 
52     private int mFanOffActiveBgColor;
53     private int mFanMaxActiveBgColor;
54 
55     private float mCornerRadius;
56 
57     private TextView mMaxButton;
58     private TextView mOffButton;
59 
60     private FanSpeedBarSegment mFanSpeed1;
61     private FanSpeedBarSegment mFanSpeed2;
62     private FanSpeedBarSegment mFanSpeed3;
63     private FanSpeedBarSegment mFanSpeed4;
64 
65     private boolean mPowerOn;
66     private boolean mAutoOn;
67 
68     private float mOnAlpha;
69     private float mOffAlpha;
70 
71     private final List<FanSpeedBarSegment> mFanSpeedButtons = new ArrayList<>();
72 
FanSpeedBar(Context context)73     public FanSpeedBar(Context context) {
74         super(context);
75         init();
76     }
77 
FanSpeedBar(Context context, AttributeSet attrs)78     public FanSpeedBar(Context context, AttributeSet attrs) {
79         super(context, attrs);
80         init();
81     }
82 
FanSpeedBar(Context context, AttributeSet attrs, int defStyleAttr)83     public FanSpeedBar(Context context, AttributeSet attrs, int defStyleAttr) {
84         super(context, attrs, defStyleAttr);
85         init();
86     }
87 
init()88     private void init() {
89         inflate(getContext(), R.layout.fan_speed, this);
90 
91         Resources res = getContext().getResources();
92         // The fanspeed bar is set as height 72dp to match min tap target size. However it is
93         // inset by fan speed inset to make it appear thinner.
94         int barHeight = res.getDimensionPixelSize(R.dimen.hvac_fan_speed_bar_height);
95         int insetHeight = res.getDimensionPixelSize(R.dimen.hvac_fan_speed_bar_vertical_inset);
96         mCornerRadius = (barHeight - 2 * insetHeight) / 2;
97 
98         mFanOffActiveBgColor = res.getColor(R.color.hvac_fanspeed_off_active_bg);
99 
100         mButtonActiveTextColor = res.getColor(R.color.hvac_fanspeed_off_active_text_color);
101         mButtonInactiveTextColor = res.getColor(R.color.hvac_fanspeed_off_inactive_text_color);
102         mFanMaxActiveBgColor = res.getColor(R.color.hvac_fanspeed_segment_color);
103         mHvacGlobalAreaId = res.getInteger(R.integer.hvac_global_area_id);
104     }
105 
106     @Override
setHvacPropertySetter(HvacPropertySetter hvacPropertySetter)107     public void setHvacPropertySetter(HvacPropertySetter hvacPropertySetter) {
108         mHvacPropertySetter = hvacPropertySetter;
109     }
110 
111     @Override
onPropertyChanged(CarPropertyValue value)112     public void onPropertyChanged(CarPropertyValue value) {
113         if (value.getPropertyId() == HVAC_FAN_SPEED) {
114             int level = (Integer) value.getValue();
115 
116             setOffAndMaxButtonsActiveState(level);
117 
118             int fanSpeedCount = mFanSpeedButtons.size();
119             int fanSpeedIndex = Math.min(level - 1, 4);
120 
121             int delay = 0;
122             // Animate segments turning on when the fan speed is increased.
123             for (int i = 0; i < fanSpeedIndex; i++) {
124                 FanSpeedBarSegment fanSpeedButton = mFanSpeedButtons.get(i);
125                 if (!fanSpeedButton.isTurnedOn()) {
126                     fanSpeedButton.playTurnOnAnimation(delay, BAR_SEGMENT_ANIMATION_PERIOD_MS);
127                     delay += BAR_SEGMENT_ANIMATION_DELAY_MS;
128                 }
129             }
130 
131             delay = 0;
132             // Animate segments turning off when the fan speed is decreased.
133             for (int i = fanSpeedCount - 1; i >= fanSpeedIndex; i--) {
134                 FanSpeedBarSegment fanSpeedButton = mFanSpeedButtons.get(i);
135                 if (fanSpeedButton.isTurnedOn()) {
136                     fanSpeedButton.playTurnOffAnimation(delay, BAR_SEGMENT_ANIMATION_PERIOD_MS);
137                     delay += BAR_SEGMENT_ANIMATION_DELAY_MS;
138                 }
139             }
140 
141             return;
142         }
143 
144         if (value.getPropertyId() == HVAC_POWER_ON) {
145             mPowerOn = (Boolean) value.getValue();
146         }
147 
148         if (value.getPropertyId() == HVAC_AUTO_ON) {
149             mAutoOn = (Boolean) value.getValue();
150         }
151 
152         updateViewPerAvailability();
153     }
154 
155     @Override
getHvacPropertyToView()156     public @HvacController.HvacProperty Integer getHvacPropertyToView() {
157         return HVAC_FAN_SPEED;
158     }
159 
160     @Override
getAreaId()161     public @HvacController.AreaId Integer getAreaId() {
162         return mHvacGlobalAreaId;
163     }
164 
165     @Override
onFinishInflate()166     protected void onFinishInflate() {
167         super.onFinishInflate();
168 
169         mOnAlpha = mContext.getResources().getFloat(R.dimen.hvac_turned_on_alpha);
170         mOffAlpha = mContext.getResources().getFloat(R.dimen.hvac_turned_off_alpha);
171 
172         mOffButton = (TextView) findViewById(R.id.fan_off);
173         mFanSpeed1 = (FanSpeedBarSegment) findViewById(R.id.fan_speed_1);
174         mFanSpeed2 = (FanSpeedBarSegment) findViewById(R.id.fan_speed_2);
175         mFanSpeed3 = (FanSpeedBarSegment) findViewById(R.id.fan_speed_3);
176         mFanSpeed4 = (FanSpeedBarSegment) findViewById(R.id.fan_speed_4);
177         mMaxButton = (TextView) findViewById(R.id.fan_max);
178 
179         mFanSpeedButtons.add(mFanSpeed1);
180         mFanSpeedButtons.add(mFanSpeed2);
181         mFanSpeedButtons.add(mFanSpeed3);
182         mFanSpeedButtons.add(mFanSpeed4);
183 
184         for (int i = 0; i < mFanSpeedButtons.size(); i++) {
185             mFanSpeedButtons.get(i).setOnClickListener(
186                     getOnClickListener(/* fanSpeedLevel =*/ i + 2));
187         }
188 
189         mMaxButton.setOnClickListener(getOnClickListener(6));
190         mOffButton.setOnClickListener(getOnClickListener(1));
191 
192         // Set the corner radius of the off/max button based on the height of the bar to get a
193         // pill-shaped border.
194         GradientDrawable offButtonBg = new GradientDrawable();
195         offButtonBg.setCornerRadii(new float[]{mCornerRadius, mCornerRadius, 0, 0,
196                 0, 0, mCornerRadius, mCornerRadius});
197         mOffButton.setBackground(offButtonBg);
198         mOffButton.setTextColor(mButtonInactiveTextColor);
199 
200         GradientDrawable maxButtonBg = new GradientDrawable();
201         maxButtonBg.setCornerRadii(new float[]{0, 0, mCornerRadius, mCornerRadius,
202                 mCornerRadius, mCornerRadius, 0, 0});
203         mMaxButton.setBackground(maxButtonBg);
204         mMaxButton.setTextColor(mButtonInactiveTextColor);
205     }
206 
207     @Override
onHvacTemperatureUnitChanged(boolean usesFahrenheit)208     public void onHvacTemperatureUnitChanged(boolean usesFahrenheit) {
209         // no-op.
210     }
211 
setOffAndMaxButtonsActiveState(int level)212     protected void setOffAndMaxButtonsActiveState(int level) {
213         setOffButtonActive(level == 1);
214         setMaxButtonActive(level == 6);
215     }
216 
setMaxButtonActive(boolean active)217     private void setMaxButtonActive(boolean active) {
218         GradientDrawable background = (GradientDrawable) mMaxButton.getBackground();
219         if (active) {
220             background.setColor(mFanMaxActiveBgColor);
221             mMaxButton.setTextColor(mButtonActiveTextColor);
222         } else {
223             background.setColor(Color.TRANSPARENT);
224             mMaxButton.setTextColor(mButtonInactiveTextColor);
225         }
226     }
227 
setOffButtonActive(boolean active)228     private void setOffButtonActive(boolean active) {
229         GradientDrawable background = (GradientDrawable) mOffButton.getBackground();
230         if (active) {
231             background.setColor(mFanOffActiveBgColor);
232             mOffButton.setTextColor(mButtonActiveTextColor);
233         } else {
234             background.setColor(Color.TRANSPARENT);
235             mOffButton.setTextColor(mButtonInactiveTextColor);
236         }
237     }
238 
getOnClickListener(int fanSpeedLevel)239     private OnClickListener getOnClickListener(int fanSpeedLevel) {
240         return v -> {
241             if (shouldAllowControl()) {
242                 mHvacPropertySetter.setHvacProperty(HVAC_FAN_SPEED, getAreaId(), fanSpeedLevel);
243             }
244         };
245     }
246 
247     private void updateViewPerAvailability() {
248         setAlpha(shouldAllowControl() ? mOnAlpha : mOffAlpha);
249     }
250 
251     private boolean shouldAllowControl() {
252         return mPowerOn && !mAutoOn;
253     }
254 }
255