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 | |
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."
23 files changed, 907 insertions, 265 deletions
diff --git a/api/current.txt b/api/current.txt index 233938b5..483283d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -23240,6 +23240,7 @@ package android.view { method public boolean dispatchUnhandledMove(android.view.View, int); method protected void dispatchVisibilityChanged(android.view.View, int); method public void dispatchWindowFocusChanged(boolean); + method public void dispatchWindowSystemUiVisiblityChanged(int); method public void dispatchWindowVisibilityChanged(int); method public void draw(android.graphics.Canvas); method protected void drawableStateChanged(); @@ -23353,6 +23354,7 @@ package android.view { method public int getVisibility(); method public final int getWidth(); method protected int getWindowAttachCount(); + method public int getWindowSystemUiVisibility(); method public android.os.IBinder getWindowToken(); method public int getWindowVisibility(); method public void getWindowVisibleDisplayFrame(android.graphics.Rect); @@ -23456,6 +23458,7 @@ package android.view { method public boolean onTrackballEvent(android.view.MotionEvent); method protected void onVisibilityChanged(android.view.View, int); method public void onWindowFocusChanged(boolean); + method public void onWindowSystemUiVisibilityChanged(int); method protected void onWindowVisibilityChanged(int); method protected boolean overScrollBy(int, int, int, int, int, int, int, int, boolean); method public boolean performClick(); @@ -23473,6 +23476,7 @@ package android.view { method public boolean removeCallbacks(java.lang.Runnable); method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); + method public void requestFitSystemWindows(); method public final boolean requestFocus(); method public final boolean requestFocus(int); method public boolean requestFocus(int, android.graphics.Rect); @@ -23671,9 +23675,14 @@ package android.view { field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000 field public static final deprecated int STATUS_BAR_HIDDEN = 1; // 0x1 field public static final deprecated int STATUS_BAR_VISIBLE = 0; // 0x0 + field public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4 field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2 + field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400 + field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200 + field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100 field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1 field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0 + field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600 field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2 field protected static int TEXT_DIRECTION_DEFAULT; field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1 @@ -24037,6 +24046,7 @@ package android.view { method public abstract void requestChildFocus(android.view.View, android.view.View); method public abstract boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean); method public abstract void requestDisallowInterceptTouchEvent(boolean); + method public abstract void requestFitSystemWindows(); method public abstract void requestLayout(); method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); method public abstract void requestTransparentRegion(android.view.View); diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 24d3a6b..cff16ff 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -611,6 +611,10 @@ public abstract class ActionBar { * If the window hosting the ActionBar does not have the feature * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application * content to fit the new space available. + * + * <p>If you are hiding the ActionBar through + * {@link View#SYSTEM_UI_FLAG_FULLSCREEN View.SYSTEM_UI_FLAG_FULLSCREEN}, + * you should not call this function directly. */ public abstract void show(); @@ -619,6 +623,12 @@ public abstract class ActionBar { * If the window hosting the ActionBar does not have the feature * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application * content to fit the new space available. + * + * <p>Instead of calling this function directly, you can also cause an + * ActionBar using the overlay feature to hide through + * {@link View#SYSTEM_UI_FLAG_FULLSCREEN View.SYSTEM_UI_FLAG_FULLSCREEN}. + * Hiding the ActionBar through this system UI flag allows you to more + * seamlessly hide it in conjunction with other screen decorations. */ public abstract void hide(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6c964b0..81f3f6a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -765,7 +765,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ static final int FILTER_TOUCHES_WHEN_OBSCURED = 0x00000400; - // note flag value 0x00000800 is now available for next flags... + /** + * Set for framework elements that use FITS_SYSTEM_WINDOWS, to indicate + * that they are optional and should be skipped if the window has + * requested system UI flags that ignore those insets for layout. + */ + static final int OPTIONAL_FITS_SYSTEM_WINDOWS = 0x00000800; /** * <p>This view doesn't show fading edges.</p> @@ -1909,28 +1914,31 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public static final int OVER_SCROLL_NEVER = 2; /** - * View has requested the system UI (status bar) to be visible (the default). + * Special constant for {@link #setSystemUiVisibility(int)}: View has + * requested the system UI (status bar) to be visible (the default). * * @see #setSystemUiVisibility(int) */ public static final int SYSTEM_UI_FLAG_VISIBLE = 0; /** - * View has requested the system UI to enter an unobtrusive "low profile" mode. + * Flag for {@link #setSystemUiVisibility(int)}: View has requested the + * system UI to enter an unobtrusive "low profile" mode. * - * This is for use in games, book readers, video players, or any other "immersive" application - * where the usual system chrome is deemed too distracting. + * <p>This is for use in games, book readers, video players, or any other + * "immersive" application where the usual system chrome is deemed too distracting. * - * In low profile mode, the status bar and/or navigation icons may dim. + * <p>In low profile mode, the status bar and/or navigation icons may dim. * * @see #setSystemUiVisibility(int) */ public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001; /** - * View has requested that the system navigation be temporarily hidden. + * Flag for {@link #setSystemUiVisibility(int)}: View has requested that the + * system navigation be temporarily hidden. * - * This is an even less obtrusive state than that called for by + * <p>This is an even less obtrusive state than that called for by * {@link #SYSTEM_UI_FLAG_LOW_PROFILE}; on devices that draw essential navigation controls * (Home, Back, and the like) on screen, <code>SYSTEM_UI_FLAG_HIDE_NAVIGATION</code> will cause * those to disappear. This is useful (in conjunction with the @@ -1938,14 +1946,92 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN FLAG_LAYOUT_IN_SCREEN} * window flags) for displaying content using every last pixel on the display. * - * There is a limitation: because navigation controls are so important, the least user - * interaction will cause them to reappear immediately. + * <p>There is a limitation: because navigation controls are so important, the least user + * interaction will cause them to reappear immediately. When this happens, both + * this flag and {@link #SYSTEM_UI_FLAG_FULLSCREEN} will be cleared automatically, + * so that both elements reappear at the same time. * * @see #setSystemUiVisibility(int) */ public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002; /** + * Flag for {@link #setSystemUiVisibility(int)}: View has requested to go + * into the normal fullscreen mode so that its content can take over the screen + * while still allowing the user to interact with the application. + * + * <p>This has the same visual effect as + * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN + * WindowManager.LayoutParams.FLAG_FULLSCREEN}, + * meaning that non-critical screen decorations (such as the status bar) will be + * hidden while the user is in the View's window, focusing the experience on + * that content. Unlike the window flag, if you are using ActionBar in + * overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY + * Window.FEATURE_ACTION_BAR_OVERLAY}, then enabling this flag will also + * hide the action bar. + * + * <p>This approach to going fullscreen is best used over the window flag when + * it is a transient state -- that is, the application does this at certain + * points in its user interaction where it wants to allow the user to focus + * on content, but not as a continuous state. For situations where the application + * would like to simply stay full screen the entire time (such as a game that + * wants to take over the screen), the + * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN window flag} + * is usually a better approach. The state set here will be removed by the system + * in various situations (such as the user moving to another application) like + * the other system UI states. + * + * <p>When using this flag, the application should provide some easy facility + * for the user to go out of it. A common example would be in an e-book + * reader, where tapping on the screen brings back whatever screen and UI + * decorations that had been hidden while the user was immersed in reading + * the book. + * + * @see #setSystemUiVisibility(int) + */ + public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004; + + /** + * Flag for {@link #setSystemUiVisibility(int)}: When using other layout + * flags, we would like a stable view of the content insets given to + * {@link #fitSystemWindows(Rect)}. This means that the insets seen there + * will always represent the worst case that the application can expect + * as a continue state. In practice this means with any of system bar, + * nav bar, and status bar shown, but not the space that would be needed + * for an input method. + * + * <p>If you are using ActionBar in + * overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY + * Window.FEATURE_ACTION_BAR_OVERLAY}, this flag will also impact the + * insets it adds to those given to the application. + */ + public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100; + + /** + * Flag for {@link #setSystemUiVisibility(int)}: View would like its window + * to be layed out as if it has requested + * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, even if it currently hasn't. This + * allows it to avoid artifacts when switching in and out of that mode, at + * the expense that some of its user interface may be covered by screen + * decorations when they are shown. You can perform layout of your inner + * UI elements to account for the navagation system UI through the + * {@link #fitSystemWindows(Rect)} method. + */ + public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200; + + /** + * Flag for {@link #setSystemUiVisibility(int)}: View would like its window + * to be layed out as if it has requested + * {@link #SYSTEM_UI_FLAG_FULLSCREEN}, even if it currently hasn't. This + * allows it to avoid artifacts when switching in and out of that mode, at + * the expense that some of its user interface may be covered by screen + * decorations when they are shown. You can perform layout of your inner + * UI elements to account for non-fullscreen system UI through the + * {@link #fitSystemWindows(Rect)} method. + */ + public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400; + + /** * @deprecated Use {@link #SYSTEM_UI_FLAG_LOW_PROFILE} instead. */ public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE; @@ -2055,17 +2141,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * @hide - * - * NOTE: This flag may only be used in subtreeSystemUiVisibility, etc. etc. - * - * This hides HOME and RECENT and is provided for compatibility with interim implementations. - */ - @Deprecated - public static final int STATUS_BAR_DISABLE_NAVIGATION = - STATUS_BAR_DISABLE_HOME | STATUS_BAR_DISABLE_RECENT; - - /** - * @hide */ public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF; @@ -2076,7 +2151,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @hide */ public static final int SYSTEM_UI_CLEARABLE_FLAGS = - SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION; + SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_FULLSCREEN; + + /** + * Flags that can impact the layout in relation to system UI. + */ + public static final int SYSTEM_UI_LAYOUT_FLAGS = + SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; /** * Find views that render the specified text. @@ -4692,21 +4775,54 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** - * Apply the insets for system windows to this view, if the FITS_SYSTEM_WINDOWS flag - * is set + * Called by the view hierarchy when the content insets for a window have + * changed, to allow it to adjust its content to fit within those windows. + * The content insets tell you the space that the status bar, input method, + * and other system windows infringe on the application's window. + * + * <p>You do not normally need to deal with this function, since the default + * window decoration given to applications takes care of applying it to the + * content of the window. If you use {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} + * or {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION} this will not be the case, + * and your content can be placed under those system elements. You can then + * use this method within your view hierarchy if you have parts of your UI + * which you would like to ensure are not being covered. * - * @param insets Insets for system windows + * <p>The default implementation of this method simply applies the content + * inset's to the view's padding. This can be enabled through + * {@link #setFitsSystemWindows(boolean)}. Alternatively, you can override + * the method and handle the insets however you would like. Note that the + * insets provided by the framework are always relative to the far edges + * of the window, not accounting for the location of the called view within + * that window. (In fact when this method is called you do not yet know + * where the layout will place the view, as it is done before layout happens.) * - * @return True if this view applied the insets, false otherwise + * <p>Note: unlike many View methods, there is no dispatch phase to this + * call. If you are overriding it in a ViewGroup and want to allow the + * call to continue to your children, you must be sure to call the super + * implementation. + * + * @param insets Current content insets of the window. Prior to + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} you must not modify + * the insets or else you and Android will be unhappy. + * + * @return Return true if this view applied the insets and it should not + * continue propagating further down the hierarchy, false otherwise. */ protected boolean fitSystemWindows(Rect insets) { if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) { - mPaddingLeft = insets.left; - mPaddingTop = insets.top; - mPaddingRight = insets.right; - mPaddingBottom = insets.bottom; - requestLayout(); - return true; + mUserPaddingStart = -1; + mUserPaddingEnd = -1; + mUserPaddingRelative = false; + if ((mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) == 0 + || mAttachInfo == null + || (mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0) { + internalSetPadding(insets.left, insets.top, insets.right, insets.bottom); + return true; + } else { + internalSetPadding(0, 0, 0, 0); + return false; + } } return false; } @@ -4742,6 +4858,23 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * Ask that a new dispatch of {@link #fitSystemWindows(Rect)} be performed. + */ + public void requestFitSystemWindows() { + if (mParent != null) { + mParent.requestFitSystemWindows(); + } + } + + /** + * For use by PhoneWindow to make its own system window fitting optional. + * @hide + */ + public void makeOptionalFitsSystemWindows() { + setFlags(OPTIONAL_FITS_SYSTEM_WINDOWS, OPTIONAL_FITS_SYSTEM_WINDOWS); + } + + /** * Returns the visibility status for this view. * * @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. @@ -6118,19 +6251,19 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Private function to aggregate all per-view attributes in to the view * root. */ - void dispatchCollectViewAttributes(int visibility) { - performCollectViewAttributes(visibility); + void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) { + performCollectViewAttributes(attachInfo, visibility); } - void performCollectViewAttributes(int visibility) { - if ((visibility & VISIBILITY_MASK) == VISIBLE && mAttachInfo != null) { + void performCollectViewAttributes(AttachInfo attachInfo, int visibility) { + if ((visibility & VISIBILITY_MASK) == VISIBLE) { if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) { - mAttachInfo.mKeepScreenOn = true; + attachInfo.mKeepScreenOn = true; } - mAttachInfo.mSystemUiVisibility |= mSystemUiVisibility; + attachInfo.mSystemUiVisibility |= mSystemUiVisibility; ListenerInfo li = mListenerInfo; if (li != null && li.mOnSystemUiVisibilityChangeListener != null) { - mAttachInfo.mHasSystemUiListeners = true; + attachInfo.mHasSystemUiListeners = true; } } } @@ -10126,7 +10259,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal mAttachInfo.mScrollContainers.add(this); mPrivateFlags |= SCROLL_CONTAINER_ADDED; } - performCollectViewAttributes(visibility); + performCollectViewAttributes(mAttachInfo, visibility); onAttachedToWindow(); ListenerInfo li = mListenerInfo; @@ -13982,6 +14115,35 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * Returns the current system UI visibility that is currently set for + * the entire window. This is the combination of the + * {@link #setSystemUiVisibility(int)} values supplied by all of the + * views in the window. + */ + public int getWindowSystemUiVisibility() { + return mAttachInfo != null ? mAttachInfo.mSystemUiVisibility : 0; + } + + /** + * Override to find out when the window's requested system UI visibility + * has changed, that is the value returned by {@link #getWindowSystemUiVisibility()}. + * This is different from the callbacks recieved through + * {@link #setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener)} + * in that this is only telling you about the local request of the window, + * not the actual values applied by the system. + */ + public void onWindowSystemUiVisibilityChanged(int visible) { + } + + /** + * Dispatch callbacks to {@link #onWindowSystemUiVisibilityChanged(int)} down + * the view hierarchy. + */ + public void dispatchWindowSystemUiVisiblityChanged(int visible) { + onWindowSystemUiVisibilityChanged(visible); + } + + /** * Set a listener to receive callbacks when the visibility of the system bar changes. * @param l The {@link OnSystemUiVisibilityChangeListener} to receive callbacks. */ diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 30d6ec7..d5c783f 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -918,7 +918,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } } - + + /** + * @hide + */ + @Override + public void makeOptionalFitsSystemWindows() { + super.makeOptionalFitsSystemWindows(); + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + children[i].makeOptionalFitsSystemWindows(); + } + } + /** * {@inheritDoc} */ @@ -1017,13 +1030,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - void dispatchCollectViewAttributes(int visibility) { - visibility |= mViewFlags&VISIBILITY_MASK; - super.dispatchCollectViewAttributes(visibility); - final int count = mChildrenCount; - final View[] children = mChildren; - for (int i = 0; i < count; i++) { - children[i].dispatchCollectViewAttributes(visibility); + void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) { + if ((visibility & VISIBILITY_MASK) == VISIBLE) { + super.dispatchCollectViewAttributes(attachInfo, visibility); + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + final View child = children[i]; + child.dispatchCollectViewAttributes(attachInfo, + visibility | (child.mViewFlags&VISIBILITY_MASK)); + } } } @@ -1239,6 +1255,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override + public void dispatchWindowSystemUiVisiblityChanged(int visible) { + super.dispatchWindowSystemUiVisiblityChanged(visible); + + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i=0; i <count; i++) { + final View child = children[i]; + child.dispatchWindowSystemUiVisiblityChanged(visible); + } + } + + @Override public void dispatchSystemUiVisibilityChanged(int visible) { super.dispatchSystemUiVisibilityChanged(visible); @@ -2244,12 +2272,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager super.dispatchAttachedToWindow(info, visibility); mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW; - visibility |= mViewFlags & VISIBILITY_MASK; - final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < count; i++) { - children[i].dispatchAttachedToWindow(info, visibility); + final View child = children[i]; + child.dispatchAttachedToWindow(info, + visibility | (child.mViewFlags&VISIBILITY_MASK)); } } diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 8395f1b..75e9151 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -271,4 +271,10 @@ public interface ViewParent { * @hide */ public void childHasTransientStateChanged(View child, boolean hasTransientState); + + /** + * Ask that a new dispatch of {@link View#fitSystemWindows(Rect) + * View.fitSystemWindows(Rect)} be performed. + */ + public void requestFitSystemWindows(); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4d589d7..d72f3b7 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -223,6 +223,7 @@ public final class ViewRootImpl implements ViewParent, long mLastTraversalFinishedTimeNanos; long mLastDrawFinishedTimeNanos; boolean mWillDrawSoon; + boolean mFitSystemWindowsRequested; boolean mLayoutRequested; boolean mFirst; boolean mReportNextDraw; @@ -230,6 +231,7 @@ public final class ViewRootImpl implements ViewParent, boolean mNewSurfaceNeeded; boolean mHasHadWindowFocus; boolean mLastWasImTarget; + int mLastSystemUiVisibility; // Pool of queued input events. private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10; @@ -263,6 +265,8 @@ public final class ViewRootImpl implements ViewParent, final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); + final Rect mFitSystemWindowsInsets = new Rect(); + final Configuration mLastConfiguration = new Configuration(); final Configuration mPendingConfiguration = new Configuration(); @@ -539,6 +543,8 @@ public final class ViewRootImpl implements ViewParent, } try { mOrigWindowType = mWindowAttributes.type; + mAttachInfo.mRecomputeGlobalAttributes = true; + collectViewAttributes(); res = sWindowSession.add(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mAttachInfo.mContentInsets, mInputChannel); @@ -786,6 +792,15 @@ public final class ViewRootImpl implements ViewParent, /** * {@inheritDoc} */ + public void requestFitSystemWindows() { + checkThread(); + mFitSystemWindowsRequested = true; + scheduleTraversals(); + } + + /** + * {@inheritDoc} + */ public void requestLayout() { checkThread(); mLayoutRequested = true; @@ -974,6 +989,100 @@ public final class ViewRootImpl implements ViewParent, } } + private boolean collectViewAttributes() { + final View.AttachInfo attachInfo = mAttachInfo; + if (attachInfo.mRecomputeGlobalAttributes) { + //Log.i(TAG, "Computing view hierarchy attributes!"); + attachInfo.mRecomputeGlobalAttributes = false; + boolean oldScreenOn = attachInfo.mKeepScreenOn; + int oldVis = attachInfo.mSystemUiVisibility; + boolean oldHasSystemUiListeners = attachInfo.mHasSystemUiListeners; + attachInfo.mKeepScreenOn = false; + attachInfo.mSystemUiVisibility = 0; + attachInfo.mHasSystemUiListeners = false; + mView.dispatchCollectViewAttributes(attachInfo, 0); + if (attachInfo.mKeepScreenOn != oldScreenOn + || attachInfo.mSystemUiVisibility != oldVis + || attachInfo.mHasSystemUiListeners != oldHasSystemUiListeners) { + WindowManager.LayoutParams params = mWindowAttributes; + if (attachInfo.mKeepScreenOn) { + params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + } + params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility; + params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners; + mView.dispatchWindowSystemUiVisiblityChanged(attachInfo.mSystemUiVisibility); + return true; + } + } + return false; + } + + private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, + final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) { + int childWidthMeasureSpec; + int childHeightMeasureSpec; + boolean windowSizeMayChange = false; + + if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG, + "Measuring " + host + " in display " + desiredWindowWidth + + "x" + desiredWindowHeight + "..."); + + boolean goodMeasure = false; + if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { + // On large screens, we don't want to allow dialogs to just + // stretch to fill the entire width of the screen to display + // one line of text. First try doing the layout at a smaller + // size to see if it will fit. + final DisplayMetrics packageMetrics = res.getDisplayMetrics(); + res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true); + int baseSize = 0; + if (mTmpValue.type == TypedValue.TYPE_DIMENSION) { + baseSize = (int)mTmpValue.getDimension(packageMetrics); + } + if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize); + if (baseSize != 0 && desiredWindowWidth > baseSize) { + childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); + childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); + host.measure(childWidthMeasureSpec, childHeightMeasureSpec); + if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" + + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); + if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { + goodMeasure = true; + } else { + // Didn't fit in that size... try expanding a bit. + baseSize = (baseSize+desiredWindowWidth)/2; + if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize=" + + baseSize); + childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); + host.measure(childWidthMeasureSpec, childHeightMeasureSpec); + if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" + + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); + if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { + if (DEBUG_DIALOG) Log.v(TAG, "Good!"); + goodMeasure = true; + } + } + } + } + + if (!goodMeasure) { + childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); + childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); + host.measure(childWidthMeasureSpec, childHeightMeasureSpec); + if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { + windowSizeMayChange = true; + } + } + + if (DBG) { + System.out.println("======================================"); + System.out.println("performTraversals -- after measure"); + host.debug(); + } + + return windowSizeMayChange; + } + private void performTraversals() { // cache mView since it is used so much below... final View host = mView; @@ -995,8 +1104,6 @@ public final class ViewRootImpl implements ViewParent, int desiredWindowWidth; int desiredWindowHeight; - int childWidthMeasureSpec; - int childHeightMeasureSpec; final View.AttachInfo attachInfo = mAttachInfo; @@ -1057,15 +1164,14 @@ public final class ViewRootImpl implements ViewParent, attachInfo.mHasWindowFocus = false; attachInfo.mWindowVisibility = viewVisibility; attachInfo.mRecomputeGlobalAttributes = false; - attachInfo.mKeepScreenOn = false; - attachInfo.mSystemUiVisibility = 0; viewVisibilityChanged = false; mLastConfiguration.setTo(host.getResources().getConfiguration()); + mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; host.dispatchAttachedToWindow(attachInfo, 0); + mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); + host.fitSystemWindows(mFitSystemWindowsInsets); //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); - host.fitSystemWindows(mAttachInfo.mContentInsets); - } else { desiredWindowWidth = frame.width(); desiredWindowHeight = frame.height(); @@ -1093,7 +1199,8 @@ public final class ViewRootImpl implements ViewParent, boolean insetsChanged = false; - if (mLayoutRequested && !mStopped) { + boolean layoutRequested = mLayoutRequested && !mStopped; + if (layoutRequested) { // Execute enqueued actions on every layout in case a view that was detached // enqueued an action after being detached getRunQueue().executeActions(attachInfo.mHandler); @@ -1134,79 +1241,12 @@ public final class ViewRootImpl implements ViewParent, } // Ask host how big it wants to be - if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG, - "Measuring " + host + " in display " + desiredWindowWidth - + "x" + desiredWindowHeight + "..."); - - boolean goodMeasure = false; - if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { - // On large screens, we don't want to allow dialogs to just - // stretch to fill the entire width of the screen to display - // one line of text. First try doing the layout at a smaller - // size to see if it will fit. - final DisplayMetrics packageMetrics = res.getDisplayMetrics(); - res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true); - int baseSize = 0; - if (mTmpValue.type == TypedValue.TYPE_DIMENSION) { - baseSize = (int)mTmpValue.getDimension(packageMetrics); - } - if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize); - if (baseSize != 0 && desiredWindowWidth > baseSize) { - childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); - childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); - host.measure(childWidthMeasureSpec, childHeightMeasureSpec); - if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" - + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); - if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { - goodMeasure = true; - } else { - // Didn't fit in that size... try expanding a bit. - baseSize = (baseSize+desiredWindowWidth)/2; - if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize=" - + baseSize); - childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); - host.measure(childWidthMeasureSpec, childHeightMeasureSpec); - if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured (" - + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")"); - if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { - if (DEBUG_DIALOG) Log.v(TAG, "Good!"); - goodMeasure = true; - } - } - } - } - - if (!goodMeasure) { - childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); - childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); - host.measure(childWidthMeasureSpec, childHeightMeasureSpec); - if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { - windowSizeMayChange = true; - } - } - - if (DBG) { - System.out.println("======================================"); - System.out.println("performTraversals -- after measure"); - host.debug(); - } + windowSizeMayChange |= measureHierarchy(host, lp, res, + desiredWindowWidth, desiredWindowHeight); } - if (attachInfo.mRecomputeGlobalAttributes && host.mAttachInfo != null) { - //Log.i(TAG, "Computing view hierarchy attributes!"); - attachInfo.mRecomputeGlobalAttributes = false; - boolean oldScreenOn = attachInfo.mKeepScreenOn; - int oldVis = attachInfo.mSystemUiVisibility; - boolean oldHasSystemUiListeners = attachInfo.mHasSystemUiListeners; - attachInfo.mKeepScreenOn = false; - attachInfo.mSystemUiVisibility = 0; - attachInfo.mHasSystemUiListeners = false; - host.dispatchCollectViewAttributes(0); - if (attachInfo.mKeepScreenOn != oldScreenOn - || attachInfo.mSystemUiVisibility != oldVis - || attachInfo.mHasSystemUiListeners != oldHasSystemUiListeners) { - params = lp; - } + if (collectViewAttributes()) { + params = lp; } if (attachInfo.mForceReportNewAttributes) { attachInfo.mForceReportNewAttributes = false; @@ -1245,7 +1285,28 @@ public final class ViewRootImpl implements ViewParent, } } - boolean windowShouldResize = mLayoutRequested && windowSizeMayChange + if (mFitSystemWindowsRequested) { + mFitSystemWindowsRequested = false; + mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); + host.fitSystemWindows(mFitSystemWindowsInsets); + if (mLayoutRequested) { + // Short-circuit catching a new layout request here, so + // we don't need to go through two layout passes when things + // change due to fitting system windows, which can happen a lot. + windowSizeMayChange |= measureHierarchy(host, lp, + mView.getContext().getResources(), + desiredWindowWidth, desiredWindowHeight); + } + } + + if (layoutRequested) { + // Clear this now, so that if anything requests a layout in the + // rest of this function we will catch it and re-run a full + // layout pass. + mLayoutRequested = false; + } + + boolean windowShouldResize = layoutRequested && windowSizeMayChange && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT && frame.width() < desiredWindowWidth && frame.width() != mWidth) @@ -1285,15 +1346,6 @@ public final class ViewRootImpl implements ViewParent, boolean hadSurface = mSurface.isValid(); try { - int fl = 0; - if (params != null) { - fl = params.flags; - if (attachInfo.mKeepScreenOn) { - params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; - } - params.subtreeSystemUiVisibility = attachInfo.mSystemUiVisibility; - params.hasSystemUiListeners = attachInfo.mHasSystemUiListeners; - } if (DEBUG_LAYOUT) { Log.i(TAG, "host=w:" + host.getMeasuredWidth() + ", h:" + host.getMeasuredHeight() + ", params=" + params); @@ -1302,10 +1354,6 @@ public final class ViewRootImpl implements ViewParent, final int surfaceGenerationId = mSurface.getGenerationId(); relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); - if (params != null) { - params.flags = fl; - } - if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString() + " content=" + mPendingContentInsets.toShortString() + " visible=" + mPendingVisibleInsets.toShortString() @@ -1323,7 +1371,9 @@ public final class ViewRootImpl implements ViewParent, visibleInsetsChanged = !mPendingVisibleInsets.equals( mAttachInfo.mVisibleInsets); if (contentInsetsChanged) { - if (mWidth > 0 && mHeight > 0 && + if (mWidth > 0 && mHeight > 0 && lp != null && + ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility) + & View.SYSTEM_UI_LAYOUT_FLAGS) == 0 && mSurface != null && mSurface.isValid() && !mAttachInfo.mTurnOffWindowResizeAnim && mAttachInfo.mHardwareRenderer != null && @@ -1390,10 +1440,16 @@ public final class ViewRootImpl implements ViewParent, } } mAttachInfo.mContentInsets.set(mPendingContentInsets); - host.fitSystemWindows(mAttachInfo.mContentInsets); if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: " + mAttachInfo.mContentInsets); } + if (contentInsetsChanged || mLastSystemUiVisibility != + mAttachInfo.mSystemUiVisibility || mFitSystemWindowsRequested) { + mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; + mFitSystemWindowsRequested = false; + mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); + host.fitSystemWindows(mFitSystemWindowsInsets); + } if (visibleInsetsChanged) { mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: " @@ -1547,8 +1603,8 @@ public final class ViewRootImpl implements ViewParent, (relayoutResult&WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE) != 0); if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { - childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); - childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); + int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); + int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth=" + mWidth + " measuredWidth=" + host.getMeasuredWidth() @@ -1586,12 +1642,12 @@ public final class ViewRootImpl implements ViewParent, host.measure(childWidthMeasureSpec, childHeightMeasureSpec); } - mLayoutRequested = true; + layoutRequested = true; } } } - final boolean didLayout = mLayoutRequested && !mStopped; + final boolean didLayout = layoutRequested && !mStopped; boolean triggerGlobalLayoutListener = didLayout || attachInfo.mRecomputeGlobalAttributes; if (didLayout) { @@ -3579,9 +3635,6 @@ public final class ViewRootImpl implements ViewParent, if (mView == null) return; if (args.localChanges != 0) { if (mAttachInfo != null) { - mAttachInfo.mSystemUiVisibility = - (mAttachInfo.mSystemUiVisibility & ~args.localChanges) | - (args.localValue & args.localChanges); mAttachInfo.mRecomputeGlobalAttributes = true; } mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index a99ac03..b0e90db 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -75,6 +75,16 @@ public abstract class Window { * over how the Action Bar is displayed, such as letting application content scroll beneath * an Action Bar with a transparent background or otherwise displaying a transparent/translucent * Action Bar over application content. + * + * <p>This mode is especially useful with {@link View#SYSTEM_UI_FLAG_FULLSCREEN + * View.SYSTEM_UI_FLAG_FULLSCREEN}, which allows you to seamlessly hide the + * action bar in conjunction with other screen decorations. + * + * <p>As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, when an + * ActionBar is in this mode it will adjust the insets provided to + * {@link View#fitSystemWindows(android.graphics.Rect) View.fitSystemWindows(Rect)} + * to include the content covered by the action bar, so you can do layout within + * that space. */ public static final int FEATURE_ACTION_BAR_OVERLAY = 9; /** 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); + } + } +} diff --git a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml index 89f0d89..f2a1ea1 100644 --- a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml +++ b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml @@ -19,31 +19,39 @@ This is an optimized layout for a screen with the Action Bar enabled overlaying application content. --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:fitsSystemWindows="true"> +<com.android.internal.widget.ActionBarOverlayLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/action_bar_overlay_layout" + android:layout_width="match_parent" + android:layout_height="match_parent"> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" /> - <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="?android:attr/actionBarStyle" - android:gravity="top"> - <com.android.internal.widget.ActionBarView - android:id="@+id/action_bar" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="?android:attr/actionBarStyle" /> - <com.android.internal.widget.ActionBarContextView - android:id="@+id/action_context_bar" + <LinearLayout android:id="@+id/top_action_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="top"> + <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:visibility="gone" - style="?android:attr/actionModeStyle" /> - </com.android.internal.widget.ActionBarContainer> - <ImageView android:src="?android:attr/windowContentOverlay" - android:scaleType="fitXY" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/action_bar_container" /> -</RelativeLayout> + android:layout_alignParentTop="true" + style="?android:attr/actionBarStyle" + android:gravity="top"> + <com.android.internal.widget.ActionBarView + android:id="@+id/action_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="?android:attr/actionBarStyle" /> + <com.android.internal.widget.ActionBarContextView + android:id="@+id/action_context_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + style="?android:attr/actionModeStyle" /> + </com.android.internal.widget.ActionBarContainer> + <ImageView android:src="?android:attr/windowContentOverlay" + android:scaleType="fitXY" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> +</com.android.internal.widget.ActionBarOverlayLayout> diff --git a/core/res/res/layout/screen_action_bar_overlay.xml b/core/res/res/layout/screen_action_bar_overlay.xml index 2a8c7c3..20a7db1 100644 --- a/core/res/res/layout/screen_action_bar_overlay.xml +++ b/core/res/res/layout/screen_action_bar_overlay.xml @@ -19,14 +19,16 @@ This is an optimized layout for a screen with the Action Bar enabled overlaying application content. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.internal.widget.ActionBarOverlayLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/action_bar_overlay_layout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:fitsSystemWindows="true"> + android:layout_height="match_parent"> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" /> - <LinearLayout android:layout_width="match_parent" + <LinearLayout android:id="@+id/top_action_bar" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top"> <com.android.internal.widget.ActionBarContainer android:id="@+id/action_bar_container" @@ -50,8 +52,7 @@ the Action Bar enabled overlaying application content. <ImageView android:src="?android:attr/windowContentOverlay" android:scaleType="fitXY" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/action_bar_container" /> + android:layout_height="wrap_content" /> </LinearLayout> <com.android.internal.widget.ActionBarContainer android:id="@+id/split_action_bar" android:layout_width="match_parent" @@ -60,4 +61,4 @@ the Action Bar enabled overlaying application content. style="?android:attr/actionBarSplitStyle" android:visibility="gone" android:gravity="center"/> -</FrameLayout> +</com.android.internal.widget.ActionBarOverlayLayout> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 7b4f50b..5af4f72 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -34,6 +34,7 @@ <java-symbol type="id" name="account_type" /> <java-symbol type="id" name="action_bar" /> <java-symbol type="id" name="action_bar_container" /> + <java-symbol type="id" name="action_bar_overlay_layout" /> <java-symbol type="id" name="action_bar_title" /> <java-symbol type="id" name="action_bar_subtitle" /> <java-symbol type="id" name="action_context_bar" /> @@ -183,6 +184,7 @@ <java-symbol type="id" name="to_common" /> <java-symbol type="id" name="to_org" /> <java-symbol type="id" name="to_org_unit" /> + <java-symbol type="id" name="top_action_bar" /> <java-symbol type="id" name="topPanel" /> <java-symbol type="id" name="up" /> <java-symbol type="id" name="value" /> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 23222f2..2fb61b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -119,7 +119,7 @@ public abstract class BaseStatusBar extends SystemUI implements createAndAddWindows(); disable(switches[0]); - setSystemUiVisibility(switches[1]); + setSystemUiVisibility(switches[1], 0xffffffff); topAppWindowChanged(switches[2] != 0); // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(binders.get(0), switches[3], switches[4]); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 37ab58a..f88a3cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -89,7 +89,7 @@ public class CommandQueue extends IStatusBar.Stub { public void disable(int state); public void animateExpand(); public void animateCollapse(); - public void setSystemUiVisibility(int vis); + public void setSystemUiVisibility(int vis, int mask); public void topAppWindowChanged(boolean visible); public void setImeWindowStatus(IBinder token, int vis, int backDisposition); public void setHardKeyboardStatus(boolean available, boolean enabled); @@ -165,10 +165,10 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void setSystemUiVisibility(int vis) { + public void setSystemUiVisibility(int vis, int mask) { synchronized (mList) { mHandler.removeMessages(MSG_SET_SYSTEMUI_VISIBILITY); - mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, 0, null).sendToTarget(); + mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget(); } } @@ -279,7 +279,7 @@ public class CommandQueue extends IStatusBar.Stub { } break; case MSG_SET_SYSTEMUI_VISIBILITY: - mCallbacks.setSystemUiVisibility(msg.arg1); + mCallbacks.setSystemUiVisibility(msg.arg1, msg.arg2); break; case MSG_TOP_APP_WINDOW_CHANGED: mCallbacks.topAppWindowChanged(msg.arg1 != 0); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 6574c7d..5582b0f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -139,7 +139,7 @@ public class NavigationBarView extends LinearLayout { setLowProfile(false, false, false); try { - mBarService.setSystemUiVisibility(0); + mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); } catch (android.os.RemoteException ex) { } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 7c679b2..dc2953e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1471,12 +1471,13 @@ public class PhoneStatusBar extends BaseStatusBar { } @Override // CommandQueue - public void setSystemUiVisibility(int vis) { - final int old = mSystemUiVisibility; - final int diff = vis ^ old; + public void setSystemUiVisibility(int vis, int mask) { + final int oldVal = mSystemUiVisibility; + final int newVal = (oldVal&~mask) | (vis&mask); + final int diff = newVal ^ oldVal; if (diff != 0) { - mSystemUiVisibility = vis; + mSystemUiVisibility = newVal; if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) { final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE)); @@ -1495,9 +1496,9 @@ public class PhoneStatusBar extends BaseStatusBar { public void setLightsOn(boolean on) { Log.v(TAG, "setLightsOn(" + on + ")"); if (on) { - setSystemUiVisibility(mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE); + setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); } else { - setSystemUiVisibility(mSystemUiVisibility | View.SYSTEM_UI_FLAG_LOW_PROFILE); + setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 2491d18..27bc972 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -594,7 +594,7 @@ public class TabletStatusBar extends BaseStatusBar implements mBarContents.setVisibility(View.VISIBLE); try { - mBarService.setSystemUiVisibility(View.STATUS_BAR_VISIBLE); + mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); } catch (RemoteException ex) { // system process dead } @@ -1165,14 +1165,20 @@ public class TabletStatusBar extends BaseStatusBar implements } @Override // CommandQueue - public void setSystemUiVisibility(int vis) { - if (vis != mSystemUiVisibility) { - mSystemUiVisibility = vis; - - mHandler.removeMessages(MSG_HIDE_CHROME); - mHandler.removeMessages(MSG_SHOW_CHROME); - mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) - ? MSG_SHOW_CHROME : MSG_HIDE_CHROME); + public void setSystemUiVisibility(int vis, int mask) { + final int oldVal = mSystemUiVisibility; + final int newVal = (oldVal&~mask) | (vis&mask); + final int diff = newVal ^ oldVal; + + if (diff != 0) { + mSystemUiVisibility = newVal; + + if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) { + mHandler.removeMessages(MSG_HIDE_CHROME); + mHandler.removeMessages(MSG_SHOW_CHROME); + mHandler.sendEmptyMessage(0 == (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) + ? MSG_SHOW_CHROME : MSG_HIDE_CHROME); + } notifyUiVisibilityChanged(); } @@ -1187,9 +1193,9 @@ public class TabletStatusBar extends BaseStatusBar implements Slog.v(TAG, "setLightsOn(" + on + ")"); if (on) { - setSystemUiVisibility(mSystemUiVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE); + setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); } else { - setSystemUiVisibility(mSystemUiVisibility | View.SYSTEM_UI_FLAG_LOW_PROFILE); + setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE); } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 301dbf5..c3c49b0 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -2829,6 +2829,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent == null) { mContentParent = generateLayout(mDecor); + // Set up decor part of UI to ignore fitsSystemWindows if appropriate. + mDecor.makeOptionalFitsSystemWindows(); + mTitleView = (TextView)findViewById(com.android.internal.R.id.title); if (mTitleView != null) { if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 6214086..fb1e106 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -230,6 +230,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; + /** + * These are the system UI flags that, when changing, can cause the layout + * of the screen to change. + */ + static final int SYSTEM_UI_CHANGING_LAYOUT = + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; + // Useful scan codes. private static final int SW_LID = 0x00; private static final int BTN_MOUSE = 0x110; @@ -391,6 +398,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // that area of the display from all other windows. int mRestrictedScreenLeft, mRestrictedScreenTop; int mRestrictedScreenWidth, mRestrictedScreenHeight; + // For applications requesting stable content insets, these are them. + int mStableLeft, mStableTop, mStableRight, mStableBottom; // During layout, the current screen borders with all outer decoration // (status bar, input method dock) accounted for. int mCurLeft, mCurTop, mCurRight, mCurBottom; @@ -1958,10 +1967,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { // When the user taps down, we re-show the nav bar. boolean changed = false; synchronized (mLock) { - // Any user activity always causes us to show the navigation controls, - // if they had been hidden. - int newVal = mResettingSystemUiFlags - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + // Any user activity always causes us to show the + // navigation controls, if they had been hidden. + // We also clear the low profile and only content + // flags so that tapping on the screen will atomically + // restore all currently hidden screen decorations. + int newVal = mResettingSystemUiFlags | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LOW_PROFILE | + View.SYSTEM_UI_FLAG_FULLSCREEN; if (mResettingSystemUiFlags != newVal) { mResettingSystemUiFlags = newVal; changed = true; @@ -1969,14 +1983,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { // We don't allow the system's nav bar to be hidden // again for 1 second, to prevent applications from // spamming us and keeping it from being shown. - newVal = mForceClearedSystemUiFlags - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + newVal = mForceClearedSystemUiFlags | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; if (mForceClearedSystemUiFlags != newVal) { mForceClearedSystemUiFlags = newVal; changed = true; mHandler.postDelayed(new Runnable() { @Override public void run() { synchronized (mLock) { + // Clear flags. mForceClearedSystemUiFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; } @@ -2017,17 +2032,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) { final int fl = attrs.flags; - + if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR)) == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { - contentInset.set(mCurLeft, mCurTop, - (mRestrictedScreenLeft+mRestrictedScreenWidth) - mCurRight, - (mRestrictedScreenTop+mRestrictedScreenHeight) - mCurBottom); - } else { - contentInset.setEmpty(); + int availRight, availBottom; + if ((attrs.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) { + availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth; + availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight; + } else { + availRight = mRestrictedScreenLeft + mRestrictedScreenWidth; + availBottom = mRestrictedScreenTop + mRestrictedScreenHeight; + } + if ((attrs.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { + contentInset.set(mStableLeft, mStableTop, + availRight - mStableRight, availBottom - mStableBottom); + } else if ((attrs.systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 0) { + contentInset.set(mCurLeft, mCurTop, + availRight - mCurRight, availBottom - mCurBottom); + } else { + contentInset.set(mCurLeft, mCurTop, + availRight - mCurRight, availBottom - mCurBottom); + } + return; } + contentInset.setEmpty(); } - + /** {@inheritDoc} */ public void beginLayoutLw(int displayWidth, int displayHeight, int displayRotation) { mUnrestrictedScreenLeft = mUnrestrictedScreenTop = 0; @@ -2036,10 +2067,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { mRestrictedScreenLeft = mRestrictedScreenTop = 0; mRestrictedScreenWidth = displayWidth; mRestrictedScreenHeight = displayHeight; - mDockLeft = mContentLeft = mCurLeft = 0; - mDockTop = mContentTop = mCurTop = 0; - mDockRight = mContentRight = mCurRight = displayWidth; - mDockBottom = mContentBottom = mCurBottom = displayHeight; + mDockLeft = mContentLeft = mStableLeft = mCurLeft = 0; + mDockTop = mContentTop = mStableTop = mCurTop = 0; + mDockRight = mContentRight = mStableRight = mCurRight = displayWidth; + mDockBottom = mContentBottom = mStableBottom = mCurBottom = displayHeight; mDockLayer = 0x10000000; // start with the current dock rect, which will be (0,0,displayWidth,displayHeight) @@ -2081,6 +2112,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Portrait screen; nav bar goes on bottom. mTmpNavigationFrame.set(0, displayHeight-mNavigationBarHeight, displayWidth, displayHeight); + mStableBottom = mTmpNavigationFrame.top; if (navVisible) { mDockBottom = mTmpNavigationFrame.top; mRestrictedScreenHeight = mDockBottom - mDockTop; @@ -2094,6 +2126,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Landscape screen; nav bar goes to the right. mTmpNavigationFrame.set(displayWidth-mNavigationBarWidth, 0, displayWidth, displayHeight); + mStableRight = mTmpNavigationFrame.left; if (navVisible) { mDockRight = mTmpNavigationFrame.left; mRestrictedScreenWidth = mDockRight - mDockLeft; @@ -2125,11 +2158,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.bottom = df.bottom = vf.bottom = mDockBottom; mStatusBar.computeFrameLw(pf, df, vf, vf); + final Rect r = mStatusBar.getFrameLw(); + + // Compute the stable dimensions whether or not the status bar is hidden. + if (mStatusBarCanHide) { + if (mDockTop == r.top) mStableTop = r.bottom; + else if (mDockBottom == r.bottom) mStableBottom = r.top; + } else { + if (mStableTop == r.top) { + mStableTop = r.bottom; + } else if (mStableBottom == r.bottom) { + mStableBottom = r.top; + } + } if (mStatusBar.isVisibleLw()) { // If the status bar is hidden, we don't want to cause // windows behind it to scroll. - final Rect r = mStatusBar.getFrameLw(); if (mStatusBarCanHide) { // Status bar may go away, so the screen area it occupies // is available to apps but just covering them when the @@ -2229,7 +2274,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int fl = attrs.flags; final int sim = attrs.softInputMode; - + final int sysUiFl = win.getSystemUiVisibility(); + final Rect pf = mTmpParentFrame; final Rect df = mTmpDisplayFrame; final Rect cf = mTmpContentFrame; @@ -2250,7 +2296,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int adjust = sim & SOFT_INPUT_MASK_ADJUST; if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR)) - == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { + == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR) + && (sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { if (DEBUG_LAYOUT) Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN, INSET_DECOR, !FULLSCREEN"); @@ -2286,6 +2333,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { "Laying out status bar window: (%d,%d - %d,%d)", pf.left, pf.top, pf.right, pf.bottom)); } + } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 + && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { + // Asking for layout as if the nav bar is hidden, lets the + // application extend into the unrestricted screen area. We + // only do this for application windows to ensure no window that + // can be above the nav bar can do this. + pf.left = df.left = mUnrestrictedScreenLeft; + pf.top = df.top = mUnrestrictedScreenTop; + pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; + pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight; } else { pf.left = df.left = mRestrictedScreenLeft; pf.top = df.top = mRestrictedScreenTop; @@ -2303,6 +2361,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { cf.right = mContentRight; cf.bottom = mContentBottom; } + if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { + // If app is requesting a stable layout, don't let the + // content insets go below the stable values. + if (cf.left < mStableLeft) cf.left = mStableLeft; + if (cf.top < mStableTop) cf.top = mStableTop; + if (cf.right > mStableRight) cf.right = mStableRight; + if (cf.bottom > mStableBottom) cf.bottom = mStableBottom; + } if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.left = mCurLeft; vf.top = mCurTop; @@ -2312,7 +2378,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { vf.set(cf); } } - } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0) { + } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl + & (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { if (DEBUG_LAYOUT) Log.v(TAG, "layoutWindowLw(" + attrs.getTitle() + "): IN_SCREEN"); // A window that has requested to fill the entire screen just @@ -2327,7 +2395,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.bottom = df.bottom = cf.bottom = hasNavBar ? mRestrictedScreenTop+mRestrictedScreenHeight : mUnrestrictedScreenTop+mUnrestrictedScreenHeight; - if (DEBUG_LAYOUT) { Log.v(TAG, String.format( "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)", @@ -2359,6 +2426,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.right = df.right = cf.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; pf.bottom = df.bottom = cf.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight; + } else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 + && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { + // Asking for layout as if the nav bar is hidden, lets the + // application extend into the unrestricted screen area. We + // only do this for application windows to ensure no window that + // can be above the nav bar can do this. + // XXX This assumes that an app asking for this will also + // ask for layout in only content. We can't currently figure out + // what the screen would be if only laying out to hide the nav bar. + pf.left = df.left = cf.left = mUnrestrictedScreenLeft; + pf.top = df.top = cf.top = mUnrestrictedScreenTop; + pf.right = df.right = cf.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; + pf.bottom = df.bottom = cf.bottom + = mUnrestrictedScreenTop+mUnrestrictedScreenHeight; } else { pf.left = df.left = cf.left = mRestrictedScreenLeft; pf.top = df.top = cf.top = mRestrictedScreenTop; @@ -2366,6 +2448,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.bottom = df.bottom = cf.bottom = mRestrictedScreenTop+mRestrictedScreenHeight; } + if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { + // If app is requesting a stable layout, don't let the + // content insets go below the stable values. + if (cf.left < mStableLeft) cf.left = mStableLeft; + if (cf.top < mStableTop) cf.top = mStableTop; + if (cf.right > mStableRight) cf.right = mStableRight; + if (cf.bottom > mStableBottom) cf.bottom = mStableBottom; + } if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.left = mCurLeft; vf.top = mCurTop; @@ -2526,7 +2616,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { Log.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs() + " lp.flags=0x" + Integer.toHexString(lp.flags)); } - topIsFullscreen = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; + topIsFullscreen = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0 + || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; // The subtle difference between the window for mTopFullscreenOpaqueWindowState // and mTopIsFullscreen is that that mTopIsFullscreen is set only if the window // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the @@ -2591,7 +2682,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if ((updateSystemUiVisibilityLw()&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) { + if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) { // If the navigation bar has been hidden or shown, we need to do another // layout pass to update that window. changes |= FINISH_LAYOUT_REDO_LAYOUT; @@ -2638,7 +2729,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { mFocusedWindow = newFocus; - if ((updateSystemUiVisibilityLw()&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) { + if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) { // If the navigation bar has been hidden or shown, we need to do another // layout pass to update that window. return FINISH_LAYOUT_REDO_LAYOUT; @@ -3998,7 +4089,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (mStatusBarService != null) { try { - mStatusBarService.setSystemUiVisibility(visibility); + mStatusBarService.setSystemUiVisibility(visibility, 0xffffffff); mStatusBarService.topAppWindowChanged(needsMenu); } catch (RemoteException e) { // not much to be done @@ -4065,6 +4156,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(","); pw.print(mRestrictedScreenTop); pw.print(") "); pw.print(mRestrictedScreenWidth); pw.print("x"); pw.println(mRestrictedScreenHeight); + pw.print(prefix); pw.print("mStable=("); pw.print(mStableLeft); + pw.print(","); pw.print(mStableTop); + pw.print(")-("); pw.print(mStableRight); + pw.print(","); pw.print(mStableBottom); pw.println(")"); pw.print(prefix); pw.print("mCur=("); pw.print(mCurLeft); pw.print(","); pw.print(mCurTop); pw.print(")-("); pw.print(mCurRight); diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java index 6452be7..8429086 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/StatusBarManagerService.java @@ -292,27 +292,27 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } - public void setSystemUiVisibility(int vis) { + public void setSystemUiVisibility(int vis, int mask) { // also allows calls from window manager which is in this process. enforceStatusBarService(); if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")"); synchronized (mLock) { - updateUiVisibilityLocked(vis); + updateUiVisibilityLocked(vis, mask); disableLocked(vis & StatusBarManager.DISABLE_MASK, mSysUiVisToken, "WindowManager.LayoutParams"); } } - private void updateUiVisibilityLocked(final int vis) { + private void updateUiVisibilityLocked(final int vis, final int mask) { if (mSystemUiVisibility != vis) { mSystemUiVisibility = vis; mHandler.post(new Runnable() { public void run() { if (mBar != null) { try { - mBar.setSystemUiVisibility(vis); + mBar.setSystemUiVisibility(vis, mask); } catch (RemoteException ex) { } } diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index a91e716..a2dfd91 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -2648,7 +2648,8 @@ public class WindowManagerService extends IWindowManager.Stub } flagChanges = win.mAttrs.flags ^= attrs.flags; attrChanges = win.mAttrs.copyFrom(attrs); - if ((attrChanges&WindowManager.LayoutParams.LAYOUT_CHANGED) != 0) { + if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED + | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) { win.mLayoutNeeded = true; } } |