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