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.controllers; 17 18 import android.animation.Animator; 19 import android.animation.AnimatorSet; 20 import android.animation.ObjectAnimator; 21 import android.animation.ValueAnimator; 22 import android.content.Context; 23 import android.content.res.Resources; 24 import android.graphics.drawable.Drawable; 25 import android.os.Handler; 26 import android.view.MotionEvent; 27 import android.view.View; 28 import android.view.WindowManager; 29 import android.widget.ImageView; 30 31 import androidx.annotation.IntDef; 32 33 import com.android.car.hvac.HvacController; 34 import com.android.car.hvac.R; 35 import com.android.car.hvac.ui.FanDirectionButtons; 36 import com.android.car.hvac.ui.FanSpeedBar; 37 import com.android.car.hvac.ui.HvacPanelRow; 38 import com.android.car.hvac.ui.SeatWarmerButton; 39 import com.android.car.hvac.ui.TemperatureBarOverlay; 40 import com.android.car.hvac.ui.ToggleButton; 41 42 import java.util.ArrayList; 43 import java.util.List; 44 45 /** 46 * A state machine to control transition from various HVAC UI layouts. 47 */ 48 public class HvacPanelController { 49 private static final int PANEL_ANIMATION_TIME_MS = 200; 50 private static final int PANEL_COLLAPSE_ANIMATION_TIME_MS = 500; 51 52 private static final int PANEL_ANIMATION_DELAY_MS = 100; 53 private static final int PANEL_ANIMATION_LONG_DELAY_MS = 3 * PANEL_ANIMATION_DELAY_MS; 54 55 private static final float DISABLED_BUTTON_ALPHA = 0.20f; 56 private static final float ENABLED_BUTTON_ALPHA = 1.0f; 57 58 private static final int STATE_COLLAPSED = 0; 59 private static final int STATE_COLLAPSED_DIMMED = 1; 60 private static final int STATE_FULL_EXPANDED = 2; 61 // Allows for delayed invocation of code. Thus we can control UI events to happen after 62 // others. Example: set something visible but do it after we've complete current UI updates. 63 private static Handler handler = new Handler(); 64 65 // We have both a collapsed and expanded version of the overlays due to a bug 66 // that does not correctly rendering a window resize event. Thus we toggle the the visibility 67 // of the windows instead. A better solution would be a having separate views collapsed state 68 // since the it does not need the other elements but this works for now. 69 private TemperatureBarOverlay mDriverTemperatureBarCollapsed; 70 private TemperatureBarOverlay mPassengerTemperatureBarCollapsed; 71 private TemperatureBarOverlay mDriverTemperatureBarExpanded; 72 private TemperatureBarOverlay mPassengerTemperatureBarExpanded; 73 private final boolean mShowCollapsed; 74 75 @IntDef({STATE_COLLAPSED, 76 STATE_COLLAPSED_DIMMED, 77 STATE_FULL_EXPANDED}) 78 private @interface HvacPanelState {} 79 80 private @HvacPanelState int mCurrentState; 81 82 private int mPanelCollapsedHeight; 83 private int mPanelFullExpandedHeight; 84 85 private View mPanel; 86 private View mContainer; 87 88 private SeatWarmerButton mDriverSeatWarmer; 89 private SeatWarmerButton mPassengerSeatWarmer; 90 91 private ToggleButton mHvacPowerSwitch; 92 93 private ToggleButton mAcButton; 94 private ToggleButton mRecycleAirButton; 95 96 private ToggleButton mFrontDefrosterButton; 97 private ToggleButton mRearDefrosterButton; 98 99 private Drawable mAutoOnDrawable; 100 private Drawable mAutoOffDrawable; 101 102 private ImageView mAutoButton; 103 104 private HvacPanelRow mPanelTopRow; 105 private HvacPanelRow mPanelBottomRow; 106 107 private FanSpeedBar mFanSpeedBar; 108 private FanDirectionButtons mFanDirectionButtons; 109 110 private float mTopPanelMaxAlpha = 1.0f; 111 112 private WindowManager mWindowManager; 113 114 private HvacPanelStateTransition mTransition; 115 116 private View mHvacFanControlBackground; 117 118 private HvacController mHvacController; 119 private FanSpeedBarController mFanSpeedBarController; 120 private FanDirectionButtonsController mFanDirectionButtonsController; 121 private TemperatureController mTemperatureController; 122 private TemperatureController mTemperatureControllerCollapsed; 123 private SeatWarmerController mSeatWarmerController; 124 125 private boolean mInAnimation; 126 127 // TODO: read from shared pref 128 private boolean mAutoMode; 129 HvacPanelController(Context context, View container, WindowManager windowManager, TemperatureBarOverlay driverTemperatureExpanded, TemperatureBarOverlay passengerTemperatureExpanded, TemperatureBarOverlay driverTemperatureBarCollapsed, TemperatureBarOverlay passengerTemperatureBarCollapsed)130 public HvacPanelController(Context context, View container, 131 WindowManager windowManager, 132 TemperatureBarOverlay driverTemperatureExpanded, 133 TemperatureBarOverlay passengerTemperatureExpanded, 134 TemperatureBarOverlay driverTemperatureBarCollapsed, 135 TemperatureBarOverlay passengerTemperatureBarCollapsed) { 136 Resources res = context.getResources(); 137 mShowCollapsed = res.getBoolean(R.bool.config_showCollapsedBars); 138 139 mDriverTemperatureBarCollapsed = driverTemperatureBarCollapsed; 140 mPassengerTemperatureBarCollapsed = passengerTemperatureBarCollapsed; 141 142 mCurrentState = STATE_COLLAPSED; 143 mWindowManager = windowManager; 144 145 mPanelCollapsedHeight = res.getDimensionPixelSize(R.dimen.car_hvac_panel_collapsed_height); 146 mPanelFullExpandedHeight 147 = res.getDimensionPixelSize(R.dimen.car_hvac_panel_full_expanded_height); 148 149 mAutoOffDrawable = res.getDrawable(R.drawable.ic_auto_off); 150 mAutoOnDrawable = res.getDrawable(R.drawable.ic_auto_on); 151 152 mDriverTemperatureBarExpanded = driverTemperatureExpanded; 153 mPassengerTemperatureBarExpanded = passengerTemperatureExpanded; 154 155 mDriverTemperatureBarExpanded.setCloseButtonOnClickListener(mCollapseHvac); 156 mPassengerTemperatureBarExpanded.setCloseButtonOnClickListener(mCollapseHvac); 157 158 // Initially the hvac panel is collapsed, hide the expanded version. 159 mDriverTemperatureBarExpanded.setVisibility(View.INVISIBLE); 160 mPassengerTemperatureBarExpanded.setVisibility(View.INVISIBLE); 161 162 mPassengerTemperatureBarCollapsed.setBarOnClickListener(mExpandHvac); 163 mDriverTemperatureBarCollapsed.setBarOnClickListener(mExpandHvac); 164 165 mContainer = container; 166 mContainer.setVisibility(View.INVISIBLE); 167 mContainer.setOnClickListener(mCollapseHvac); 168 mPanel = mContainer.findViewById(R.id.hvac_center_panel); 169 170 mHvacFanControlBackground = mPanel.findViewById(R.id.fan_control_bg); 171 // set clickable so that clicks are not forward to the mContainer. This way a miss click 172 // does not close the UI 173 mPanel.setClickable(true); 174 175 // Set up top row buttons 176 mPanelTopRow = (HvacPanelRow) mContainer.findViewById(R.id.top_row); 177 178 mAcButton = (ToggleButton) mPanelTopRow.findViewById(R.id.ac_button); 179 mAcButton.setToggleIcons(res.getDrawable(R.drawable.ic_ac_on), 180 res.getDrawable(R.drawable.ic_ac_off)); 181 182 mRecycleAirButton = (ToggleButton) mPanelTopRow.findViewById(R.id.recycle_air_button); 183 184 mRecycleAirButton.setToggleIcons(res.getDrawable(R.drawable.ic_recycle_air_on), 185 res.getDrawable(R.drawable.ic_recycle_air_off)); 186 187 // Setup bottom row buttons 188 mPanelBottomRow = (HvacPanelRow) mContainer.findViewById(R.id.bottom_row); 189 190 mAutoButton = (ImageView) mContainer.findViewById(R.id.auto_button); 191 mAutoButton.setOnClickListener(mAutoButtonClickListener); 192 193 mFrontDefrosterButton = (ToggleButton) mPanelBottomRow.findViewById(R.id.front_defroster); 194 mRearDefrosterButton = (ToggleButton) mPanelBottomRow.findViewById(R.id.rear_defroster); 195 196 mFrontDefrosterButton.setToggleIcons(res.getDrawable(R.drawable.ic_front_defroster_on), 197 res.getDrawable(R.drawable.ic_front_defroster_off)); 198 199 mRearDefrosterButton.setToggleIcons(res.getDrawable(R.drawable.ic_rear_defroster_on), 200 res.getDrawable(R.drawable.ic_rear_defroster_off)); 201 202 mFanSpeedBar = (FanSpeedBar) mContainer.findViewById(R.id.fan_speed_bar); 203 mFanDirectionButtons = (FanDirectionButtons) mContainer.findViewById(R.id.fan_direction_buttons); 204 205 mDriverSeatWarmer = (SeatWarmerButton) mContainer.findViewById(R.id.left_seat_heater); 206 mPassengerSeatWarmer = (SeatWarmerButton) mContainer.findViewById(R.id.right_seat_heater); 207 208 mHvacPowerSwitch = (ToggleButton)mPanelBottomRow.findViewById(R.id.hvac_master_switch); 209 // TODO: this is not good UX design - just a placeholder 210 mHvacPowerSwitch.setToggleIcons(res.getDrawable(R.drawable.ac_master_switch_on), 211 res.getDrawable(R.drawable.ac_master_switch_off)); 212 213 if (!mShowCollapsed) { 214 mDriverTemperatureBarCollapsed.setVisibility(View.INVISIBLE); 215 mPassengerTemperatureBarCollapsed.setVisibility(View.INVISIBLE); 216 } 217 } 218 updateHvacController(HvacController controller)219 public void updateHvacController(HvacController controller) { 220 //TODO: handle disconnected HvacController. 221 mHvacController = controller; 222 223 mFanSpeedBarController = new FanSpeedBarController(mFanSpeedBar, mHvacController); 224 mFanDirectionButtonsController 225 = new FanDirectionButtonsController(mFanDirectionButtons, mHvacController); 226 mTemperatureController = new TemperatureController( 227 mPassengerTemperatureBarExpanded, 228 mDriverTemperatureBarExpanded, 229 mPassengerTemperatureBarCollapsed, 230 mDriverTemperatureBarCollapsed, 231 mHvacController); 232 mSeatWarmerController = new SeatWarmerController(mPassengerSeatWarmer, 233 mDriverSeatWarmer, mHvacController); 234 235 // Toggle buttons do not need additional logic to map between hardware 236 // and UI settings. Simply use a ToggleListener to handle clicks. 237 mAcButton.setIsOn(mHvacController.getAcState()); 238 mAcButton.setToggleListener(new ToggleButton.ToggleListener() { 239 @Override 240 public void onToggled(boolean isOn) { 241 mHvacController.setAcState(isOn); 242 } 243 }); 244 245 mFrontDefrosterButton.setIsOn(mHvacController.getFrontDefrosterState()); 246 mFrontDefrosterButton.setToggleListener(new ToggleButton.ToggleListener() { 247 @Override 248 public void onToggled(boolean isOn) { 249 mHvacController.setFrontDefrosterState(isOn); 250 } 251 }); 252 253 mRearDefrosterButton.setIsOn(mHvacController.getRearDefrosterState()); 254 mRearDefrosterButton.setToggleListener(new ToggleButton.ToggleListener() { 255 @Override 256 public void onToggled(boolean isOn) { 257 mHvacController.setRearDefrosterState(isOn); 258 } 259 }); 260 261 mRecycleAirButton.setIsOn(mHvacController.getAirCirculationState()); 262 mRecycleAirButton.setToggleListener(new ToggleButton.ToggleListener() { 263 @Override 264 public void onToggled(boolean isOn) { 265 mHvacController.setAirCirculation(isOn); 266 } 267 }); 268 269 setAutoMode(mHvacController.getAutoModeState()); 270 271 mHvacPowerSwitch.setIsOn(mHvacController.getHvacPowerState()); 272 mHvacPowerSwitch.setToggleListener(isOn -> mHvacController.setHvacPowerState(isOn)); 273 274 mHvacController.registerCallback(mToggleButtonCallbacks); 275 mToggleButtonCallbacks.onHvacPowerChange(mHvacController.getHvacPowerState()); 276 } 277 278 private HvacController.Callback mToggleButtonCallbacks 279 = new HvacController.Callback() { 280 @Override 281 public void onAirCirculationChange(boolean isOn) { 282 mRecycleAirButton.setIsOn(isOn); 283 } 284 285 @Override 286 public void onFrontDefrosterChange(boolean isOn) { 287 mFrontDefrosterButton.setIsOn(isOn); 288 } 289 290 @Override 291 public void onRearDefrosterChange(boolean isOn) { 292 mRearDefrosterButton.setIsOn(isOn); 293 } 294 295 @Override 296 public void onAcStateChange(boolean isOn) { 297 mAcButton.setIsOn(isOn); 298 } 299 300 @Override 301 public void onAutoModeChange(boolean isOn) { 302 mAutoMode = isOn; 303 setAutoMode(mAutoMode); 304 } 305 }; 306 307 /** 308 * Take the listeners and animators from a {@link AnimatorSet} and merge them to the 309 * input {@link Animator} and {@link android.animation.Animator.AnimatorListener} lists. 310 */ combineAnimationSet(List<Animator> animatorList, List<Animator.AnimatorListener> listenerList, AnimatorSet set)311 private void combineAnimationSet(List<Animator> animatorList, 312 List<Animator.AnimatorListener> listenerList, AnimatorSet set) { 313 314 ArrayList<Animator> list = set.getChildAnimations(); 315 if (list != null) { 316 int size = list.size(); 317 for (int i = 0; i < size; i++) { 318 animatorList.add(list.get(i)); 319 } 320 } 321 322 ArrayList<Animator.AnimatorListener> listeners = set.getListeners(); 323 if (listeners != null) { 324 int size = listeners.size(); 325 for (int i = 0; i < size; i++) { 326 listenerList.add(listeners.get(i)); 327 } 328 } 329 } 330 331 /** 332 * Play necessary animations between {@link HvacPanelState} transitions 333 */ transitionState(@vacPanelState int startState, @HvacPanelState int endState)334 private void transitionState(@HvacPanelState int startState, @HvacPanelState int endState) { 335 if (startState == endState || mInAnimation) { 336 return; 337 } 338 339 List<Animator> animationList = new ArrayList<>(); 340 List<Animator.AnimatorListener> listenerList = new ArrayList<>(); 341 ValueAnimator heightAnimator = getPanelHeightAnimator(startState, endState); 342 mTransition = new HvacPanelStateTransition(startState, endState); 343 ValueAnimator fanBgAlphaAnimator; 344 switch (endState) { 345 case STATE_COLLAPSED: 346 // Transition to collapsed state: 347 // 1. Collapse the temperature bars. 348 // 2. Collapse the top and bottom panel, staggered with a different delay. 349 // 3. Decrease height of the hvac center panel, but maintain container height. 350 // 4. Fade the background of the fan controls seperately to create staggered effect. 351 animationList.add(heightAnimator); 352 heightAnimator.setDuration(PANEL_COLLAPSE_ANIMATION_TIME_MS); 353 fanBgAlphaAnimator 354 = ObjectAnimator.ofFloat(mHvacFanControlBackground, View.ALPHA, 1, 0) 355 .setDuration(PANEL_COLLAPSE_ANIMATION_TIME_MS); 356 fanBgAlphaAnimator.setStartDelay(PANEL_ANIMATION_DELAY_MS); 357 animationList.add(fanBgAlphaAnimator); 358 359 ValueAnimator panelAlphaAnimator 360 = ObjectAnimator.ofFloat(mContainer, View.ALPHA, 1, 0); 361 panelAlphaAnimator.setDuration(200); 362 panelAlphaAnimator.setStartDelay(300); 363 364 animationList.add(panelAlphaAnimator); 365 366 combineAnimationSet(animationList, listenerList, 367 mDriverTemperatureBarExpanded.getCollapseAnimations()); 368 combineAnimationSet(animationList, listenerList, 369 mPassengerTemperatureBarExpanded.getCollapseAnimations()); 370 371 combineAnimationSet(animationList, listenerList, 372 mPanelTopRow.getCollapseAnimation(PANEL_ANIMATION_DELAY_MS, 373 mTopPanelMaxAlpha)); 374 combineAnimationSet(animationList, listenerList, 375 mPanelBottomRow.getCollapseAnimation(PANEL_ANIMATION_DELAY_MS, 376 mTopPanelMaxAlpha)); 377 break; 378 case STATE_COLLAPSED_DIMMED: 379 // Hide the temperature numbers, open arrows and auto state button. 380 // TODO: determine if this section is still needed. 381 break; 382 case STATE_FULL_EXPANDED: 383 // Transition to expaneded state: 384 // 1. Expand the temperature bars. 385 // 2. Expand the top and bottom panel, staggered with a different delay. 386 // 3. Increase height of the hvac center panel, but maintain container height. 387 // 4. Fade in fan control background in a staggered manner. 388 fanBgAlphaAnimator 389 = ObjectAnimator.ofFloat(mHvacFanControlBackground, View.ALPHA, 0, 1) 390 .setDuration(PANEL_ANIMATION_TIME_MS); 391 fanBgAlphaAnimator.setStartDelay(PANEL_ANIMATION_DELAY_MS); 392 animationList.add(fanBgAlphaAnimator); 393 394 animationList.add(heightAnimator); 395 combineAnimationSet(animationList, listenerList, 396 mDriverTemperatureBarExpanded.getExpandAnimatons()); 397 combineAnimationSet(animationList, listenerList, 398 mPassengerTemperatureBarExpanded.getExpandAnimatons()); 399 400 // During expansion, the bottom panel animation should be delayed 401 combineAnimationSet(animationList, listenerList, 402 mPanelTopRow.getExpandAnimation(PANEL_ANIMATION_DELAY_MS, 403 mTopPanelMaxAlpha)); 404 combineAnimationSet(animationList, listenerList, 405 mPanelBottomRow.getExpandAnimation(PANEL_ANIMATION_LONG_DELAY_MS, 1f)); 406 break; 407 default: 408 } 409 410 // If there are animations for the state change, play them all together and ensure 411 // the animation listeners are attached. 412 if (animationList.size() > 0) { 413 AnimatorSet animatorSet = new AnimatorSet(); 414 animatorSet.playTogether(animationList); 415 for (Animator.AnimatorListener listener : listenerList) { 416 animatorSet.addListener(listener); 417 } 418 animatorSet.addListener(mAnimatorListener); 419 animatorSet.start(); 420 } 421 } 422 423 private AnimatorSet.AnimatorListener mAnimatorListener = new AnimatorSet.AnimatorListener() { 424 @Override 425 public void onAnimationStart(Animator animation) { 426 mTransition.onTransitionStart(); 427 } 428 429 @Override 430 public void onAnimationEnd(Animator animation) { 431 mTransition.onTransitionComplete(); 432 } 433 434 @Override 435 public void onAnimationCancel(Animator animation) {} 436 437 @Override 438 public void onAnimationRepeat(Animator animation) {} 439 }; 440 getPanelHeightAnimator(@vacPanelState int startState, @HvacPanelState int endState)441 private ValueAnimator getPanelHeightAnimator(@HvacPanelState int startState, 442 @HvacPanelState int endState) { 443 int startHeight = getStateHeight(startState); 444 int endHeight = getStateHeight(endState); 445 if (startHeight == endHeight) { 446 return null; 447 } 448 449 ValueAnimator heightAnimator = new ValueAnimator().ofInt(startHeight, endHeight) 450 .setDuration(PANEL_ANIMATION_TIME_MS); 451 heightAnimator.addUpdateListener(mHeightUpdateListener); 452 return heightAnimator; 453 } 454 getStateHeight(@vacPanelState int state)455 private int getStateHeight(@HvacPanelState int state) { 456 switch (state) { 457 case STATE_COLLAPSED: 458 case STATE_COLLAPSED_DIMMED: 459 return mPanelCollapsedHeight; 460 case STATE_FULL_EXPANDED: 461 return mPanelFullExpandedHeight; 462 default: 463 throw new IllegalArgumentException("No height mapped to HVAC State: " + state); 464 } 465 } 466 setAutoMode(boolean isOn)467 private void setAutoMode(boolean isOn) { 468 if (isOn) { 469 mPanelTopRow.setOnTouchListener(new View.OnTouchListener() { 470 @Override 471 public boolean onTouch(View v, MotionEvent event) { 472 return true; 473 } 474 }); 475 mAutoMode = true; 476 mPanelTopRow.disablePanel(true); 477 mTopPanelMaxAlpha = DISABLED_BUTTON_ALPHA; 478 mAutoButton.setImageDrawable(mAutoOnDrawable); 479 } else { 480 mPanelTopRow.disablePanel(false); 481 mTopPanelMaxAlpha = ENABLED_BUTTON_ALPHA; 482 mAutoButton.setImageDrawable(mAutoOffDrawable); 483 } 484 mHvacFanControlBackground.setAlpha(mTopPanelMaxAlpha); 485 mPanelTopRow.setAlpha(mTopPanelMaxAlpha); 486 } 487 488 private View.OnClickListener mAutoButtonClickListener = new View.OnClickListener() { 489 @Override 490 public void onClick(View v) { 491 if (mAutoMode) { 492 mAutoMode = false; 493 } else { 494 mAutoMode = true; 495 } 496 mHvacController.setAutoMode(mAutoMode); 497 setAutoMode(mAutoMode); 498 } 499 }; 500 501 private View.OnClickListener mCollapseHvac = new View.OnClickListener() { 502 @Override 503 public void onClick(View v) { 504 if (mInAnimation) { 505 return; 506 } 507 if (mCurrentState != STATE_COLLAPSED) { 508 transitionState(mCurrentState, STATE_COLLAPSED); 509 } 510 } 511 }; 512 toggleHvacUi()513 public void toggleHvacUi() { 514 if (mCurrentState != STATE_COLLAPSED) { 515 mCollapseHvac.onClick(null); 516 } else { 517 mExpandHvac.onClick(null); 518 } 519 } 520 collapseHvacUi()521 public void collapseHvacUi() { 522 if (mCurrentState != STATE_COLLAPSED) { 523 mCollapseHvac.onClick(null); 524 } 525 } 526 527 public View.OnClickListener mExpandHvac = new View.OnClickListener() { 528 @Override 529 public void onClick(View v) { 530 if (mInAnimation) { 531 return; 532 } 533 if (mCurrentState != STATE_FULL_EXPANDED) { 534 transitionState(mCurrentState, STATE_FULL_EXPANDED); 535 } 536 } 537 }; 538 539 540 private ValueAnimator.AnimatorUpdateListener mHeightUpdateListener 541 = new ValueAnimator.AnimatorUpdateListener() { 542 @Override 543 public void onAnimationUpdate(ValueAnimator animation) { 544 int height = (Integer) animation.getAnimatedValue(); 545 int currentHeight = mPanel.getLayoutParams().height; 546 mPanel.getLayoutParams().height = height; 547 mPanel.setTop(mPanel.getTop() + height - currentHeight); 548 mPanel.requestLayout(); 549 } 550 }; 551 552 /** 553 * Handles the necessary setup/clean up before and after a state transition. 554 */ 555 private class HvacPanelStateTransition { 556 private @HvacPanelState int mEndState; 557 private @HvacPanelState int mStartState; 558 HvacPanelStateTransition(@vacPanelState int startState, @HvacPanelState int endState)559 public HvacPanelStateTransition(@HvacPanelState int startState, 560 @HvacPanelState int endState) { 561 mStartState = startState; 562 mEndState = endState; 563 } 564 onTransitionStart()565 public void onTransitionStart() { 566 mInAnimation = true; 567 if (mEndState == STATE_FULL_EXPANDED) { 568 mPassengerTemperatureBarExpanded.setVisibility(View.VISIBLE); 569 mDriverTemperatureBarExpanded.setVisibility(View.VISIBLE); 570 if (mShowCollapsed) { 571 mDriverTemperatureBarCollapsed.setVisibility(View.INVISIBLE); 572 mPassengerTemperatureBarCollapsed.setVisibility(View.INVISIBLE); 573 } 574 mContainer.setAlpha(1); 575 mContainer.setVisibility(View.VISIBLE); 576 } 577 } 578 onTransitionComplete()579 public void onTransitionComplete() { 580 if (mEndState == STATE_COLLAPSED) { 581 if (mShowCollapsed) { 582 mDriverTemperatureBarCollapsed.setVisibility(View.VISIBLE); 583 mPassengerTemperatureBarCollapsed.setVisibility(View.VISIBLE); 584 } 585 handler.postAtFrontOfQueue(() -> { 586 mDriverTemperatureBarExpanded.setVisibility(View.INVISIBLE); 587 mPassengerTemperatureBarExpanded.setVisibility(View.INVISIBLE); 588 }); 589 } 590 if (mStartState == STATE_FULL_EXPANDED) { 591 mContainer.setVisibility(View.INVISIBLE); 592 } 593 594 // set new states 595 mCurrentState = mEndState; 596 mInAnimation = false; 597 } 598 } 599 } 600