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