1 /*
2  * Copyright (c) 2016, 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 package com.android.car.hvac.ui;
17 
18 import android.content.Context;
19 import android.content.res.Resources;
20 import android.graphics.Color;
21 import android.graphics.drawable.GradientDrawable;
22 import android.util.AttributeSet;
23 import android.view.View;
24 import android.widget.RelativeLayout;
25 import android.widget.TextView;
26 import com.android.car.hvac.R;
27 
28 import java.util.ArrayList;
29 
30 /**
31  * Represents the fan speed bar. The bar is composed of a list of fan speed buttons. When a
32  * speed is selected, all lower levels will turn on and all higher levels will turn off. The
33  * currently selected speed can also be toggled on and off.
34  */
35 public class FanSpeedBar extends RelativeLayout {
36     /**
37      * A listener that is notified when the buttons in the fan speed bar are clicked.
38      */
39     public interface FanSpeedButtonClickListener {
onMaxButtonClicked()40         void onMaxButtonClicked();
41 
onOffButtonClicked()42         void onOffButtonClicked();
43 
onFanSpeedSegmentClicked(int position)44         void onFanSpeedSegmentClicked(int position);
45     }
46 
47     private static final int BAR_SEGMENT_ANIMATION_DELAY_MS = 50;
48     private static final int BAR_SEGMENT_ANIMATION_MS = 100;
49     private static final int NUM_FAN_SPEED = 34;
50 
51     private int mButtonEnabledTextColor;
52     private int mButtonDisabledTextColor;
53 
54     private int mFanOffEnabledBgColor;
55     private int mFanMaxEnabledBgColor;
56 
57     private float mCornerRadius;
58 
59     private TextView mMaxButton;
60     private TextView mOffButton;
61 
62     private FanSpeedBarSegment mFanSpeed1;
63     private FanSpeedBarSegment mFanSpeed2;
64     private FanSpeedBarSegment mFanSpeed3;
65     private FanSpeedBarSegment mFanSpeed4;
66 
67     private FanSpeedButtonClickListener mListener;
68 
69     private final ArrayList<FanSpeedBarSegment> mFanSpeedButtons = new ArrayList<>(NUM_FAN_SPEED);
70 
FanSpeedBar(Context context)71     public FanSpeedBar(Context context) {
72         super(context);
73         init();
74     }
75 
FanSpeedBar(Context context, AttributeSet attrs)76     public FanSpeedBar(Context context, AttributeSet attrs) {
77         super(context, attrs);
78         init();
79     }
80 
FanSpeedBar(Context context, AttributeSet attrs, int defStyleAttr)81     public FanSpeedBar(Context context, AttributeSet attrs, int defStyleAttr) {
82         super(context, attrs, defStyleAttr);
83         init();
84     }
85 
init()86     private void init() {
87         inflate(getContext(), R.layout.fan_speed, this);
88 
89         Resources res = getContext().getResources();
90         // The fanspeed bar is set as height 72dp to match min tap target size. However it is
91         // inset by to make it appear thinner.
92         int barHeight = res.getDimensionPixelSize(R.dimen.hvac_fan_speed_bar_height);
93         int insetHeight = res.getDimensionPixelSize(R.dimen.hvac_fan_speed_bar_vertical_inset);
94         mCornerRadius = (barHeight - 2 * insetHeight) / 2;
95 
96         mFanOffEnabledBgColor = res.getColor(R.color.hvac_fanspeed_off_enabled_bg);
97 
98         mButtonEnabledTextColor = res.getColor(R.color.hvac_fanspeed_off_enabled_text_color);
99         mButtonDisabledTextColor = res.getColor(R.color.hvac_fanspeed_off_disabled_text_color);
100         mFanMaxEnabledBgColor = res.getColor(R.color.hvac_fanspeed_segment_color);
101     }
102 
setFanspeedButtonClickListener(FanSpeedButtonClickListener clickListener)103     public void setFanspeedButtonClickListener(FanSpeedButtonClickListener clickListener) {
104         mListener = clickListener;
105     }
106 
107     @Override
onFinishInflate()108     protected void onFinishInflate() {
109         super.onFinishInflate();
110 
111         mFanSpeed1 = (FanSpeedBarSegment) findViewById(R.id.fan_speed_1);
112         mFanSpeed2 = (FanSpeedBarSegment) findViewById(R.id.fan_speed_2);
113         mFanSpeed3 = (FanSpeedBarSegment) findViewById(R.id.fan_speed_3);
114         mFanSpeed4 = (FanSpeedBarSegment) findViewById(R.id.fan_speed_4);
115 
116         mFanSpeed1.setTag(R.id.TAG_FAN_SPEED_LEVEL, 1);
117         mFanSpeed2.setTag(R.id.TAG_FAN_SPEED_LEVEL, 2);
118         mFanSpeed3.setTag(R.id.TAG_FAN_SPEED_LEVEL, 3);
119         mFanSpeed4.setTag(R.id.TAG_FAN_SPEED_LEVEL, 4);
120 
121         mFanSpeedButtons.add(mFanSpeed1);
122         mFanSpeedButtons.add(mFanSpeed2);
123         mFanSpeedButtons.add(mFanSpeed3);
124         mFanSpeedButtons.add(mFanSpeed4);
125 
126         for (View view : mFanSpeedButtons) {
127             view.setOnClickListener(mFanSpeedBarClickListener);
128         }
129 
130         mMaxButton = (TextView) findViewById(R.id.fan_max);
131         mMaxButton.setOnClickListener(new OnClickListener() {
132             @Override
133             public void onClick(View v) {
134                 setMax();
135                 if (mListener != null) {
136                   mListener.onMaxButtonClicked();
137                 }
138             }
139         });
140 
141         mOffButton = (TextView) findViewById(R.id.fan_off);
142         mOffButton.setOnClickListener(new OnClickListener() {
143             @Override
144             public void onClick(View v) {
145                 setOff();
146                 if (mListener != null) {
147                     mListener.onOffButtonClicked();
148                 }
149             }
150         });
151 
152         // Set the corner radius of the off/max button based on the height of the bar to get a
153         // semicircular border.
154         GradientDrawable offButtonBg = new GradientDrawable();
155         offButtonBg.setCornerRadii(new float[]{mCornerRadius, mCornerRadius, 0, 0,
156                 0, 0, mCornerRadius, mCornerRadius});
157         mOffButton.setBackground(offButtonBg);
158         mOffButton.setTextColor(mButtonDisabledTextColor);
159 
160         GradientDrawable maxButtonBg = new GradientDrawable();
161         maxButtonBg.setCornerRadii(new float[]{0, 0, mCornerRadius, mCornerRadius,
162                 mCornerRadius, mCornerRadius, 0, 0});
163         mMaxButton.setBackground(maxButtonBg);
164         mMaxButton.setTextColor(mButtonDisabledTextColor);
165     }
166 
setMax()167     public void setMax() {
168         int numFanSpeed = mFanSpeedButtons.size();
169         int delay = 0;
170 
171         for (int i = 0; i < numFanSpeed; i++) {
172             if (!mFanSpeedButtons.get(i).isTurnedOn()) {
173                 mFanSpeedButtons.get(i).playTurnOnAnimation(BAR_SEGMENT_ANIMATION_MS, delay);
174                 delay += BAR_SEGMENT_ANIMATION_DELAY_MS;
175             }
176         }
177         setOffButtonEnabled(false);
178         setMaxButtonEnabled(true);
179     }
180 
setMaxButtonEnabled(boolean enabled)181     private void setMaxButtonEnabled(boolean enabled) {
182         GradientDrawable background = (GradientDrawable) mMaxButton.getBackground();
183         if (enabled) {
184             background.setColor(mFanMaxEnabledBgColor);
185             mMaxButton.setTextColor(mButtonEnabledTextColor);
186         } else {
187             background.setColor(Color.TRANSPARENT);
188             mMaxButton.setTextColor(mButtonDisabledTextColor);
189         }
190     }
191 
192 
setOffButtonEnabled(boolean enabled)193     private void setOffButtonEnabled(boolean enabled) {
194         GradientDrawable background = (GradientDrawable) mOffButton.getBackground();
195         if (enabled) {
196             background.setColor(mFanOffEnabledBgColor);
197             mOffButton.setTextColor(mButtonEnabledTextColor);
198         } else {
199             background.setColor(Color.TRANSPARENT);
200             mOffButton.setTextColor(mButtonDisabledTextColor);
201         }
202     }
203 
setOff()204     public void setOff() {
205         setOffButtonEnabled(true);
206         setMaxButtonEnabled(false);
207 
208         int numFanSpeed = mFanSpeedButtons.size();
209         int delay = 0;
210         for (int i = numFanSpeed - 1; i >= 0; i--) {
211             if (mFanSpeedButtons.get(i).isTurnedOn()) {
212                 mFanSpeedButtons.get(i).playTurnOffAnimation(BAR_SEGMENT_ANIMATION_MS, delay);
213                 delay += BAR_SEGMENT_ANIMATION_DELAY_MS;
214             }
215         }
216     }
217 
218     /**
219      * Sets the fan speed segments to on off based on the position. Note the changes do not animate,
220      * if animation is required use {@link FanSpeedBar#animateToSpeedSegment(int)}
221      */
setSpeedSegment(int position)222     public void setSpeedSegment(int position) {
223         for (int i = 0; i < mFanSpeedButtons.size(); i++) {
224             // For segments lower than the position, they should be turned on.
225             mFanSpeedButtons.get(i).setTurnedOn(i < position ? true : false);
226         }
227     }
228 
229     /**
230      * Animates the fan speed bar to a specific position. Turning on all positions before it
231      * and turning off all positions after it.
232      */
233     public void animateToSpeedSegment(int position) {
234         setOffButtonEnabled(false);
235         setMaxButtonEnabled(false);
236 
237         int fanSpeedCount = mFanSpeedButtons.size();
238         int fanSpeedIndex = position - 1;
239 
240         if (fanSpeedIndex < 0) {
241             fanSpeedIndex = 0;
242         } else if (fanSpeedIndex > fanSpeedCount) {
243             fanSpeedIndex = fanSpeedCount - 1;
244         }
245 
246         int delay = 0;
247         if (mFanSpeedButtons.get(fanSpeedIndex).isTurnedOn()) {
248             // If selected position is already turned on, then make sure each segment
249             // after is turned off.
250             for (int i = fanSpeedCount - 1; i > fanSpeedIndex; i--) {
251                 if (mFanSpeedButtons.get(i).isTurnedOn()) {
252                     mFanSpeedButtons.get(i).playTurnOffAnimation(BAR_SEGMENT_ANIMATION_MS, delay);
253                     delay += BAR_SEGMENT_ANIMATION_DELAY_MS;
254                 }
255             }
256         } else {
257             // If the selected position is turned off, turn on all positions before it and itself on.
258             for (int i = 0; i <= fanSpeedIndex; i++) {
259                 if (!mFanSpeedButtons.get(i).isTurnedOn()) {
260                     mFanSpeedButtons.get(i).playTurnOnAnimation(BAR_SEGMENT_ANIMATION_MS, delay);
261                     delay += BAR_SEGMENT_ANIMATION_DELAY_MS;
262                 }
263             }
264         }
265     }
266 
267     private final OnClickListener mFanSpeedBarClickListener = new OnClickListener() {
268         @Override
269         public void onClick(View v) {
270             int level = (int) v.getTag(R.id.TAG_FAN_SPEED_LEVEL);
271 
272             setOffButtonEnabled(false);
273             setMaxButtonEnabled(false);
274 
275             int fanSpeedCount = mFanSpeedButtons.size();
276             int fanSpeedIndex = level - 1;
277 
278             // If selected speed is the last segment in the bar, turn it off it's currently on.
279             if (fanSpeedIndex == fanSpeedCount - 1
280                     && mFanSpeedButtons.get(fanSpeedIndex).isTurnedOn()) {
281                 mFanSpeedButtons.get(fanSpeedIndex)
282                         .playTurnOffAnimation(BAR_SEGMENT_ANIMATION_MS, 0);
283                 return;
284             }
285 
286             // If the selected speed is on, and the next fan speed is not on. Then turn off
287             // the selected speed.
288             if (fanSpeedIndex < fanSpeedCount - 1
289                     && mFanSpeedButtons.get(fanSpeedIndex).isTurnedOn()
290                     && !mFanSpeedButtons.get(fanSpeedIndex + 1).isTurnedOn()) {
291                 mFanSpeedButtons.get(fanSpeedIndex)
292                         .playTurnOffAnimation(BAR_SEGMENT_ANIMATION_MS, 0);
293                 return;
294             }
295 
296             int delay = 0;
297             for (int i = 0; i < level; i++) {
298                 if (!mFanSpeedButtons.get(i).isTurnedOn()) {
299                     mFanSpeedButtons.get(i).playTurnOnAnimation(BAR_SEGMENT_ANIMATION_MS, delay);
300                     delay += BAR_SEGMENT_ANIMATION_DELAY_MS;
301                 }
302             }
303 
304             delay = 0;
305             for (int i = fanSpeedCount - 1; i >= level; i--) {
306                 if (mFanSpeedButtons.get(i).isTurnedOn()) {
307                     mFanSpeedButtons.get(i).playTurnOffAnimation(BAR_SEGMENT_ANIMATION_MS, delay);
308                     delay += BAR_SEGMENT_ANIMATION_DELAY_MS;
309                 }
310             }
311 
312             if (mListener != null) {
313                 mListener.onFanSpeedSegmentClicked(level);
314             }
315         }
316     };
317 }
318