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 ActionBar getActionBar(); 50 } 51 52 public ActionBarController(ActivityUi activityUi, SearchEditTextLayout searchBox) { 53 mActivityUi = activityUi; 54 mSearchBox = searchBox; 55 } 56 57 /** 58 * @return Whether or not the action bar is currently showing (both slid down and visible) 59 */ 60 public boolean isActionBarShowing() { 61 return !mIsActionBarSlidUp && !mSearchBox.isFadedOut(); 62 } 63 64 /** 65 * Called when the user has tapped on the collapsed search box, to start a new search query. 66 */ 67 public void onSearchBoxTapped() { 68 if (DEBUG) { 69 Log.d(TAG, "OnSearchBoxTapped: isInSearchUi " + mActivityUi.isInSearchUi()); 70 } 71 if (!mActivityUi.isInSearchUi()) { 72 mSearchBox.expand(true /* animate */, true /* requestFocus */); 73 } 74 } 75 76 /** 77 * Called when search UI has been exited for some reason. 78 */ 79 public void onSearchUiExited() { 80 if (DEBUG) { 81 Log.d(TAG, "OnSearchUIExited: isExpanded " + mSearchBox.isExpanded() 82 + " isFadedOut: " + mSearchBox.isFadedOut() 83 + " shouldShowActionBar: " + mActivityUi.shouldShowActionBar()); 84 } 85 if (mSearchBox.isExpanded()) { 86 mSearchBox.collapse(true /* animate */); 87 } 88 if (mSearchBox.isFadedOut()) { 89 mSearchBox.fadeIn(); 90 } 91 92 if (mActivityUi.shouldShowActionBar()) { 93 slideActionBar(false /* slideUp */, false /* animate */); 94 } else { 95 slideActionBar(true /* slideUp */, false /* animate */); 96 } 97 } 98 99 /** 100 * Called to indicate that the user is trying to hide the dialpad. Should be called before 101 * any state changes have actually occurred. 102 */ 103 public void onDialpadDown() { 104 if (DEBUG) { 105 Log.d(TAG, "OnDialpadDown: isInSearchUi " + mActivityUi.isInSearchUi() 106 + " hasSearchQuery: " + mActivityUi.hasSearchQuery() 107 + " isFadedOut: " + mSearchBox.isFadedOut() 108 + " isExpanded: " + mSearchBox.isExpanded()); 109 } 110 if (mActivityUi.isInSearchUi()) { 111 if (mActivityUi.hasSearchQuery()) { 112 if (mSearchBox.isFadedOut()) { 113 mSearchBox.setVisible(true); 114 } 115 if (!mSearchBox.isExpanded()) { 116 mSearchBox.expand(false /* animate */, false /* requestFocus */); 117 } 118 slideActionBar(false /* slideUp */, true /* animate */); 119 } else { 120 mSearchBox.fadeIn(); 121 } 122 } 123 } 124 125 /** 126 * Called to indicate that the user is trying to show the dialpad. Should be called before 127 * any state changes have actually occurred. 128 */ 129 public void onDialpadUp() { 130 if (DEBUG) { 131 Log.d(TAG, "OnDialpadUp: isInSearchUi " + mActivityUi.isInSearchUi()); 132 } 133 if (mActivityUi.isInSearchUi()) { 134 slideActionBar(true /* slideUp */, true /* animate */); 135 } else { 136 // From the lists fragment 137 mSearchBox.fadeOut(mFadeOutCallback); 138 } 139 } 140 141 public void slideActionBar(boolean slideUp, boolean animate) { 142 if (DEBUG) { 143 Log.d(TAG, "Sliding actionBar - up: " + slideUp + " animate: " + animate); 144 } 145 if (animate) { 146 ValueAnimator animator = 147 slideUp ? ValueAnimator.ofFloat(0, 1) : ValueAnimator.ofFloat(1, 0); 148 animator.addUpdateListener(new AnimatorUpdateListener() { 149 @Override 150 public void onAnimationUpdate(ValueAnimator animation) { 151 final float value = (float) animation.getAnimatedValue(); 152 setHideOffset( 153 (int) (mActivityUi.getActionBarHeight() * value)); 154 } 155 }); 156 animator.start(); 157 } else { 158 setHideOffset(slideUp ? mActivityUi.getActionBarHeight() : 0); 159 } 160 mIsActionBarSlidUp = slideUp; 161 } 162 163 public void setAlpha(float alphaValue) { 164 mSearchBox.animate().alpha(alphaValue).start(); 165 } 166 167 public void setHideOffset(int offset) { 168 mIsActionBarSlidUp = offset >= mActivityUi.getActionBarHeight(); 169 mActivityUi.getActionBar().setHideOffset(offset); 170 } 171 172 /** 173 * @return The offset the action bar is being translated upwards by 174 */ 175 public int getHideOffset() { 176 return mActivityUi.getActionBar().getHideOffset(); 177 } 178 179 /** 180 * Saves the current state of the action bar into a provided {@link Bundle} 181 */ 182 public void saveInstanceState(Bundle outState) { 183 outState.putBoolean(KEY_IS_SLID_UP, mIsActionBarSlidUp); 184 outState.putBoolean(KEY_IS_FADED_OUT, mSearchBox.isFadedOut()); 185 outState.putBoolean(KEY_IS_EXPANDED, mSearchBox.isExpanded()); 186 } 187 188 /** 189 * Restores the action bar state from a provided {@link Bundle}. 190 */ 191 public void restoreInstanceState(Bundle inState) { 192 mIsActionBarSlidUp = inState.getBoolean(KEY_IS_SLID_UP); 193 194 final boolean isSearchBoxFadedOut = inState.getBoolean(KEY_IS_FADED_OUT); 195 if (isSearchBoxFadedOut) { 196 if (!mSearchBox.isFadedOut()) { 197 mSearchBox.setVisible(false); 198 } 199 } else if (mSearchBox.isFadedOut()) { 200 mSearchBox.setVisible(true); 201 } 202 203 final boolean isSearchBoxExpanded = inState.getBoolean(KEY_IS_EXPANDED); 204 if (isSearchBoxExpanded) { 205 if (!mSearchBox.isExpanded()) { 206 mSearchBox.expand(false, false); 207 } 208 } else if (mSearchBox.isExpanded()) { 209 mSearchBox.collapse(false); 210 } 211 } 212 213 /** 214 * This should be called after onCreateOptionsMenu has been called, when the actionbar has 215 * been laid out and actually has a height. 216 */ 217 public void restoreActionBarOffset() { 218 slideActionBar(mIsActionBarSlidUp /* slideUp */, false /* animate */); 219 } 220 221 @VisibleForTesting 222 public boolean getIsActionBarSlidUp() { 223 return mIsActionBarSlidUp; 224 } 225 } 226