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 56 public AbsActionBarView(Context context) { 57 this(context, null); 58 } 59 60 public AbsActionBarView(Context context, AttributeSet attrs) { 61 this(context, attrs, 0); 62 } 63 64 public AbsActionBarView(Context context, AttributeSet attrs, int defStyleAttr) { 65 this(context, attrs, defStyleAttr, 0); 66 } 67 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 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 */ 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 */ 112 public void setSplitWhenNarrow(boolean splitWhenNarrow) { 113 mSplitWhenNarrow = splitWhenNarrow; 114 } 115 116 public void setContentHeight(int height) { 117 mContentHeight = height; 118 requestLayout(); 119 } 120 121 public int getContentHeight() { 122 return mContentHeight; 123 } 124 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 */ 132 public int getAnimatedVisibility() { 133 if (mVisibilityAnim != null) { 134 return mVisAnimListener.mFinalVisibility; 135 } 136 return getVisibility(); 137 } 138 139 public void animateToVisibility(int visibility) { 140 if (mVisibilityAnim != null) { 141 mVisibilityAnim.cancel(); 142 } 143 if (visibility == VISIBLE) { 144 if (getVisibility() != VISIBLE) { 145 setAlpha(0); 146 if (mSplitView != null && mMenuView != null) { 147 mMenuView.setAlpha(0); 148 } 149 } 150 ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1); 151 anim.setDuration(FADE_DURATION); 152 anim.setInterpolator(sAlphaInterpolator); 153 if (mSplitView != null && mMenuView != null) { 154 AnimatorSet set = new AnimatorSet(); 155 ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 1); 156 splitAnim.setDuration(FADE_DURATION); 157 set.addListener(mVisAnimListener.withFinalVisibility(visibility)); 158 set.play(anim).with(splitAnim); 159 set.start(); 160 } else { 161 anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); 162 anim.start(); 163 } 164 } else { 165 ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0); 166 anim.setDuration(FADE_DURATION); 167 anim.setInterpolator(sAlphaInterpolator); 168 if (mSplitView != null && mMenuView != null) { 169 AnimatorSet set = new AnimatorSet(); 170 ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 0); 171 splitAnim.setDuration(FADE_DURATION); 172 set.addListener(mVisAnimListener.withFinalVisibility(visibility)); 173 set.play(anim).with(splitAnim); 174 set.start(); 175 } else { 176 anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); 177 anim.start(); 178 } 179 } 180 } 181 182 @Override 183 public void setVisibility(int visibility) { 184 if (visibility != getVisibility()) { 185 if (mVisibilityAnim != null) { 186 mVisibilityAnim.end(); 187 } 188 super.setVisibility(visibility); 189 } 190 } 191 192 public boolean showOverflowMenu() { 193 if (mActionMenuPresenter != null) { 194 return mActionMenuPresenter.showOverflowMenu(); 195 } 196 return false; 197 } 198 199 public void postShowOverflowMenu() { 200 post(new Runnable() { 201 public void run() { 202 showOverflowMenu(); 203 } 204 }); 205 } 206 207 public boolean hideOverflowMenu() { 208 if (mActionMenuPresenter != null) { 209 return mActionMenuPresenter.hideOverflowMenu(); 210 } 211 return false; 212 } 213 214 public boolean isOverflowMenuShowing() { 215 if (mActionMenuPresenter != null) { 216 return mActionMenuPresenter.isOverflowMenuShowing(); 217 } 218 return false; 219 } 220 221 public boolean isOverflowMenuShowPending() { 222 if (mActionMenuPresenter != null) { 223 return mActionMenuPresenter.isOverflowMenuShowPending(); 224 } 225 return false; 226 } 227 228 public boolean isOverflowReserved() { 229 return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved(); 230 } 231 232 public boolean canShowOverflowMenu() { 233 return isOverflowReserved() && getVisibility() == VISIBLE; 234 } 235 236 public void dismissPopupMenus() { 237 if (mActionMenuPresenter != null) { 238 mActionMenuPresenter.dismissPopupMenus(); 239 } 240 } 241 242 protected int measureChildView(View child, int availableWidth, int childSpecHeight, 243 int spacing) { 244 child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), 245 childSpecHeight); 246 247 availableWidth -= child.getMeasuredWidth(); 248 availableWidth -= spacing; 249 250 return Math.max(0, availableWidth); 251 } 252 253 static protected int next(int x, int val, boolean isRtl) { 254 return isRtl ? x - val : x + val; 255 } 256 257 protected int positionChild(View child, int x, int y, int contentHeight, boolean reverse) { 258 int childWidth = child.getMeasuredWidth(); 259 int childHeight = child.getMeasuredHeight(); 260 int childTop = y + (contentHeight - childHeight) / 2; 261 262 if (reverse) { 263 child.layout(x - childWidth, childTop, x, childTop + childHeight); 264 } else { 265 child.layout(x, childTop, x + childWidth, childTop + childHeight); 266 } 267 268 return (reverse ? -childWidth : childWidth); 269 } 270 271 protected class VisibilityAnimListener implements Animator.AnimatorListener { 272 private boolean mCanceled = false; 273 int mFinalVisibility; 274 275 public VisibilityAnimListener withFinalVisibility(int visibility) { 276 mFinalVisibility = visibility; 277 return this; 278 } 279 280 @Override 281 public void onAnimationStart(Animator animation) { 282 setVisibility(VISIBLE); 283 mVisibilityAnim = animation; 284 mCanceled = false; 285 } 286 287 @Override 288 public void onAnimationEnd(Animator animation) { 289 if (mCanceled) return; 290 291 mVisibilityAnim = null; 292 setVisibility(mFinalVisibility); 293 if (mSplitView != null && mMenuView != null) { 294 mMenuView.setVisibility(mFinalVisibility); 295 } 296 } 297 298 @Override 299 public void onAnimationCancel(Animator animation) { 300 mCanceled = true; 301 } 302 303 @Override 304 public void onAnimationRepeat(Animator animation) { 305 } 306 } 307 } 308