1 /* 2 * Copyright (C) 2011 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.internal.widget; 17 18 import com.android.internal.R; 19 20 import android.util.TypedValue; 21 import android.view.ContextThemeWrapper; 22 import android.widget.ActionMenuPresenter; 23 import android.widget.ActionMenuView; 24 25 import android.animation.Animator; 26 import android.animation.AnimatorSet; 27 import android.animation.ObjectAnimator; 28 import android.animation.TimeInterpolator; 29 import android.content.Context; 30 import android.content.res.Configuration; 31 import android.content.res.TypedArray; 32 import android.util.AttributeSet; 33 import android.view.View; 34 import android.view.ViewGroup; 35 import android.view.animation.DecelerateInterpolator; 36 37 public abstract class AbsActionBarView extends ViewGroup { 38 private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator(); 39 40 private static final int FADE_DURATION = 200; 41 42 protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener(); 43 44 /** Context against which to inflate popup menus. */ 45 protected final Context mPopupContext; 46 47 protected ActionMenuView mMenuView; 48 protected ActionMenuPresenter mActionMenuPresenter; 49 protected ViewGroup mSplitView; 50 protected boolean mSplitActionBar; 51 protected boolean mSplitWhenNarrow; 52 protected int mContentHeight; 53 54 protected Animator mVisibilityAnim; 55 AbsActionBarView(Context context)56 public AbsActionBarView(Context context) { 57 this(context, null); 58 } 59 AbsActionBarView(Context context, AttributeSet attrs)60 public AbsActionBarView(Context context, AttributeSet attrs) { 61 this(context, attrs, 0); 62 } 63 AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr)64 public AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr) { 65 this(context, attrs, defStyleAttr, 0); 66 } 67 AbsActionBarView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)68 public AbsActionBarView( 69 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 70 super(context, attrs, defStyleAttr, defStyleRes); 71 72 final TypedValue tv = new TypedValue(); 73 if (context.getTheme().resolveAttribute(R.attr.actionBarPopupTheme, tv, true) 74 && tv.resourceId != 0) { 75 mPopupContext = new ContextThemeWrapper(context, tv.resourceId); 76 } else { 77 mPopupContext = context; 78 } 79 } 80 81 @Override onConfigurationChanged(Configuration newConfig)82 protected void onConfigurationChanged(Configuration newConfig) { 83 super.onConfigurationChanged(newConfig); 84 85 // Action bar can change size on configuration changes. 86 // Reread the desired height from the theme-specified style. 87 TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar, 88 com.android.internal.R.attr.actionBarStyle, 0); 89 setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0)); 90 a.recycle(); 91 if (mSplitWhenNarrow) { 92 setSplitToolbar(getContext().getResources().getBoolean( 93 com.android.internal.R.bool.split_action_bar_is_narrow)); 94 } 95 if (mActionMenuPresenter != null) { 96 mActionMenuPresenter.onConfigurationChanged(newConfig); 97 } 98 } 99 100 /** 101 * Sets whether the bar should be split right now, no questions asked. 102 * @param split true if the bar should split 103 */ setSplitToolbar(boolean split)104 public void setSplitToolbar(boolean split) { 105 mSplitActionBar = split; 106 } 107 108 /** 109 * Sets whether the bar should split if we enter a narrow screen configuration. 110 * @param splitWhenNarrow true if the bar should check to split after a config change 111 */ setSplitWhenNarrow(boolean splitWhenNarrow)112 public void setSplitWhenNarrow(boolean splitWhenNarrow) { 113 mSplitWhenNarrow = splitWhenNarrow; 114 } 115 setContentHeight(int height)116 public void setContentHeight(int height) { 117 mContentHeight = height; 118 requestLayout(); 119 } 120 getContentHeight()121 public int getContentHeight() { 122 return mContentHeight; 123 } 124 setSplitView(ViewGroup splitView)125 public void setSplitView(ViewGroup splitView) { 126 mSplitView = splitView; 127 } 128 129 /** 130 * @return Current visibility or if animating, the visibility being animated to. 131 */ getAnimatedVisibility()132 public int getAnimatedVisibility() { 133 if (mVisibilityAnim != null) { 134 return mVisAnimListener.mFinalVisibility; 135 } 136 return getVisibility(); 137 } 138 setupAnimatorToVisibility(int visibility, long duration)139 public Animator setupAnimatorToVisibility(int visibility, long duration) { 140 if (mVisibilityAnim != null) { 141 mVisibilityAnim.cancel(); 142 } 143 144 if (visibility == VISIBLE) { 145 if (getVisibility() != VISIBLE) { 146 setAlpha(0); 147 if (mSplitView != null && mMenuView != null) { 148 mMenuView.setAlpha(0); 149 } 150 } 151 ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 1); 152 anim.setDuration(duration); 153 anim.setInterpolator(sAlphaInterpolator); 154 if (mSplitView != null && mMenuView != null) { 155 AnimatorSet set = new AnimatorSet(); 156 ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 1); 157 splitAnim.setDuration(duration); 158 set.addListener(mVisAnimListener.withFinalVisibility(visibility)); 159 set.play(anim).with(splitAnim); 160 return set; 161 } else { 162 anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); 163 return anim; 164 } 165 } else { 166 ObjectAnimator anim = ObjectAnimator.ofFloat(this, View.ALPHA, 0); 167 anim.setDuration(duration); 168 anim.setInterpolator(sAlphaInterpolator); 169 if (mSplitView != null && mMenuView != null) { 170 AnimatorSet set = new AnimatorSet(); 171 ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, View.ALPHA, 0); 172 splitAnim.setDuration(duration); 173 set.addListener(mVisAnimListener.withFinalVisibility(visibility)); 174 set.play(anim).with(splitAnim); 175 return set; 176 } else { 177 anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); 178 return anim; 179 } 180 } 181 } 182 animateToVisibility(int visibility)183 public void animateToVisibility(int visibility) { 184 Animator anim = setupAnimatorToVisibility(visibility, FADE_DURATION); 185 anim.start(); 186 } 187 188 @Override setVisibility(int visibility)189 public void setVisibility(int visibility) { 190 if (visibility != getVisibility()) { 191 if (mVisibilityAnim != null) { 192 mVisibilityAnim.end(); 193 } 194 super.setVisibility(visibility); 195 } 196 } 197 showOverflowMenu()198 public boolean showOverflowMenu() { 199 if (mActionMenuPresenter != null) { 200 return mActionMenuPresenter.showOverflowMenu(); 201 } 202 return false; 203 } 204 postShowOverflowMenu()205 public void postShowOverflowMenu() { 206 post(new Runnable() { 207 public void run() { 208 showOverflowMenu(); 209 } 210 }); 211 } 212 hideOverflowMenu()213 public boolean hideOverflowMenu() { 214 if (mActionMenuPresenter != null) { 215 return mActionMenuPresenter.hideOverflowMenu(); 216 } 217 return false; 218 } 219 isOverflowMenuShowing()220 public boolean isOverflowMenuShowing() { 221 if (mActionMenuPresenter != null) { 222 return mActionMenuPresenter.isOverflowMenuShowing(); 223 } 224 return false; 225 } 226 isOverflowMenuShowPending()227 public boolean isOverflowMenuShowPending() { 228 if (mActionMenuPresenter != null) { 229 return mActionMenuPresenter.isOverflowMenuShowPending(); 230 } 231 return false; 232 } 233 isOverflowReserved()234 public boolean isOverflowReserved() { 235 return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved(); 236 } 237 canShowOverflowMenu()238 public boolean canShowOverflowMenu() { 239 return isOverflowReserved() && getVisibility() == VISIBLE; 240 } 241 dismissPopupMenus()242 public void dismissPopupMenus() { 243 if (mActionMenuPresenter != null) { 244 mActionMenuPresenter.dismissPopupMenus(); 245 } 246 } 247 measureChildView(View child, int availableWidth, int childSpecHeight, int spacing)248 protected int measureChildView(View child, int availableWidth, int childSpecHeight, 249 int spacing) { 250 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 251 childSpecHeight); 252 253 availableWidth -= child.getMeasuredWidth(); 254 availableWidth -= spacing; 255 256 return Math.max(0, availableWidth); 257 } 258 next(int x, int val, boolean isRtl)259 static protected int next(int x, int val, boolean isRtl) { 260 return isRtl ? x - val : x + val; 261 } 262 positionChild(View child, int x, int y, int contentHeight, boolean reverse)263 protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) { 264 int childWidth = child.getMeasuredWidth(); 265 int childHeight = child.getMeasuredHeight(); 266 int childTop = y + (contentHeight - childHeight) / 2; 267 268 if (reverse) { 269 child.layout(x - childWidth, childTop, x, childTop + childHeight); 270 } else { 271 child.layout(x, childTop, x + childWidth, childTop + childHeight); 272 } 273 274 return (reverse ? -childWidth : childWidth); 275 } 276 277 protected class VisibilityAnimListener implements Animator.AnimatorListener { 278 private boolean mCanceled = false; 279 int mFinalVisibility; 280 withFinalVisibility(int visibility)281 public VisibilityAnimListener withFinalVisibility(int visibility) { 282 mFinalVisibility = visibility; 283 return this; 284 } 285 286 @Override onAnimationStart(Animator animation)287 public void onAnimationStart(Animator animation) { 288 setVisibility(VISIBLE); 289 mVisibilityAnim = animation; 290 mCanceled = false; 291 } 292 293 @Override onAnimationEnd(Animator animation)294 public void onAnimationEnd(Animator animation) { 295 if (mCanceled) return; 296 297 mVisibilityAnim = null; 298 setVisibility(mFinalVisibility); 299 if (mSplitView != null && mMenuView != null) { 300 mMenuView.setVisibility(mFinalVisibility); 301 } 302 } 303 304 @Override onAnimationCancel(Animator animation)305 public void onAnimationCancel(Animator animation) { 306 mCanceled = true; 307 } 308 309 @Override onAnimationRepeat(Animator animation)310 public void onAnimationRepeat(Animator animation) { 311 } 312 } 313 } 314