Home | History | Annotate | Download | only in ui
      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 {
     40         void onMaxButtonClicked();
     41 
     42         void onOffButtonClicked();
     43 
     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 
     71     public FanSpeedBar(Context context) {
     72         super(context);
     73         init();
     74     }
     75 
     76     public FanSpeedBar(Context context, AttributeSet attrs) {
     77         super(context, attrs);
     78         init();
     79     }
     80 
     81     public FanSpeedBar(Context context, AttributeSet attrs, int defStyleAttr) {
     82         super(context, attrs, defStyleAttr);
     83         init();
     84     }
     85 
     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 
    103     public void setFanspeedButtonClickListener(FanSpeedButtonClickListener clickListener) {
    104         mListener = clickListener;
    105     }
    106 
    107     @Override
    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 
    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 
    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 
    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 
    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      */
    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