1 /* 2 * Copyright (C) 2013 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.dialer.app.widget; 17 18 import android.animation.ValueAnimator; 19 import android.os.Bundle; 20 import com.android.dialer.animation.AnimUtils.AnimationCallback; 21 import com.android.dialer.common.LogUtil; 22 23 /** 24 * Controls the various animated properties of the actionBar: showing/hiding, fading/revealing, and 25 * collapsing/expanding, and assigns suitable properties to the actionBar based on the current state 26 * of the UI. 27 */ 28 public class ActionBarController { 29 30 private static final String KEY_IS_SLID_UP = "key_actionbar_is_slid_up"; 31 private static final String KEY_IS_FADED_OUT = "key_actionbar_is_faded_out"; 32 private static final String KEY_IS_EXPANDED = "key_actionbar_is_expanded"; 33 34 private ActivityUi mActivityUi; 35 private SearchEditTextLayout mSearchBox; 36 37 private boolean mIsActionBarSlidUp; 38 39 private final AnimationCallback mFadeOutCallback = 40 new AnimationCallback() { 41 @Override 42 public void onAnimationEnd() { 43 slideActionBar(true /* slideUp */, false /* animate */); 44 } 45 46 @Override 47 public void onAnimationCancel() { 48 slideActionBar(true /* slideUp */, false /* animate */); 49 } 50 }; 51 52 private final AnimationCallback mFadeInCallback = 53 new AnimationCallback() { 54 @Override 55 public void onAnimationEnd() { 56 slideActionBar(false /* slideUp */, false /* animate */); 57 } 58 59 @Override 60 public void onAnimationCancel() { 61 slideActionBar(false /* slideUp */, false /* animate */); 62 } 63 }; 64 private ValueAnimator mAnimator; 65 ActionBarController(ActivityUi activityUi, SearchEditTextLayout searchBox)66 public ActionBarController(ActivityUi activityUi, SearchEditTextLayout searchBox) { 67 mActivityUi = activityUi; 68 mSearchBox = searchBox; 69 } 70 71 /** @return Whether or not the action bar is currently showing (both slid down and visible) */ isActionBarShowing()72 public boolean isActionBarShowing() { 73 return !mIsActionBarSlidUp && !mSearchBox.isFadedOut(); 74 } 75 76 /** Called when the user has tapped on the collapsed search box, to start a new search query. */ onSearchBoxTapped()77 public void onSearchBoxTapped() { 78 LogUtil.d( 79 "ActionBarController.onSearchBoxTapped", "isInSearchUi " + mActivityUi.isInSearchUi()); 80 if (!mActivityUi.isInSearchUi()) { 81 mSearchBox.expand(true /* animate */, true /* requestFocus */); 82 } 83 } 84 85 /** Called when search UI has been exited for some reason. */ onSearchUiExited()86 public void onSearchUiExited() { 87 LogUtil.d( 88 "ActionBarController.onSearchUIExited", 89 "isExpanded: %b, isFadedOut %b", 90 mSearchBox.isExpanded(), 91 mSearchBox.isFadedOut()); 92 if (mSearchBox.isExpanded()) { 93 mSearchBox.collapse(true /* animate */); 94 } 95 if (mSearchBox.isFadedOut()) { 96 mSearchBox.fadeIn(); 97 } 98 99 slideActionBar(false /* slideUp */, false /* animate */); 100 } 101 102 /** 103 * Called to indicate that the user is trying to hide the dialpad. Should be called before any 104 * state changes have actually occurred. 105 */ onDialpadDown()106 public void onDialpadDown() { 107 LogUtil.d( 108 "ActionBarController.onDialpadDown", 109 "isInSearchUi: %b, hasSearchQuery: %b, isFadedOut: %b, isExpanded: %b", 110 mActivityUi.isInSearchUi(), 111 mActivityUi.hasSearchQuery(), 112 mSearchBox.isFadedOut(), 113 mSearchBox.isExpanded()); 114 if (mActivityUi.isInSearchUi()) { 115 if (mActivityUi.hasSearchQuery()) { 116 if (mSearchBox.isFadedOut()) { 117 mSearchBox.setVisible(true); 118 } 119 if (!mSearchBox.isExpanded()) { 120 mSearchBox.expand(false /* animate */, false /* requestFocus */); 121 } 122 slideActionBar(false /* slideUp */, true /* animate */); 123 } else { 124 mSearchBox.fadeIn(mFadeInCallback); 125 } 126 } 127 } 128 129 /** 130 * Called to indicate that the user is trying to show the dialpad. Should be called before any 131 * state changes have actually occurred. 132 */ onDialpadUp()133 public void onDialpadUp() { 134 LogUtil.d("ActionBarController.onDialpadUp", "isInSearchUi " + mActivityUi.isInSearchUi()); 135 if (mActivityUi.isInSearchUi()) { 136 slideActionBar(true /* slideUp */, true /* animate */); 137 } else { 138 // From the lists fragment 139 mSearchBox.fadeOut(mFadeOutCallback); 140 } 141 } 142 slideActionBar(boolean slideUp, boolean animate)143 public void slideActionBar(boolean slideUp, boolean animate) { 144 LogUtil.d("ActionBarController.slidingActionBar", "up: %b, animate: %b", slideUp, animate); 145 146 if (mAnimator != null && mAnimator.isRunning()) { 147 mAnimator.cancel(); 148 mAnimator.removeAllUpdateListeners(); 149 } 150 if (animate) { 151 mAnimator = slideUp ? ValueAnimator.ofFloat(0, 1) : ValueAnimator.ofFloat(1, 0); 152 mAnimator.addUpdateListener( 153 animation -> { 154 final float value = (float) animation.getAnimatedValue(); 155 setHideOffset((int) (mActivityUi.getActionBarHeight() * value)); 156 }); 157 mAnimator.start(); 158 } else { 159 setHideOffset(slideUp ? mActivityUi.getActionBarHeight() : 0); 160 } 161 mIsActionBarSlidUp = slideUp; 162 } 163 setAlpha(float alphaValue)164 public void setAlpha(float alphaValue) { 165 mSearchBox.animate().alpha(alphaValue).start(); 166 } 167 setHideOffset(int offset)168 private void setHideOffset(int offset) { 169 mActivityUi.setActionBarHideOffset(offset); 170 } 171 172 /** Saves the current state of the action bar into a provided {@link Bundle} */ saveInstanceState(Bundle outState)173 public void saveInstanceState(Bundle outState) { 174 outState.putBoolean(KEY_IS_SLID_UP, mIsActionBarSlidUp); 175 outState.putBoolean(KEY_IS_FADED_OUT, mSearchBox.isFadedOut()); 176 outState.putBoolean(KEY_IS_EXPANDED, mSearchBox.isExpanded()); 177 } 178 179 /** Restores the action bar state from a provided {@link Bundle}. */ restoreInstanceState(Bundle inState)180 public void restoreInstanceState(Bundle inState) { 181 mIsActionBarSlidUp = inState.getBoolean(KEY_IS_SLID_UP); 182 183 final boolean isSearchBoxFadedOut = inState.getBoolean(KEY_IS_FADED_OUT); 184 if (isSearchBoxFadedOut) { 185 if (!mSearchBox.isFadedOut()) { 186 mSearchBox.setVisible(false); 187 } 188 } else if (mSearchBox.isFadedOut()) { 189 mSearchBox.setVisible(true); 190 } 191 192 final boolean isSearchBoxExpanded = inState.getBoolean(KEY_IS_EXPANDED); 193 if (isSearchBoxExpanded) { 194 if (!mSearchBox.isExpanded()) { 195 mSearchBox.expand(false, false); 196 } 197 } else if (mSearchBox.isExpanded()) { 198 mSearchBox.collapse(false); 199 } 200 } 201 202 /** 203 * This should be called after onCreateOptionsMenu has been called, when the actionbar has been 204 * laid out and actually has a height. 205 */ restoreActionBarOffset()206 public void restoreActionBarOffset() { 207 slideActionBar(mIsActionBarSlidUp /* slideUp */, false /* animate */); 208 } 209 210 public interface ActivityUi { 211 isInSearchUi()212 boolean isInSearchUi(); 213 hasSearchQuery()214 boolean hasSearchQuery(); 215 getActionBarHeight()216 int getActionBarHeight(); 217 setActionBarHideOffset(int offset)218 void setActionBarHideOffset(int offset); 219 } 220 } 221