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 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 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 */ 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 */ 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 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 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 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 513 public void toggleHvacUi() { 514 if(mCurrentState != STATE_COLLAPSED) { 515 mCollapseHvac.onClick(null); 516 } else { 517 mExpandHvac.onClick(null); 518 } 519 } 520 521 public View.OnClickListener mExpandHvac = new View.OnClickListener() { 522 @Override 523 public void onClick(View v) { 524 if (mInAnimation) { 525 return; 526 } 527 if (mCurrentState != STATE_FULL_EXPANDED) { 528 transitionState(mCurrentState, STATE_FULL_EXPANDED); 529 } 530 } 531 }; 532 533 534 private ValueAnimator.AnimatorUpdateListener mHeightUpdateListener 535 = new ValueAnimator.AnimatorUpdateListener() { 536 @Override 537 public void onAnimationUpdate(ValueAnimator animation) { 538 int height = (Integer) animation.getAnimatedValue(); 539 int currentHeight = mPanel.getLayoutParams().height; 540 mPanel.getLayoutParams().height = height; 541 mPanel.setTop(mPanel.getTop() + height - currentHeight); 542 mPanel.requestLayout(); 543 } 544 }; 545 546 /** 547 * Handles the necessary setup/clean up before and after a state transition. 548 */ 549 private class HvacPanelStateTransition { 550 private @HvacPanelState int mEndState; 551 private @HvacPanelState int mStartState; 552 553 public HvacPanelStateTransition(@HvacPanelState int startState, 554 @HvacPanelState int endState) { 555 mStartState = startState; 556 mEndState = endState; 557 } 558 559 public void onTransitionStart() { 560 mInAnimation = true; 561 if (mEndState == STATE_FULL_EXPANDED) { 562 mPassengerTemperatureBarExpanded.setVisibility(View.VISIBLE); 563 mDriverTemperatureBarExpanded.setVisibility(View.VISIBLE); 564 if (mShowCollapsed) { 565 mDriverTemperatureBarCollapsed.setVisibility(View.INVISIBLE); 566 mPassengerTemperatureBarCollapsed.setVisibility(View.INVISIBLE); 567 } 568 mContainer.setAlpha(1); 569 mContainer.setVisibility(View.VISIBLE); 570 } 571 } 572 573 public void onTransitionComplete() { 574 if (mEndState == STATE_COLLAPSED) { 575 if (mShowCollapsed) { 576 mDriverTemperatureBarCollapsed.setVisibility(View.VISIBLE); 577 mPassengerTemperatureBarCollapsed.setVisibility(View.VISIBLE); 578 } 579 handler.postAtFrontOfQueue(() -> { 580 mDriverTemperatureBarExpanded.setVisibility(View.INVISIBLE); 581 mPassengerTemperatureBarExpanded.setVisibility(View.INVISIBLE); 582 }); 583 } 584 if (mStartState == STATE_FULL_EXPANDED) { 585 mContainer.setVisibility(View.INVISIBLE); 586 } 587 588 // set new states 589 mCurrentState = mEndState; 590 mInAnimation = false; 591 } 592 } 593 } 594