Update transient navigation confirmation logic.

Show the confirmation toast when hiding the transient
navigation bar only the first time per app, per reboot.

Hitting the power key while the transient nav is hidden
is taken as a signal of possible user confusion, so reshow
the prompt the next time.

This requires the confirmation prompt to live in policy
(not sysui).  It arguably should have been here in the first
place, since the transient bar state/policy was here, and
sysui should not have not been able to fail to display
the prompt correctly.

Also take this opportunity to remove a hack wrt positioning
the confirmation properly while the nav bar is transitioning.
Toasts now support LAYOUT_HIDE_NAVIGATION if applied to the
toast's view.

Bug: 10246225
Change-Id: Ieb6355e4ca975c0758918a39e3c2ec13da81c7f4
diff --git a/packages/SystemUI/res/values-land/refs.xml b/core/res/res/values-land/refs.xml
similarity index 83%
rename from packages/SystemUI/res/values-land/refs.xml
rename to core/res/res/values-land/refs.xml
index 62fb77d..cda38cf 100644
--- a/packages/SystemUI/res/values-land/refs.xml
+++ b/core/res/res/values-land/refs.xml
@@ -16,5 +16,5 @@
 */
 -->
 <resources>
-    <item type="string" name="hiding_navigation_confirmation_message">@string/hiding_navigation_confirmation_message_long</item>
-</resources>
+    <item type="string" name="transient_navigation_confirmation">@string/transient_navigation_confirmation_long</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/refs.xml b/core/res/res/values-sw600dp-port/refs.xml
similarity index 83%
copy from packages/SystemUI/res/values-land/refs.xml
copy to core/res/res/values-sw600dp-port/refs.xml
index 62fb77d..cda38cf 100644
--- a/packages/SystemUI/res/values-land/refs.xml
+++ b/core/res/res/values-sw600dp-port/refs.xml
@@ -16,5 +16,5 @@
 */
 -->
 <resources>
-    <item type="string" name="hiding_navigation_confirmation_message">@string/hiding_navigation_confirmation_message_long</item>
-</resources>
+    <item type="string" name="transient_navigation_confirmation">@string/transient_navigation_confirmation_long</item>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 138debf..e497c85 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4313,4 +4313,9 @@
         <item quantity="other">Incorrect PIN. Try again in <xliff:g id="count">%d</xliff:g> seconds.</item>
     </plurals>
 
+    <!-- Toast bar message when hiding the transient navigation bar [CHAR LIMIT=35] -->
+    <string name="transient_navigation_confirmation">Swipe edge of screen to reveal bar</string>
+
+    <!-- Longer version of toast bar message when hiding the transient navigation bar (if room) -->
+    <string name="transient_navigation_confirmation_long">Swipe from edge of screen to reveal system bar</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2e05663..cd0c872 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -870,6 +870,8 @@
   <java-symbol type="string" name="restr_pin_enter_pin" />
   <java-symbol type="string" name="write_fail_reason_cancelled" />
   <java-symbol type="string" name="write_fail_reason_cannot_write" />
+  <java-symbol type="string" name="transient_navigation_confirmation" />
+  <java-symbol type="string" name="transient_navigation_confirmation_long" />
 
   <java-symbol type="plurals" name="abbrev_in_num_days" />
   <java-symbol type="plurals" name="abbrev_in_num_hours" />
diff --git a/packages/SystemUI/res/values-sw600dp-port/refs.xml b/packages/SystemUI/res/values-sw600dp-port/refs.xml
deleted file mode 100644
index 62fb77d..0000000
--- a/packages/SystemUI/res/values-sw600dp-port/refs.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2013, 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.
-*/
--->
-<resources>
-    <item type="string" name="hiding_navigation_confirmation_message">@string/hiding_navigation_confirmation_message_long</item>
-</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 33a85c3..eec8177 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -503,10 +503,4 @@
     <string name="status_bar_help_title">Notifications appear here</string>
     <!-- Body of help text shown when the notification panel is pulled down for the very first time. [CHAR LIMIT=NONE] -->
     <string name="status_bar_help_text">Access them anytime by swiping down.\nSwipe down again for system controls.</string>
-
-    <!-- Toast bar message when hiding the navigation bar -->
-    <string name="hiding_navigation_confirmation_message">Swipe edge of screen to reveal bar</string>
-
-    <!-- Longer version of toast bar message when hiding the navigation bar (if room) -->
-    <string name="hiding_navigation_confirmation_message_long">Swipe from edge of screen to reveal system bar</string>
 </resources>
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 c914a34..0a09a52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -57,7 +57,6 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
-import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewPropertyAnimator;
@@ -72,7 +71,7 @@
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
-import android.widget.Toast;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
@@ -129,8 +128,6 @@
 
     private static final int STATUS_OR_NAV_TRANSIENT =
             View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
-    private static final int TRANSIENT_NAV_HIDING =
-            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT;
     private static final long AUTOHIDE_TIMEOUT_MS = 3000;
     private static final float TRANSPARENT_ALPHA = 0.7f;
 
@@ -314,37 +311,6 @@
         }
     };
 
-    private Toast mHidingNavigationConfirmation;
-    private boolean mHidingNavigationConfirmationDismissed;
-
-    private final View.OnTouchListener mDismissHidingNavigationConfirmationOnTouchOutside =
-            new View.OnTouchListener() {
-                @Override
-                public boolean onTouch(View v, MotionEvent event) {
-                    if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
-                        dismissHidingNavigationConfirmation();
-                    }
-                    return false;
-                }
-            };
-
-    private final Runnable mHidingNavigationConfirmationAction = new Runnable() {
-        @Override
-        public void run() {
-            if (mHidingNavigationConfirmation != null) {
-                final boolean isGloballyConfirmed = Prefs.read(mContext)
-                        .getBoolean(Prefs.HIDING_NAVIGATION_CONFIRMED, false);
-                if (!isGloballyConfirmed) {
-                    // user pressed button, consider this a confirmation
-                    Prefs.edit(mContext)
-                            .putBoolean(Prefs.HIDING_NAVIGATION_CONFIRMED, true)
-                            .apply();
-                }
-                dismissHidingNavigationConfirmation();
-            }
-        }
-    };
-
     private boolean mAutohideSuspended;
 
     private final Runnable mAutohide = new Runnable() {
@@ -1955,16 +1921,6 @@
                 }
             }
 
-            // update hiding navigation confirmation
-            if (mNavigationBarView != null) {
-                boolean oldShowConfirm = (oldVal & TRANSIENT_NAV_HIDING) == TRANSIENT_NAV_HIDING;
-                boolean newShowConfirm = (newVal & TRANSIENT_NAV_HIDING) == TRANSIENT_NAV_HIDING;
-                if (!oldShowConfirm && newShowConfirm) {
-                    mHidingNavigationConfirmationDismissed = false;
-                }
-                setHidingNavigationConfirmationVisible(newShowConfirm);
-            }
-
             // send updated sysui visibility to window manager
             notifyUiVisibilityChanged(mSystemUiVisibility);
         }
@@ -1987,45 +1943,6 @@
                 : BAR_MODE_NORMAL;
     }
 
-    private void dismissHidingNavigationConfirmation() {
-        if (mHidingNavigationConfirmation != null) {
-            mHidingNavigationConfirmationDismissed = true;
-            mHidingNavigationConfirmation.cancel();
-            mHidingNavigationConfirmation = null;
-        }
-    }
-
-    private void setHidingNavigationConfirmationVisible(boolean visible) {
-        if (DEBUG) Log.d(TAG, "setHidingNavigationConfirmationVisible " + visible);
-        if (visible &&
-                mHidingNavigationConfirmation == null && !mHidingNavigationConfirmationDismissed) {
-            // create the confirmation toast bar
-            int msg = R.string.hiding_navigation_confirmation_message;
-            mHidingNavigationConfirmation = Toast.makeBar(mContext, msg, Toast.LENGTH_INFINITE)
-                    .setAction(com.android.internal.R.string.ok,
-                            mHidingNavigationConfirmationAction);
-            View v = mHidingNavigationConfirmation.getView();
-            v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-            boolean isGloballyConfirmed = Prefs.read(mContext)
-                    .getBoolean(Prefs.HIDING_NAVIGATION_CONFIRMED, false);
-            if (isGloballyConfirmed) {
-                // dismiss on outside touch if globally confirmed
-                v.setOnTouchListener(mDismissHidingNavigationConfirmationOnTouchOutside);
-            }
-            // position at the bottom like normal toasts, but use top gravity
-            // to avoid jumping around when showing/hiding the nav bar
-            v.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-            int offsetY = mContext.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.toast_y_offset);
-            mHidingNavigationConfirmation.setGravity(Gravity.TOP,
-                    0, mCurrentDisplaySize.y - v.getMeasuredHeight() / 2 - offsetY);
-            // show the confirmation
-            mHidingNavigationConfirmation.show();
-        } else if (!visible) {
-            dismissHidingNavigationConfirmation();
-        }
-    }
-
     @Override
     public void resumeAutohide() {
         if (mAutohideSuspended) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
index 3d51f20..16a92ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java
@@ -25,8 +25,6 @@
     public static final String SHOWN_COMPAT_MODE_HELP = "shown_compat_mode_help";
     public static final String SHOWN_QUICK_SETTINGS_HELP = "shown_quick_settings_help";
 
-    public static final String HIDING_NAVIGATION_CONFIRMED = "hiding_navigation_confirmed";
-
     public static SharedPreferences read(Context context) {
         return context.getSharedPreferences(Prefs.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
     }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index ae7120f..952dfda 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -558,6 +558,7 @@
     private static final int TRANSIENT_BAR_HIDING = 2;
     private int mStatusTransientBar;
     private int mNavigationTransientBar;
+    private TransientNavigationConfirmation mTransientNavigationConfirmation;
 
     private SystemGesturesPointerEventListener mSystemGestures;
 
@@ -942,6 +943,7 @@
                         }
                     }
                 });
+        mTransientNavigationConfirmation = new TransientNavigationConfirmation(mContext, mHandler);
         mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
 
         mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
@@ -3173,11 +3175,12 @@
                             = mOverscanScreenTop + mOverscanScreenHeight;
                 } else if (mCanHideNavigationBar
                         && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
-                        && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
-                        && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+                        && (attrs.type == TYPE_TOAST
+                            || (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
+                    // only do this for application windows (or toasts) 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
@@ -3879,6 +3882,9 @@
             case KeyEvent.KEYCODE_POWER: {
                 result &= ~ACTION_PASS_TO_USER;
                 if (down) {
+                    if (isScreenOn && isNavigationBarTransient(mLastSystemUiFlags)) {
+                        mTransientNavigationConfirmation.unconfirmLastPackage();
+                    }
                     if (isScreenOn && !mPowerKeyTriggered
                             && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                         mPowerKeyTriggered = true;
@@ -5019,7 +5025,7 @@
         if (mForcingShowNavBar && mFocusedWindow.getSurfaceLayer() < mForcingShowNavBarLayer) {
             tmpVisibility &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
         }
-        final int visibility = updateTransientBarsLw(tmpVisibility);
+        final int visibility = updateTransientBarsLw(mLastSystemUiFlags, tmpVisibility);
         final int diff = visibility ^ mLastSystemUiFlags;
         final boolean needsMenu = mFocusedWindow.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
         if (diff == 0 && mLastFocusNeedsMenu == needsMenu
@@ -5047,7 +5053,7 @@
         return diff;
     }
 
-    private int updateTransientBarsLw(int vis) {
+    private int updateTransientBarsLw(int oldVis, int vis) {
         if (ImmersiveModeTesting.enabled) {
             vis = ImmersiveModeTesting.applyForced(mFocusedWindow, vis);
         }
@@ -5059,9 +5065,10 @@
                     | View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT;
             vis = (vis & ~flags) | (mLastSystemUiFlags & flags);
         }
-        boolean transientAllowed = (vis & View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT) != 0;
         if (mStatusTransientBar == TRANSIENT_BAR_SHOWING) {
             // status transient bar requested
+            boolean transientAllowed =
+                    (vis & View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT) != 0;
             boolean hideStatusBarWM =
                     (mFocusedWindow.getAttrs().flags
                             & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
@@ -5092,13 +5099,16 @@
                 }
             }
         }
+        boolean oldTransientNav = isNavigationBarTransient(oldVis);
+        boolean isTransientNav = isNavigationBarTransient(vis);
+        if (mFocusedWindow != null && oldTransientNav != isTransientNav) {
+            final int uid = getCurrentUserId();
+            final String pkg = mFocusedWindow.getOwningPackage();
+            mTransientNavigationConfirmation.transientNavigationChanged(uid, pkg, isTransientNav);
+        }
         if (mNavigationTransientBar == TRANSIENT_BAR_SHOWING) {
             // navigation transient bar requested
-            boolean hideNavigationBarSysui =
-                    (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
-            boolean transientNavigationBarAllowed =
-                    mNavigationBar != null && hideNavigationBarSysui && transientAllowed;
-            if (!transientNavigationBarAllowed) {
+            if (!isTransientNav) {
                 mNavigationTransientBar = TRANSIENT_BAR_NONE;
             } else {
                 // show navigation transient bar
@@ -5112,6 +5122,12 @@
         return vis;
     }
 
+    private boolean isNavigationBarTransient(int vis) {
+        return mNavigationBar != null
+                && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
+                && (vis & View.SYSTEM_UI_FLAG_ALLOW_TRANSIENT) != 0;
+    }
+
     private boolean setBarShowingLw(WindowState win, final boolean show) {
         final int window =
                   win == mStatusBar ? StatusBarManager.WINDOW_STATUS_BAR
diff --git a/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java b/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java
new file mode 100644
index 0000000..bd5bd80
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 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.policy.impl;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.internal.R;
+
+/**
+ *  Helper to manage showing/hiding a confirmation prompt when the transient navigation bar
+ *  is hidden.
+ */
+public class TransientNavigationConfirmation {
+    private final String TAG = "TransientNavigationConfirmation";
+    private final boolean DEBUG = false;
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final ArraySet<String> mConfirmedUserPackages = new ArraySet<String>();
+
+    private final Runnable mHandleDismiss = new Runnable() {
+        @Override
+        public void run() {
+            if (mToast != null) {
+                mToast.cancel();
+                mToast = null;
+            }
+        }
+    };
+
+    private Toast mToast;
+    private String mLastUserPackage;
+
+    public TransientNavigationConfirmation(Context context, Handler handler) {
+        mContext = context;
+        mHandler = handler;
+    }
+
+    public void transientNavigationChanged(int userId, String pkg, boolean isNavTransient) {
+        if (pkg == null) {
+            return;
+        }
+        String userPkg = userId + ":" + pkg;
+        if (isNavTransient) {
+            mLastUserPackage = userPkg;
+            if (!mConfirmedUserPackages.contains(userPkg)) {
+                if (DEBUG) Slog.d(TAG, "Showing transient navigation confirmation for " + userPkg);
+                mHandler.post(handleShowConfirmation(userPkg));
+            }
+        } else {
+            mLastUserPackage = null;
+            if (DEBUG) Slog.d(TAG, "Hiding transient navigation confirmation for " + userPkg);
+            mHandler.post(mHandleDismiss);
+        }
+    }
+
+    public void unconfirmLastPackage() {
+        if (mLastUserPackage != null) {
+            if (DEBUG) Slog.d(TAG, "Unconfirming transient navigation for " + mLastUserPackage);
+            mConfirmedUserPackages.remove(mLastUserPackage);
+        }
+    }
+
+    private Runnable handleShowConfirmation(final String userPkg) {
+        return new Runnable() {
+            @Override
+            public void run() {
+                // create the confirmation toast bar
+                final int msg = R.string.transient_navigation_confirmation;
+                mToast = Toast.makeBar(mContext, msg, Toast.LENGTH_INFINITE);
+                mToast.setAction(R.string.ok, confirmAction(userPkg));
+
+                // we will be hiding the nav bar, so layout as if it's already hidden
+                mToast.getView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+
+                // show the confirmation
+                mToast.show();
+            }
+        };
+    }
+
+    private Runnable confirmAction(final String userPkg) {
+        return new Runnable() {
+            @Override
+            public void run() {
+                mConfirmedUserPackages.add(userPkg);
+                mHandleDismiss.run();
+            }
+        };
+    }
+}