summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/ViewRootImpl.java10
-rw-r--r--core/java/android/view/WindowManager.java15
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl13
-rw-r--r--packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml2
-rw-r--r--packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml2
-rw-r--r--packages/SystemUI/res/layout/immersive_mode_cling.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java590
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java15
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java14
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java26
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java88
-rw-r--r--services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java7
14 files changed, 819 insertions, 21 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 379d1d8769ac..cf71d69d0141 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -310,6 +310,16 @@ public final class ViewRootImpl implements ViewParent,
SystemProperties.getBoolean("persist.wm.debug.client_transient", false);
/**
+ * Whether the client (system UI) is handling the immersive confirmation window. If
+ * {@link CLIENT_TRANSIENT} is set to true, the immersive confirmation window will always be the
+ * client instance and this flag will be ignored. Otherwise, the immersive confirmation window
+ * can be switched freely by this flag.
+ * @hide
+ */
+ public static final boolean CLIENT_IMMERSIVE_CONFIRMATION =
+ SystemProperties.getBoolean("persist.wm.debug.client_immersive_confirmation", false);
+
+ /**
* Whether the client should compute the window frame on its own.
* @hide
*/
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f1537ca950d6..355448353179 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3099,6 +3099,16 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE = 1 << 16;
/**
+ * Flag to indicate that this window is a immersive mode confirmation window. The window
+ * should be ignored when calculating insets control. This is used for prompt window
+ * triggered by insets visibility changes. If it can take over the insets control, the
+ * visibility will change unexpectedly and the window may dismiss itself. Power button panic
+ * handling will be disabled when this window exists.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW = 1 << 17;
+
+ /**
* Flag to indicate that any window added by an application process that is of type
* {@link #TYPE_TOAST} or that requires
* {@link android.app.AppOpsManager#OP_SYSTEM_ALERT_WINDOW} permission should be hidden when
@@ -3242,6 +3252,7 @@ public interface WindowManager extends ViewManager {
PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS,
PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
+ PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
@@ -3326,6 +3337,10 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
name = "SUSTAINED_PERFORMANCE_MODE"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW,
+ equals = PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW,
+ name = "IMMERSIVE_CONFIRMATION_WINDOW"),
+ @ViewDebug.FlagToString(
mask = SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
equals = SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
name = "HIDE_NON_SYSTEM_OVERLAY_WINDOWS"),
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index d2564fb9c268..c6f5086b8346 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -63,6 +63,19 @@ oneway interface IStatusBar
void cancelPreloadRecentApps();
void showScreenPinningRequest(int taskId);
+ /**
+ * Notify system UI the immersive prompt should be dismissed as confirmed, and the confirmed
+ * status should be saved without user clicking on the button. This could happen when a user
+ * swipe on the edge with the confirmation prompt showing.
+ */
+ void confirmImmersivePrompt();
+
+ /**
+ * Notify system UI the immersive mode changed. This shall be removed when client immersive is
+ * enabled.
+ */
+ void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode);
+
void dismissKeyboardShortcutsMenu();
void toggleKeyboardShortcutsMenu(int deviceId);
diff --git a/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml
index 4029702ec6b4..32e88ab22b91 100644
--- a/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml
+++ b/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml
@@ -17,7 +17,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
- <solid android:color="@android:color/white" />
+ <solid android:color="?android:attr/colorBackground" />
<size
android:height="56dp"
diff --git a/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml
index e3c7d0ce89aa..12c3e23bf0a0 100644
--- a/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml
+++ b/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml
@@ -17,7 +17,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
- <solid android:color="#80ffffff" />
+ <solid android:color="?android:attr/colorBackground" />
<size
android:height="76dp"
diff --git a/packages/SystemUI/res/layout/immersive_mode_cling.xml b/packages/SystemUI/res/layout/immersive_mode_cling.xml
index bfb8184ee044..e6529b9aa9a1 100644
--- a/packages/SystemUI/res/layout/immersive_mode_cling.xml
+++ b/packages/SystemUI/res/layout/immersive_mode_cling.xml
@@ -58,7 +58,7 @@
android:paddingStart="48dp"
android:paddingTop="40dp"
android:text="@string/immersive_cling_title"
- android:textColor="@android:color/white"
+ android:textColor="?android:attr/textColorPrimaryInverse"
android:textSize="24sp" />
<TextView
@@ -70,7 +70,7 @@
android:paddingStart="48dp"
android:paddingTop="12.6dp"
android:text="@string/immersive_cling_description"
- android:textColor="@android:color/white"
+ android:textColor="?android:attr/textColorPrimaryInverse"
android:textSize="16sp" />
<Button
@@ -85,7 +85,7 @@
android:paddingEnd="8dp"
android:paddingStart="8dp"
android:text="@string/immersive_cling_positive"
- android:textColor="@android:color/white"
+ android:textColor="?android:attr/textColorPrimaryInverse"
android:textSize="14sp" />
</RelativeLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index a560accfff68..d9665c5b5047 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -48,6 +48,7 @@ import com.android.systemui.reardisplay.RearDisplayDialogController
import com.android.systemui.recents.Recents
import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
+import com.android.systemui.statusbar.ImmersiveModeConfirmation
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
import com.android.systemui.statusbar.phone.LockscreenWallpaper
@@ -162,6 +163,12 @@ abstract class SystemUICoreStartableModule {
@ClassKey(Recents::class)
abstract fun bindRecents(sysui: Recents): CoreStartable
+ /** Inject into ImmersiveModeConfirmation. */
+ @Binds
+ @IntoMap
+ @ClassKey(ImmersiveModeConfirmation::class)
+ abstract fun bindImmersiveModeConfirmation(sysui: ImmersiveModeConfirmation): CoreStartable
+
/** Inject into RingtonePlayer. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 92df78bac17f..6304c1ea2635 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -168,7 +168,10 @@ public class CommandQueue extends IStatusBar.Stub implements
private static final int MSG_ENTER_STAGE_SPLIT_FROM_RUNNING_APP = 71 << MSG_SHIFT;
private static final int MSG_SHOW_MEDIA_OUTPUT_SWITCHER = 72 << MSG_SHIFT;
private static final int MSG_TOGGLE_TASKBAR = 73 << MSG_SHIFT;
-
+ private static final int MSG_SETTING_CHANGED = 74 << MSG_SHIFT;
+ private static final int MSG_LOCK_TASK_MODE_CHANGED = 75 << MSG_SHIFT;
+ private static final int MSG_CONFIRM_IMMERSIVE_PROMPT = 77 << MSG_SHIFT;
+ private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -498,6 +501,16 @@ public class CommandQueue extends IStatusBar.Stub implements
* @see IStatusBar#showMediaOutputSwitcher
*/
default void showMediaOutputSwitcher(String packageName) {}
+
+ /**
+ * @see IStatusBar#confirmImmersivePrompt
+ */
+ default void confirmImmersivePrompt() {}
+
+ /**
+ * @see IStatusBar#immersiveModeChanged
+ */
+ default void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {}
}
@VisibleForTesting
@@ -783,6 +796,23 @@ public class CommandQueue extends IStatusBar.Stub implements
}
@Override
+ public void confirmImmersivePrompt() {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_CONFIRM_IMMERSIVE_PROMPT).sendToTarget();
+ }
+ }
+
+ @Override
+ public void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {
+ synchronized (mLock) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = rootDisplayAreaId;
+ args.argi2 = isImmersiveMode ? 1 : 0;
+ mHandler.obtainMessage(MSG_IMMERSIVE_CHANGED, args).sendToTarget();
+ }
+ }
+
+ @Override
public void appTransitionPending(int displayId) {
appTransitionPending(displayId, false /* forced */);
}
@@ -1810,6 +1840,19 @@ public class CommandQueue extends IStatusBar.Stub implements
mCallbacks.get(i).showMediaOutputSwitcher(clientPackageName);
}
break;
+ case MSG_CONFIRM_IMMERSIVE_PROMPT:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).confirmImmersivePrompt();
+ }
+ break;
+ case MSG_IMMERSIVE_CHANGED:
+ args = (SomeArgs) msg.obj;
+ int rootDisplayAreaId = args.argi1;
+ boolean isImmersiveMode = args.argi2 != 0;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).immersiveModeChanged(rootDisplayAreaId, isImmersiveMode);
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
new file mode 100644
index 000000000000..a7ec02ff43c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -0,0 +1,590 @@
+/*
+ * Copyright (C) 2023 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.systemui.statusbar;
+
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.StatusBarManager.DISABLE_BACK;
+import static android.app.StatusBarManager.DISABLE_HOME;
+import static android.app.StatusBarManager.DISABLE_RECENT;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.ViewRootImpl.CLIENT_IMMERSIVE_CONFIRMATION;
+import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
+
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.ContentObserver;
+import android.graphics.Insets;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+
+/**
+ * Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden
+ * entering immersive mode.
+ */
+public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Callbacks,
+ TaskStackChangeListener {
+ private static final String TAG = "ImmersiveModeConfirm";
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution
+ private static final String CONFIRMED = "confirmed";
+ private static final int IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE =
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+
+ private static boolean sConfirmed;
+ private final SecureSettings mSecureSettings;
+
+ private Context mDisplayContext;
+ private final Context mSysUiContext;
+ private final Handler mHandler = new H(Looper.getMainLooper());
+ private long mShowDelayMs = 0L;
+ private final IBinder mWindowToken = new Binder();
+ private final CommandQueue mCommandQueue;
+
+ private ClingWindowView mClingWindow;
+ /** The last {@link WindowManager} that is used to add the confirmation window. */
+ @Nullable
+ private WindowManager mWindowManager;
+ /**
+ * The WindowContext that is registered with {@link #mWindowManager} with options to specify the
+ * {@link RootDisplayArea} to attach the confirmation window.
+ */
+ @Nullable
+ private Context mWindowContext;
+ /**
+ * The root display area feature id that the {@link #mWindowContext} is attaching to.
+ */
+ private int mWindowContextRootDisplayAreaId = FEATURE_UNDEFINED;
+ // Local copy of vr mode enabled state, to avoid calling into VrManager with
+ // the lock held.
+ private boolean mVrModeEnabled = false;
+ private boolean mCanSystemBarsBeShownByUser = true;
+ private int mLockTaskState = LOCK_TASK_MODE_NONE;
+ private boolean mNavBarEmpty;
+
+ private ContentObserver mContentObserver;
+
+ @Inject
+ public ImmersiveModeConfirmation(Context context, CommandQueue commandQueue,
+ SecureSettings secureSettings) {
+ mSysUiContext = context;
+ final Display display = mSysUiContext.getDisplay();
+ mDisplayContext = display.getDisplayId() == DEFAULT_DISPLAY
+ ? mSysUiContext : mSysUiContext.createDisplayContext(display);
+ mCommandQueue = commandQueue;
+ mSecureSettings = secureSettings;
+ }
+
+ boolean loadSetting(int currentUserId) {
+ final boolean wasConfirmed = sConfirmed;
+ sConfirmed = false;
+ if (DEBUG) Log.d(TAG, String.format("loadSetting() currentUserId=%d", currentUserId));
+ String value = null;
+ try {
+ value = mSecureSettings.getStringForUser(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
+ UserHandle.USER_CURRENT);
+ sConfirmed = CONFIRMED.equals(value);
+ if (DEBUG) Log.d(TAG, "Loaded sConfirmed=" + sConfirmed);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error loading confirmations, value=" + value, t);
+ }
+ return sConfirmed != wasConfirmed;
+ }
+
+ private static void saveSetting(Context context) {
+ if (DEBUG) Log.d(TAG, "saveSetting()");
+ try {
+ final String value = sConfirmed ? CONFIRMED : null;
+ Settings.Secure.putStringForUser(context.getContentResolver(),
+ Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
+ value,
+ UserHandle.USER_CURRENT);
+ if (DEBUG) Log.d(TAG, "Saved value=" + value);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error saving confirmations, sConfirmed=" + sConfirmed, t);
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (displayId != mSysUiContext.getDisplayId()) {
+ return;
+ }
+ mHandler.removeMessages(H.SHOW);
+ mHandler.removeMessages(H.HIDE);
+ IVrManager vrManager = IVrManager.Stub.asInterface(
+ ServiceManager.getService(Context.VR_SERVICE));
+ if (vrManager != null) {
+ try {
+ vrManager.unregisterListener(mVrStateCallbacks);
+ } catch (RemoteException ex) {
+ }
+ }
+ mCommandQueue.removeCallback(this);
+ }
+
+ private void onSettingChanged(int currentUserId) {
+ final boolean changed = loadSetting(currentUserId);
+ // Remove the window if the setting changes to be confirmed.
+ if (changed && sConfirmed) {
+ mHandler.sendEmptyMessage(H.HIDE);
+ }
+ }
+
+ @Override
+ public void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {
+ mHandler.removeMessages(H.SHOW);
+ if (isImmersiveMode) {
+ if (DEBUG) Log.d(TAG, "immersiveModeChanged() sConfirmed=" + sConfirmed);
+ boolean userSetupComplete = (mSecureSettings.getIntForUser(
+ Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0);
+
+ if ((DEBUG_SHOW_EVERY_TIME || !sConfirmed)
+ && userSetupComplete
+ && !mVrModeEnabled
+ && mCanSystemBarsBeShownByUser
+ && !mNavBarEmpty
+ && !UserManager.isDeviceInDemoMode(mDisplayContext)
+ && (mLockTaskState != LOCK_TASK_MODE_LOCKED)) {
+ final Message msg = mHandler.obtainMessage(
+ H.SHOW);
+ msg.arg1 = rootDisplayAreaId;
+ mHandler.sendMessageDelayed(msg, mShowDelayMs);
+ }
+ } else {
+ mHandler.sendEmptyMessage(H.HIDE);
+ }
+ }
+
+ @Override
+ public void disable(int displayId, int disableFlag, int disableFlag2, boolean animate) {
+ if (mSysUiContext.getDisplayId() != displayId) {
+ return;
+ }
+ final int disableNavigationBar = (DISABLE_HOME | DISABLE_BACK | DISABLE_RECENT);
+ mNavBarEmpty = (disableFlag & disableNavigationBar) == disableNavigationBar;
+ }
+
+ @Override
+ public void confirmImmersivePrompt() {
+ if (mClingWindow != null) {
+ if (DEBUG) Log.d(TAG, "confirmImmersivePrompt()");
+ mHandler.post(mConfirm);
+ }
+ }
+
+ private void handleHide() {
+ if (mClingWindow != null) {
+ if (DEBUG) Log.d(TAG, "Hiding immersive mode confirmation");
+ if (mWindowManager != null) {
+ try {
+ mWindowManager.removeView(mClingWindow);
+ } catch (WindowManager.InvalidDisplayException e) {
+ Log.w(TAG, "Fail to hide the immersive confirmation window because of "
+ + e);
+ }
+ mWindowManager = null;
+ mWindowContext = null;
+ }
+ mClingWindow = null;
+ }
+ }
+
+ private WindowManager.LayoutParams getClingWindowLayoutParams() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
+ // Trusted overlay so touches outside the touchable area are allowed to pass through
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+ | WindowManager.LayoutParams.PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW;
+ lp.setTitle("ImmersiveModeConfirmation");
+ lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation;
+ lp.token = getWindowToken();
+ return lp;
+ }
+
+ private FrameLayout.LayoutParams getBubbleLayoutParams() {
+ return new FrameLayout.LayoutParams(
+ mSysUiContext.getResources().getDimensionPixelSize(
+ R.dimen.immersive_mode_cling_width),
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_HORIZONTAL | Gravity.TOP);
+ }
+
+ /**
+ * @return the window token that's used by all ImmersiveModeConfirmation windows.
+ */
+ IBinder getWindowToken() {
+ return mWindowToken;
+ }
+
+ @Override
+ public void start() {
+ if (CLIENT_TRANSIENT || CLIENT_IMMERSIVE_CONFIRMATION) {
+ mCommandQueue.addCallback(this);
+
+ final Resources r = mSysUiContext.getResources();
+ mShowDelayMs = r.getInteger(R.integer.dock_enter_exit_duration) * 3L;
+ mCanSystemBarsBeShownByUser = !r.getBoolean(
+ R.bool.config_remoteInsetsControllerControlsSystemBars) || r.getBoolean(
+ R.bool.config_remoteInsetsControllerSystemBarsCanBeShownByUserAction);
+ IVrManager vrManager = IVrManager.Stub.asInterface(
+ ServiceManager.getService(Context.VR_SERVICE));
+ if (vrManager != null) {
+ try {
+ mVrModeEnabled = vrManager.getVrModeState();
+ vrManager.registerListener(mVrStateCallbacks);
+ mVrStateCallbacks.onVrStateChanged(mVrModeEnabled);
+ } catch (RemoteException e) {
+ // Ignore, we cannot do anything if we failed to access vr manager.
+ }
+ }
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
+ mContentObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onSettingChanged(mSysUiContext.getUserId());
+ }
+ };
+
+ // Register to listen for changes in Settings.Secure settings.
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, mContentObserver,
+ UserHandle.USER_CURRENT);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.USER_SETUP_COMPLETE, mContentObserver,
+ UserHandle.USER_CURRENT);
+ }
+ }
+
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+ @Override
+ public void onVrStateChanged(boolean enabled) {
+ mVrModeEnabled = enabled;
+ if (mVrModeEnabled) {
+ mHandler.removeMessages(H.SHOW);
+ mHandler.sendEmptyMessage(H.HIDE);
+ }
+ }
+ };
+
+ private class ClingWindowView extends FrameLayout {
+ private static final int BGCOLOR = 0x80000000;
+ private static final int OFFSET_DP = 96;
+ private static final int ANIMATION_DURATION = 250;
+
+ private final Runnable mConfirm;
+ private final ColorDrawable mColor = new ColorDrawable(0);
+ private final Interpolator mInterpolator;
+ private ValueAnimator mColorAnim;
+ private ViewGroup mClingLayout;
+
+ private Runnable mUpdateLayoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mClingLayout != null && mClingLayout.getParent() != null) {
+ mClingLayout.setLayoutParams(getBubbleLayoutParams());
+ }
+ }
+ };
+
+ private ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener =
+ new ViewTreeObserver.OnComputeInternalInsetsListener() {
+ private final int[] mTmpInt2 = new int[2];
+
+ @Override
+ public void onComputeInternalInsets(
+ ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ // Set touchable region to cover the cling layout.
+ mClingLayout.getLocationInWindow(mTmpInt2);
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ inoutInfo.touchableRegion.set(
+ mTmpInt2[0],
+ mTmpInt2[1],
+ mTmpInt2[0] + mClingLayout.getWidth(),
+ mTmpInt2[1] + mClingLayout.getHeight());
+ }
+ };
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+ post(mUpdateLayoutRunnable);
+ }
+ }
+ };
+
+ ClingWindowView(Context context, Runnable confirm) {
+ super(context);
+ mConfirm = confirm;
+ setBackground(mColor);
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mInterpolator = AnimationUtils
+ .loadInterpolator(mContext, android.R.interpolator.linear_out_slow_in);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mContext.getDisplay().getMetrics(metrics);
+ float density = metrics.density;
+
+ getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
+
+ // create the confirmation cling
+ mClingLayout = (ViewGroup)
+ View.inflate(mSysUiContext, R.layout.immersive_mode_cling, null);
+
+ TypedArray ta = mDisplayContext.obtainStyledAttributes(
+ new int[]{android.R.attr.colorAccent});
+ int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ mClingLayout.setBackgroundColor(colorAccent);
+ ImageView expandMore = mClingLayout.findViewById(R.id.immersive_cling_ic_expand_more);
+ if (expandMore != null) {
+ expandMore.setImageTintList(ColorStateList.valueOf(colorAccent));
+ }
+ ImageView lightBgCirc = mClingLayout.findViewById(R.id.immersive_cling_back_bg_light);
+ if (lightBgCirc != null) {
+ // Set transparency to 50%
+ lightBgCirc.setImageAlpha(128);
+ }
+
+ final Button ok = mClingLayout.findViewById(R.id.ok);
+ ok.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mConfirm.run();
+ }
+ });
+ addView(mClingLayout, getBubbleLayoutParams());
+
+ if (ActivityManager.isHighEndGfx()) {
+ final View cling = mClingLayout;
+ cling.setAlpha(0f);
+ cling.setTranslationY(-OFFSET_DP * density);
+
+ postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ cling.animate()
+ .alpha(1f)
+ .translationY(0)
+ .setDuration(ANIMATION_DURATION)
+ .setInterpolator(mInterpolator)
+ .withLayer()
+ .start();
+
+ mColorAnim = ValueAnimator.ofObject(new ArgbEvaluator(), 0, BGCOLOR);
+ mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final int c = (Integer) animation.getAnimatedValue();
+ mColor.setColor(c);
+ }
+ });
+ mColorAnim.setDuration(ANIMATION_DURATION);
+ mColorAnim.setInterpolator(mInterpolator);
+ mColorAnim.start();
+ }
+ });
+ } else {
+ mColor.setColor(BGCOLOR);
+ }
+
+ mContext.registerReceiver(mReceiver,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent motion) {
+ return true;
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ // we will be hiding the nav bar, so layout as if it's already hidden
+ return new WindowInsets.Builder(insets).setInsets(
+ Type.systemBars(), Insets.NONE).build();
+ }
+ }
+
+ /**
+ * To get window manager for the display.
+ *
+ * @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the
+ * confirmation window.
+ */
+ @NonNull
+ private WindowManager createWindowManager(int rootDisplayAreaId) {
+ if (mWindowManager != null) {
+ throw new IllegalStateException(
+ "Must not create a new WindowManager while there is an existing one");
+ }
+ // Create window context to specify the RootDisplayArea
+ final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
+ mWindowContextRootDisplayAreaId = rootDisplayAreaId;
+ mWindowContext = mDisplayContext.createWindowContext(
+ IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
+ mWindowManager = mWindowContext.getSystemService(WindowManager.class);
+ return mWindowManager;
+ }
+
+ /**
+ * Returns options that specify the {@link RootDisplayArea} to attach the confirmation window.
+ * {@code null} if the {@code rootDisplayAreaId} is {@link FEATURE_UNDEFINED}.
+ */
+ @Nullable
+ private Bundle getOptionsForWindowContext(int rootDisplayAreaId) {
+ // In case we don't care which root display area the window manager is specifying.
+ if (rootDisplayAreaId == FEATURE_UNDEFINED) {
+ return null;
+ }
+
+ final Bundle options = new Bundle();
+ options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
+ return options;
+ }
+
+ private void handleShow(int rootDisplayAreaId) {
+ if (mClingWindow != null) {
+ if (rootDisplayAreaId == mWindowContextRootDisplayAreaId) {
+ if (DEBUG) Log.d(TAG, "Immersive mode confirmation has already been shown");
+ return;
+ } else {
+ // Hide the existing confirmation before show a new one in the new root.
+ if (DEBUG) Log.d(TAG, "Immersive mode confirmation was shown in a different root");
+ handleHide();
+ }
+ }
+ if (DEBUG) Log.d(TAG, "Showing immersive mode confirmation");
+ mClingWindow = new ClingWindowView(mDisplayContext, mConfirm);
+ // show the confirmation
+ final WindowManager.LayoutParams lp = getClingWindowLayoutParams();
+ try {
+ createWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+ } catch (WindowManager.InvalidDisplayException e) {
+ Log.w(TAG, "Fail to show the immersive confirmation window because of " + e);
+ }
+ }
+
+ private final Runnable mConfirm = new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) Log.d(TAG, "mConfirm.run()");
+ if (!sConfirmed) {
+ sConfirmed = true;
+ saveSetting(mDisplayContext);
+ }
+ handleHide();
+ }
+ };
+
+ private final class H extends Handler {
+ private static final int SHOW = 1;
+ private static final int HIDE = 2;
+
+ H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (!CLIENT_TRANSIENT && !CLIENT_IMMERSIVE_CONFIRMATION) {
+ return;
+ }
+ switch(msg.what) {
+ case SHOW:
+ handleShow(msg.arg1);
+ break;
+ case HIDE:
+ handleHide();
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onLockTaskModeChanged(int lockTaskState) {
+ mLockTaskState = lockTaskState;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 1643e174ee13..b04d5d3d44e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -517,6 +517,21 @@ public class CommandQueueTest extends SysuiTestCase {
}
@Test
+ public void testConfirmImmersivePrompt() {
+ mCommandQueue.confirmImmersivePrompt();
+ waitForIdleSync();
+ verify(mCallbacks).confirmImmersivePrompt();
+ }
+
+ @Test
+ public void testImmersiveModeChanged() {
+ final int displayAreaId = 10;
+ mCommandQueue.immersiveModeChanged(displayAreaId, true);
+ waitForIdleSync();
+ verify(mCallbacks).immersiveModeChanged(displayAreaId, true);
+ }
+
+ @Test
public void testShowRearDisplayDialog() {
final int currentBaseState = 1;
mCommandQueue.showRearDisplayDialog(currentBaseState);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index efd8b6d9a943..a5123311d499 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -140,6 +140,20 @@ public interface StatusBarManagerInternal {
boolean showShutdownUi(boolean isReboot, String requestString);
/**
+ * Notify system UI the immersive prompt should be dismissed as confirmed, and the confirmed
+ * status should be saved without user clicking on the button. This could happen when a user
+ * swipe on the edge with the confirmation prompt showing.
+ */
+ void confirmImmersivePrompt();
+
+ /**
+ * Notify System UI that the system get into or exit immersive mode.
+ * @param rootDisplayAreaId The changed display area Id.
+ * @param isImmersiveMode {@code true} if the display area get into immersive mode.
+ */
+ void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode);
+
+ /**
* Show a rotation suggestion that a user may approve to rotate the screen.
*
* @param rotation rotation suggestion
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index cc849b6fbf91..40e9c1305f01 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -27,6 +27,7 @@ import static android.app.StatusBarManager.NavBarMode;
import static android.app.StatusBarManager.SessionFlags;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
import android.Manifest;
@@ -638,6 +639,31 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
return false;
}
+ @Override
+ public void confirmImmersivePrompt() {
+ if (mBar == null) {
+ return;
+ }
+ try {
+ mBar.confirmImmersivePrompt();
+ } catch (RemoteException ex) {
+ }
+ }
+
+ @Override
+ public void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {
+ if (mBar == null) {
+ return;
+ }
+ if (!CLIENT_TRANSIENT) {
+ // Only call from here when the client transient is not enabled.
+ try {
+ mBar.immersiveModeChanged(rootDisplayAreaId, isImmersiveMode);
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
// TODO(b/118592525): support it per display if necessary.
@Override
public void onProposedRotationChanged(int rotation, boolean isValid) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index ab89b8748e2f..1871cf6175c5 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -22,6 +22,7 @@ import static android.view.InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE;
import static android.view.InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS;
import static android.view.InsetsFrameProvider.SOURCE_DISPLAY;
import static android.view.InsetsFrameProvider.SOURCE_FRAME;
+import static android.view.ViewRootImpl.CLIENT_IMMERSIVE_CONFIRMATION;
import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -38,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACK
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
@@ -194,6 +196,8 @@ public class DisplayPolicy {
private final ScreenshotHelper mScreenshotHelper;
private final Object mServiceAcquireLock = new Object();
+ private long mPanicTime;
+ private final long mPanicThresholdMs;
private StatusBarManagerInternal mStatusBarManagerInternal;
@Px
@@ -246,6 +250,8 @@ public class DisplayPolicy {
private volatile boolean mKeyguardDrawComplete;
private volatile boolean mWindowManagerDrawComplete;
+ private boolean mImmersiveConfirmationWindowExists;
+
private WindowState mStatusBar = null;
private volatile WindowState mNotificationShade;
private WindowState mNavigationBar = null;
@@ -402,6 +408,7 @@ public class DisplayPolicy {
mCanSystemBarsBeShownByUser = !r.getBoolean(
R.bool.config_remoteInsetsControllerControlsSystemBars) || r.getBoolean(
R.bool.config_remoteInsetsControllerSystemBarsCanBeShownByUserAction);
+ mPanicThresholdMs = r.getInteger(R.integer.config_immersive_mode_confirmation_panic);
mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
Context.ACCESSIBILITY_SERVICE);
@@ -623,8 +630,12 @@ public class DisplayPolicy {
};
displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
displayContent.mTransitionController.registerLegacyListener(mAppTransitionListener);
- mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
- mService.mVrModeEnabled, mCanSystemBarsBeShownByUser);
+ if (CLIENT_TRANSIENT || CLIENT_IMMERSIVE_CONFIRMATION) {
+ mImmersiveModeConfirmation = null;
+ } else {
+ mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
+ mService.mVrModeEnabled, mCanSystemBarsBeShownByUser);
+ }
// TODO: Make it can take screenshot on external display
mScreenshotHelper = displayContent.isDefaultDisplay
@@ -1075,6 +1086,9 @@ public class DisplayPolicy {
mNavigationBar = win;
break;
}
+ if ((attrs.privateFlags & PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW) != 0) {
+ mImmersiveConfirmationWindowExists = true;
+ }
if (attrs.providedInsets != null) {
for (int i = attrs.providedInsets.length - 1; i >= 0; i--) {
final InsetsFrameProvider provider = attrs.providedInsets[i];
@@ -1234,6 +1248,9 @@ public class DisplayPolicy {
}
}
mInsetsSourceWindowsExceptIme.remove(win);
+ if ((win.mAttrs.privateFlags & PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW) != 0) {
+ mImmersiveConfirmationWindowExists = false;
+ }
}
WindowState getStatusBar() {
@@ -2171,7 +2188,11 @@ public class DisplayPolicy {
}
}
}
- mImmersiveModeConfirmation.confirmCurrentPrompt();
+ if (CLIENT_IMMERSIVE_CONFIRMATION || CLIENT_TRANSIENT) {
+ mStatusBarManagerInternal.confirmImmersivePrompt();
+ } else {
+ mImmersiveModeConfirmation.confirmCurrentPrompt();
+ }
}
boolean isKeyguardShowing() {
@@ -2221,7 +2242,8 @@ public class DisplayPolicy {
// Immersive mode confirmation should never affect the system bar visibility, otherwise
// it will unhide the navigation bar and hide itself.
- if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
+ if ((winCandidate.getAttrs().privateFlags
+ & PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW) != 0) {
if (mNotificationShade != null && mNotificationShade.canReceiveKeys()) {
// Let notification shade control the system bar visibility.
winCandidate = mNotificationShade;
@@ -2389,9 +2411,16 @@ public class DisplayPolicy {
// The immersive confirmation window should be attached to the immersive window root.
final RootDisplayArea root = win.getRootDisplayArea();
final int rootDisplayAreaId = root == null ? FEATURE_UNDEFINED : root.mFeatureId;
- mImmersiveModeConfirmation.immersiveModeChangedLw(rootDisplayAreaId, isImmersiveMode,
- mService.mPolicy.isUserSetupComplete(),
- isNavBarEmpty(disableFlags));
+ if (!CLIENT_TRANSIENT && !CLIENT_IMMERSIVE_CONFIRMATION) {
+ mImmersiveModeConfirmation.immersiveModeChangedLw(rootDisplayAreaId,
+ isImmersiveMode,
+ mService.mPolicy.isUserSetupComplete(),
+ isNavBarEmpty(disableFlags));
+ } else {
+ // TODO (b/277290737): Move this to the client side, instead of using a proxy.
+ callStatusBarSafely(statusBar -> statusBar.immersiveModeChanged(rootDisplayAreaId,
+ isImmersiveMode));
+ }
}
// Show transient bars for panic if needed.
@@ -2604,16 +2633,39 @@ public class DisplayPolicy {
void onPowerKeyDown(boolean isScreenOn) {
// Detect user pressing the power button in panic when an application has
// taken over the whole screen.
- boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,
- SystemClock.elapsedRealtime(), isImmersiveMode(mSystemUiControllingWindow),
- isNavBarEmpty(mLastDisableFlags));
+ boolean panic = false;
+ if (!CLIENT_TRANSIENT && !CLIENT_IMMERSIVE_CONFIRMATION) {
+ panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,
+ SystemClock.elapsedRealtime(), isImmersiveMode(mSystemUiControllingWindow),
+ isNavBarEmpty(mLastDisableFlags));
+ } else {
+ panic = isPowerKeyDownPanic(isScreenOn, SystemClock.elapsedRealtime(),
+ isImmersiveMode(mSystemUiControllingWindow), isNavBarEmpty(mLastDisableFlags));
+ }
if (panic) {
mHandler.post(mHiddenNavPanic);
}
}
+ private boolean isPowerKeyDownPanic(boolean isScreenOn, long time, boolean inImmersiveMode,
+ boolean navBarEmpty) {
+ if (!isScreenOn && (time - mPanicTime < mPanicThresholdMs)) {
+ // turning the screen back on within the panic threshold
+ return !mImmersiveConfirmationWindowExists;
+ }
+ if (isScreenOn && inImmersiveMode && !navBarEmpty) {
+ // turning the screen off, remember if we were in immersive mode
+ mPanicTime = time;
+ } else {
+ mPanicTime = 0;
+ }
+ return false;
+ }
+
void onVrStateChangedLw(boolean enabled) {
- mImmersiveModeConfirmation.onVrStateChangedLw(enabled);
+ if (!CLIENT_TRANSIENT && !CLIENT_IMMERSIVE_CONFIRMATION) {
+ mImmersiveModeConfirmation.onVrStateChangedLw(enabled);
+ }
}
/**
@@ -2626,7 +2678,9 @@ public class DisplayPolicy {
* {@link ActivityManager#LOCK_TASK_MODE_PINNED}.
*/
public void onLockTaskStateChangedLw(int lockTaskState) {
- mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState);
+ if (!CLIENT_TRANSIENT && !CLIENT_IMMERSIVE_CONFIRMATION) {
+ mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState);
+ }
}
/** Called when a {@link android.os.PowerManager#USER_ACTIVITY_EVENT_TOUCH} is sent. */
@@ -2643,7 +2697,11 @@ public class DisplayPolicy {
}
boolean onSystemUiSettingsChanged() {
- return mImmersiveModeConfirmation.onSettingChanged(mService.mCurrentUserId);
+ if (CLIENT_TRANSIENT || CLIENT_IMMERSIVE_CONFIRMATION) {
+ return false;
+ } else {
+ return mImmersiveModeConfirmation.onSettingChanged(mService.mCurrentUserId);
+ }
}
/**
@@ -2857,7 +2915,9 @@ public class DisplayPolicy {
mDisplayContent.mTransitionController.unregisterLegacyListener(mAppTransitionListener);
mHandler.post(mGestureNavigationSettingsObserver::unregister);
mHandler.post(mForceShowNavBarSettingsObserver::unregister);
- mImmersiveModeConfirmation.release();
+ if (!CLIENT_TRANSIENT && !CLIENT_IMMERSIVE_CONFIRMATION) {
+ mImmersiveModeConfirmation.release();
+ }
if (mService.mPointerLocationEnabled) {
setPointerLocationEnabled(false);
}
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 271d71ed0ec0..b2ba9d1cc8fe 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
@@ -234,7 +235,8 @@ public class ImmersiveModeConfirmation {
lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
// Trusted overlay so touches outside the touchable area are allowed to pass through
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
- | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+ | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+ | WindowManager.LayoutParams.PRIVATE_FLAG_IMMERSIVE_CONFIRMATION_WINDOW;
lp.setTitle("ImmersiveModeConfirmation");
lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation;
lp.token = getWindowToken();
@@ -475,6 +477,9 @@ public class ImmersiveModeConfirmation {
@Override
public void handleMessage(Message msg) {
+ if (CLIENT_TRANSIENT) {
+ return;
+ }
switch(msg.what) {
case SHOW:
handleShow(msg.arg1);