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