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
This commit is contained in:
John Spurlock
2013-08-10 06:52:28 -04:00
parent 43e56c4a91
commit 34e13d90ed
9 changed files with 149 additions and 107 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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>

View File

@@ -57,7 +57,6 @@ import android.view.Gravity;
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.ImageView;
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 @@ public class PhoneStatusBar extends BaseStatusBar {
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 @@ public class PhoneStatusBar extends BaseStatusBar {
}
};
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 @@ public class PhoneStatusBar extends BaseStatusBar {
}
}
// 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 @@ public class PhoneStatusBar extends BaseStatusBar {
: 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) {

View File

@@ -25,8 +25,6 @@ public class Prefs {
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);
}

View File

@@ -558,6 +558,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final int TRANSIENT_BAR_HIDING = 2;
private int mStatusTransientBar;
private int mNavigationTransientBar;
private TransientNavigationConfirmation mTransientNavigationConfirmation;
private SystemGesturesPointerEventListener mSystemGestures;
@@ -942,6 +943,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
});
mTransientNavigationConfirmation = new TransientNavigationConfirmation(mContext, mHandler);
mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
@@ -3173,11 +3175,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
= 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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
| 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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
}
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 @@ public class PhoneWindowManager implements WindowManagerPolicy {
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

View File

@@ -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();
}
};
}
}