From e26ab70de2f80de95264990a7af07fd445637065 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sun, 16 Oct 2011 13:21:33 -0700 Subject: [PATCH] Fix issue #5398675: It's (too) easy to keep the navigation bar... ...from ever becoming visible Now there is a 1 second delay from when the user dismisses the nav bar until when it can be re-hidden. Also move the code for capturing touch events while nav bar is hidden out to be used even when there is no nav bar, so this API behaves consistently across devices whether or not they have some element of the UI that is being hidden. On devices with a nav bar, this will all work the same as prime (the flag is set, the app gets the callback about the flag being set, when the user touches that touch is captured so the app doesn't see it put does clear the flag and tell the app about this). Change-Id: Icb5ea0ddaf614aa3f12d2140796217f128761dee --- .../policy/impl/PhoneWindowManager.java | 109 +++++++++++++----- 1 file changed, 77 insertions(+), 32 deletions(-) diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index aac320940cb85..48172fa3aeee1 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -353,8 +353,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mDockLeft, mDockTop, mDockRight, mDockBottom; // During layout, the layer at which the doc window is placed. int mDockLayer; - int mLastSystemUiVisibility; - int mForceClearingStatusBarVisibility = 0; + int mLastSystemUiFlags; + // Bits that we are in the process of clearing, so we want to prevent + // them from being set by applications until everything has been updated + // to have them clear. + int mResettingSystemUiFlags = 0; + // Bits that we are currently always keeping cleared. + int mForceClearedSystemUiFlags = 0; FakeWindow mHideNavFakeWindow = null; @@ -1719,6 +1724,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + /** + * A delayed callback use to determine when it is okay to re-allow applications + * to use certain system UI flags. This is used to prevent applications from + * spamming system UI changes that prevent the navigation bar from being shown. + */ + final Runnable mAllowSystemUiDelay = new Runnable() { + @Override public void run() { + } + }; + + /** + * Input handler used while nav bar is hidden. Captures any touch on the screen, + * to determine when the nav bar should be shown and prevent applications from + * receiving those touches. + */ final InputHandler mHideNavInputHandler = new BaseInputHandler() { @Override public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) { @@ -1731,12 +1751,30 @@ public class PhoneWindowManager implements WindowManagerPolicy { synchronized (mLock) { // Any user activity always causes us to show the navigation controls, // if they had been hidden. - int newVal = mForceClearingStatusBarVisibility + int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - if (mForceClearingStatusBarVisibility != newVal) { - mForceClearingStatusBarVisibility = newVal; + if (mResettingSystemUiFlags != newVal) { + mResettingSystemUiFlags = newVal; changed = true; } + // 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; + if (mForceClearedSystemUiFlags != newVal) { + mForceClearedSystemUiFlags = newVal; + changed = true; + mHandler.postDelayed(new Runnable() { + @Override public void run() { + synchronized (mLock) { + mForceClearedSystemUiFlags &= + ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + } + mWindowManagerFuncs.reevaluateStatusBarVisibility(); + } + }, 1000); + } } if (changed) { mWindowManagerFuncs.reevaluateStatusBarVisibility(); @@ -1753,10 +1791,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { public int adjustSystemUiVisibilityLw(int visibility) { // Reset any bits in mForceClearingStatusBarVisibility that // are now clear. - mForceClearingStatusBarVisibility &= visibility; + mResettingSystemUiFlags &= visibility; // Clear any bits in the new visibility that are currently being // force cleared, before reporting it. - return visibility & ~mForceClearingStatusBarVisibility; + return visibility & ~mResettingSystemUiFlags + & ~mForceClearedSystemUiFlags; } public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) { @@ -1795,11 +1834,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { pf.right = df.right = vf.right = mDockRight; pf.bottom = df.bottom = vf.bottom = mDockBottom; + final boolean navVisible = mNavigationBar != null && mNavigationBar.isVisibleLw() && + (mLastSystemUiFlags&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + + // When the navigation bar isn't visible, we put up a fake + // input window to catch all touch events. This way we can + // detect when the user presses anywhere to bring back the nav + // bar and ensure the application doesn't see the event. + if (navVisible) { + if (mHideNavFakeWindow != null) { + mHideNavFakeWindow.dismiss(); + mHideNavFakeWindow = null; + } + } else if (mHideNavFakeWindow == null) { + mHideNavFakeWindow = mWindowManagerFuncs.addFakeWindow( + mHandler.getLooper(), mHideNavInputHandler, + "hidden nav", WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER, + 0, false, false, true); + } + // decide where the status bar goes ahead of time if (mStatusBar != null) { if (mNavigationBar != null) { - final boolean navVisible = mNavigationBar.isVisibleLw() && - (mLastSystemUiVisibility&View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; // Force the navigation bar to its appropriate place and // size. We need to do this directly, instead of relying on // it to bubble up from the nav bar, because this needs to @@ -1831,21 +1887,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mTmpNavigationFrame.offset(mNavigationBarWidth, 0); } } - // When the navigation bar isn't visible, we put up a fake - // input window to catch all touch events. This way we can - // detect when the user presses anywhere to bring back the nav - // bar and ensure the application doesn't see the event. - if (navVisible) { - if (mHideNavFakeWindow != null) { - mHideNavFakeWindow.dismiss(); - mHideNavFakeWindow = null; - } - } else if (mHideNavFakeWindow == null) { - mHideNavFakeWindow = mWindowManagerFuncs.addFakeWindow( - mHandler.getLooper(), mHideNavInputHandler, - "hidden nav", WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER, - 0, false, false, true); - } // And compute the final frame. mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame); @@ -3653,12 +3694,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } final int visibility = mFocusedWindow.getSystemUiVisibility() - & ~mForceClearingStatusBarVisibility; - int diff = visibility ^ mLastSystemUiVisibility; + & ~mResettingSystemUiFlags + & ~mForceClearedSystemUiFlags; + int diff = visibility ^ mLastSystemUiFlags; if (diff == 0) { return 0; } - mLastSystemUiVisibility = visibility; + mLastSystemUiFlags = visibility; mHandler.post(new Runnable() { public void run() { if (mStatusBarService == null) { @@ -3685,11 +3727,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mLidOpen="); pw.print(mLidOpen); pw.print(" mLidOpenRotation="); pw.print(mLidOpenRotation); pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged); - if (mLastSystemUiVisibility != 0 || mForceClearingStatusBarVisibility != 0) { - pw.print(prefix); pw.print("mLastSystemUiVisibility=0x"); - pw.println(Integer.toHexString(mLastSystemUiVisibility)); - pw.print(" mForceClearingStatusBarVisibility=0x"); - pw.println(Integer.toHexString(mForceClearingStatusBarVisibility)); + if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0 + || mForceClearedSystemUiFlags != 0) { + pw.print(prefix); pw.print("mLastSystemUiFlags=0x"); + pw.print(Integer.toHexString(mLastSystemUiFlags)); + pw.print(" mResettingSystemUiFlags=0x"); + pw.print(Integer.toHexString(mResettingSystemUiFlags)); + pw.print(" mForceClearedSystemUiFlags=0x"); + pw.println(Integer.toHexString(mForceClearedSystemUiFlags)); } pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode); pw.print(" mDockMode="); pw.print(mDockMode);