diff options
author | Dianne Hackborn <hackbod@google.com> | 2012-03-30 17:36:32 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-03-30 17:36:32 -0700 |
commit | 61d6c8ca49d4a3d5bf4c961878a3f71145d75058 (patch) | |
tree | dac339e3067c6e8cd20e759f1d1e5b0d402d05e4 /core/java/com | |
parent | 96695f9ba04f85b24f906c59673e92f00f89adbe (diff) | |
parent | 3a3a6cfd8ec12208ca75c0d0d871d19d76c34194 (diff) | |
download | frameworks_base-61d6c8ca49d4a3d5bf4c961878a3f71145d75058.zip frameworks_base-61d6c8ca49d4a3d5bf4c961878a3f71145d75058.tar.gz frameworks_base-61d6c8ca49d4a3d5bf4c961878a3f71145d75058.tar.bz2 |
Merge "Add new feature to let apps layout over status bar / system bar."
Diffstat (limited to 'core/java/com')
4 files changed, 275 insertions, 29 deletions
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index f3486bd..3115eff 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -21,6 +21,7 @@ import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; +import com.android.internal.widget.ActionBarOverlayLayout; import com.android.internal.widget.ActionBarView; import com.android.internal.widget.ScrollingTabContainerView; @@ -47,6 +48,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.accessibility.AccessibilityEvent; import android.widget.SpinnerAdapter; @@ -69,7 +71,9 @@ public class ActionBarImpl extends ActionBar { private Activity mActivity; private Dialog mDialog; + private ActionBarOverlayLayout mOverlayLayout; private ActionBarContainer mContainerView; + private ViewGroup mTopVisibilityView; private ActionBarView mActionView; private ActionBarContextView mContextView; private ActionBarContainer mSplitView; @@ -100,6 +104,8 @@ public class ActionBarImpl extends ActionBar { final Handler mHandler = new Handler(); Runnable mTabSelector; + private int mCurWindowVisibility = View.VISIBLE; + private Animator mCurrentShowAnim; private Animator mCurrentModeAnim; private boolean mShowHideAnimationEnabled; @@ -110,12 +116,12 @@ public class ActionBarImpl extends ActionBar { public void onAnimationEnd(Animator animation) { if (mContentView != null) { mContentView.setTranslationY(0); - mContainerView.setTranslationY(0); + mTopVisibilityView.setTranslationY(0); } if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { mSplitView.setVisibility(View.GONE); } - mContainerView.setVisibility(View.GONE); + mTopVisibilityView.setVisibility(View.GONE); mContainerView.setTransitioning(false); mCurrentShowAnim = null; completeDeferredDestroyActionMode(); @@ -126,7 +132,7 @@ public class ActionBarImpl extends ActionBar { @Override public void onAnimationEnd(Animator animation) { mCurrentShowAnim = null; - mContainerView.requestLayout(); + mTopVisibilityView.requestLayout(); } }; @@ -147,11 +153,21 @@ public class ActionBarImpl extends ActionBar { private void init(View decor) { mContext = decor.getContext(); + mOverlayLayout = (ActionBarOverlayLayout) decor.findViewById( + com.android.internal.R.id.action_bar_overlay_layout); + if (mOverlayLayout != null) { + mOverlayLayout.setActionBar(this); + } mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar); mContextView = (ActionBarContextView) decor.findViewById( com.android.internal.R.id.action_context_bar); mContainerView = (ActionBarContainer) decor.findViewById( com.android.internal.R.id.action_bar_container); + mTopVisibilityView = (ViewGroup)decor.findViewById( + com.android.internal.R.id.top_action_bar); + if (mTopVisibilityView == null) { + mTopVisibilityView = mContainerView; + } mSplitView = (ActionBarContainer) decor.findViewById( com.android.internal.R.id.split_action_bar); @@ -190,11 +206,22 @@ public class ActionBarImpl extends ActionBar { } final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS; if (mTabScrollView != null) { - mTabScrollView.setVisibility(isInTabMode ? View.VISIBLE : View.GONE); + if (isInTabMode) { + mTabScrollView.setVisibility(View.VISIBLE); + if (mOverlayLayout != null) { + mOverlayLayout.requestFitSystemWindows(); + } + } else { + mTabScrollView.setVisibility(View.GONE); + } } mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode); } + public boolean hasNonEmbeddedTabs() { + return !mHasEmbeddedTabs && getNavigationMode() == NAVIGATION_MODE_TABS; + } + private void ensureTabsExist() { if (mTabScrollView != null) { return; @@ -206,8 +233,14 @@ public class ActionBarImpl extends ActionBar { tabScroller.setVisibility(View.VISIBLE); mActionView.setEmbeddedTabView(tabScroller); } else { - tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ? - View.VISIBLE : View.GONE); + if (getNavigationMode() == NAVIGATION_MODE_TABS) { + tabScroller.setVisibility(View.VISIBLE); + if (mOverlayLayout != null) { + mOverlayLayout.requestFitSystemWindows(); + } + } else { + tabScroller.setVisibility(View.GONE); + } mContainerView.setTabContainer(tabScroller); } mTabScrollView = tabScroller; @@ -221,6 +254,10 @@ public class ActionBarImpl extends ActionBar { } } + public void setWindowVisibility(int visibility) { + mCurWindowVisibility = visibility; + } + /** * Enables or disables animation between show/hide states. * If animation is disabled using this method, animations in progress @@ -396,7 +433,12 @@ public class ActionBarImpl extends ActionBar { animateToMode(true); if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { // TODO animate this - mSplitView.setVisibility(View.VISIBLE); + if (mSplitView.getVisibility() != View.VISIBLE) { + mSplitView.setVisibility(View.VISIBLE); + if (mOverlayLayout != null) { + mOverlayLayout.requestFitSystemWindows(); + } + } } mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); mActionMode = mode; @@ -530,28 +572,29 @@ public class ActionBarImpl extends ActionBar { @Override public void show() { - show(true); + show(true, false); } - void show(boolean markHiddenBeforeMode) { + public void show(boolean markHiddenBeforeMode, boolean alwaysAnimate) { if (mCurrentShowAnim != null) { mCurrentShowAnim.end(); } - if (mContainerView.getVisibility() == View.VISIBLE) { + if (mTopVisibilityView.getVisibility() == View.VISIBLE) { if (markHiddenBeforeMode) mWasHiddenBeforeMode = false; return; } - mContainerView.setVisibility(View.VISIBLE); + mTopVisibilityView.setVisibility(View.VISIBLE); - if (mShowHideAnimationEnabled) { - mContainerView.setAlpha(0); + if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled + || alwaysAnimate)) { + mTopVisibilityView.setAlpha(0); AnimatorSet anim = new AnimatorSet(); - AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1)); + AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mTopVisibilityView, "alpha", 1)); if (mContentView != null) { b.with(ObjectAnimator.ofFloat(mContentView, "translationY", - -mContainerView.getHeight(), 0)); - mContainerView.setTranslationY(-mContainerView.getHeight()); - b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0)); + -mTopVisibilityView.getHeight(), 0)); + mTopVisibilityView.setTranslationY(-mTopVisibilityView.getHeight()); + b.with(ObjectAnimator.ofFloat(mTopVisibilityView, "translationY", 0)); } if (mSplitView != null && mContextDisplayMode == CONTEXT_DISPLAY_SPLIT) { mSplitView.setAlpha(0); @@ -562,7 +605,7 @@ public class ActionBarImpl extends ActionBar { mCurrentShowAnim = anim; anim.start(); } else { - mContainerView.setAlpha(1); + mTopVisibilityView.setAlpha(1); mContainerView.setTranslationY(0); mShowListener.onAnimationEnd(null); } @@ -570,23 +613,28 @@ public class ActionBarImpl extends ActionBar { @Override public void hide() { + hide(false); + } + + public void hide(boolean alwaysAnimate) { if (mCurrentShowAnim != null) { mCurrentShowAnim.end(); } - if (mContainerView.getVisibility() == View.GONE) { + if (mTopVisibilityView.getVisibility() == View.GONE) { return; } - if (mShowHideAnimationEnabled) { - mContainerView.setAlpha(1); + if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled + || alwaysAnimate)) { + mTopVisibilityView.setAlpha(1); mContainerView.setTransitioning(true); AnimatorSet anim = new AnimatorSet(); - AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0)); + AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mTopVisibilityView, "alpha", 0)); if (mContentView != null) { b.with(ObjectAnimator.ofFloat(mContentView, "translationY", - 0, -mContainerView.getHeight())); - b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", - -mContainerView.getHeight())); + 0, -mTopVisibilityView.getHeight())); + b.with(ObjectAnimator.ofFloat(mTopVisibilityView, "translationY", + -mTopVisibilityView.getHeight())); } if (mSplitView != null && mSplitView.getVisibility() == View.VISIBLE) { mSplitView.setAlpha(1); @@ -601,12 +649,12 @@ public class ActionBarImpl extends ActionBar { } public boolean isShowing() { - return mContainerView.getVisibility() == View.VISIBLE; + return mTopVisibilityView.getVisibility() == View.VISIBLE; } void animateToMode(boolean toActionMode) { if (toActionMode) { - show(false); + show(false, false); } if (mCurrentModeAnim != null) { mCurrentModeAnim.end(); @@ -980,6 +1028,11 @@ public class ActionBarImpl extends ActionBar { mTabScrollView.setVisibility(View.GONE); break; } + if (oldMode != mode && !mHasEmbeddedTabs) { + if (mOverlayLayout != null) { + mOverlayLayout.requestFitSystemWindows(); + } + } mActionView.setNavigationMode(mode); switch (mode) { case NAVIGATION_MODE_TABS: diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 6e36fdb..294d4c4 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -30,7 +30,7 @@ oneway interface IStatusBar void disable(int state); void animateExpand(); void animateCollapse(); - void setSystemUiVisibility(int vis); + void setSystemUiVisibility(int vis, int mask); void topAppWindowChanged(boolean menuVisible); void setImeWindowStatus(in IBinder token, int vis, int backDisposition); void setHardKeyboardStatus(boolean available, boolean enabled); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 118e541..c64f170 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -44,7 +44,7 @@ interface IStatusBarService int uid, int initialPid, String message); void onClearAllNotifications(); void onNotificationClear(String pkg, String tag, int id); - void setSystemUiVisibility(int vis); + void setSystemUiVisibility(int vis, int mask); void setHardKeyboardEnabled(boolean enabled); void toggleRecentApps(); void preloadRecentApps(); diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java new file mode 100644 index 0000000..5f36bb6 --- /dev/null +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import com.android.internal.app.ActionBarImpl; + +import android.animation.LayoutTransition; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.FrameLayout; + +/** + * Special layout for the containing of an overlay action bar (and its + * content) to correctly handle fitting system windows when the content + * has request that its layout ignore them. + */ +public class ActionBarOverlayLayout extends FrameLayout { + private int mActionBarHeight; + private ActionBarImpl mActionBar; + private int mWindowVisibility = View.VISIBLE; + private View mContent; + private View mActionBarTop; + private ActionBarContainer mContainerView; + private ActionBarView mActionView; + private View mActionBarBottom; + private int mLastSystemUiVisibility; + private final Rect mZeroRect = new Rect(0, 0, 0, 0); + + static final int[] mActionBarSizeAttr = new int [] { + com.android.internal.R.attr.actionBarSize + }; + + public ActionBarOverlayLayout(Context context) { + super(context); + init(context); + } + + public ActionBarOverlayLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + private void init(Context context) { + TypedArray ta = getContext().getTheme().obtainStyledAttributes(mActionBarSizeAttr); + mActionBarHeight = ta.getDimensionPixelSize(0, 0); + ta.recycle(); + } + + public void setActionBar(ActionBarImpl impl) { + mActionBar = impl; + if (getWindowToken() != null) { + // This is being initialized after being added to a window; + // make sure to update all state now. + mActionBar.setWindowVisibility(mWindowVisibility); + if (mLastSystemUiVisibility != 0) { + int newVis = mLastSystemUiVisibility; + onWindowSystemUiVisibilityChanged(newVis); + requestFitSystemWindows(); + } + } + } + + @Override + public void onWindowSystemUiVisibilityChanged(int visible) { + super.onWindowSystemUiVisibilityChanged(visible); + pullChildren(); + final int diff = mLastSystemUiVisibility ^ visible; + mLastSystemUiVisibility = visible; + final boolean barVisible = (visible&SYSTEM_UI_FLAG_FULLSCREEN) == 0; + final boolean wasVisible = mActionBar != null ? mActionBar.isShowing() : true; + if (barVisible != wasVisible || (diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { + if (mActionBar != null) { + if (barVisible) mActionBar.show(true, true); + else mActionBar.hide(true); + requestFitSystemWindows(); + } + } + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + mWindowVisibility = visibility; + if (mActionBar != null) { + mActionBar.setWindowVisibility(visibility); + } + } + + private boolean applyInsets(View view, Rect insets, boolean left, boolean top, + boolean bottom, boolean right) { + boolean changed = false; + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)view.getLayoutParams(); + if (left && lp.leftMargin != insets.left) { + changed = true; + lp.leftMargin = insets.left; + } + if (top && lp.topMargin != insets.top) { + changed = true; + lp.topMargin = insets.top; + } + if (right && lp.rightMargin != insets.right) { + changed = true; + lp.rightMargin = insets.right; + } + if (bottom && lp.bottomMargin != insets.bottom) { + changed = true; + lp.bottomMargin = insets.bottom; + } + return changed; + } + + @Override + protected boolean fitSystemWindows(Rect insets) { + pullChildren(); + + final int vis = getWindowSystemUiVisibility(); + final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0; + + // The top and bottom action bars are always within the content area. + boolean changed = applyInsets(mActionBarTop, insets, true, true, false, true); + changed |= applyInsets(mActionBarBottom, insets, true, false, true, true); + + // If the window has not requested system UI layout flags, we need to + // make sure its content is not being covered by system UI... though it + // will still be covered by the action bar since they have requested it to + // overlay. + if ((vis & SYSTEM_UI_LAYOUT_FLAGS) == 0) { + changed |= applyInsets(mContent, insets, true, true, true, true); + // The insets are now consumed. + insets.set(0, 0, 0, 0); + } else { + changed |= applyInsets(mContent, mZeroRect, true, true, true, true); + } + + + if (stable || mActionBarTop.getVisibility() == VISIBLE) { + // The action bar creates additional insets for its content to use. + insets.top += mActionBarHeight; + } + + if (mActionBar != null && mActionBar.hasNonEmbeddedTabs()) { + View tabs = mContainerView.getTabContainer(); + if (stable || (tabs != null && tabs.getVisibility() == VISIBLE)) { + // If tabs are not embedded, adjust insets to account for them. + insets.top += mActionBarHeight; + } + } + + if (mActionView.isSplitActionBar()) { + if (stable || (mActionBarBottom != null + && mActionBarBottom.getVisibility() == VISIBLE)) { + // If action bar is split, adjust buttom insets for it. + insets.bottom += mActionBarHeight; + } + } + + if (changed) { + requestLayout(); + } + + return super.fitSystemWindows(insets); + } + + void pullChildren() { + if (mContent == null) { + mContent = findViewById(com.android.internal.R.id.content); + mActionBarTop = findViewById(com.android.internal.R.id.top_action_bar); + mContainerView = (ActionBarContainer)findViewById( + com.android.internal.R.id.action_bar_container); + mActionView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); + mActionBarBottom = findViewById(com.android.internal.R.id.split_action_bar); + } + } +} |