Move ImmersiveModeConfirmation to system UI (4/n)
This change move the WM side immersive mode confirmation window to the system UI side. The major change of the immersive mode confirmation mode to accomodate system UI side logic includes: 1. Power key panic is handled purely in DisplayPolicy, with a tracking of existance of the immersive confirmation window. 2. Introduce another layout params private flag to identify the immersive mode confiramtion mode. 3. CLIENT_* flags handling. 4. Vr and release handling, and most of the settings and display change handling moved to system UI. 5. Updated the text color of the window to match the themed background color. Introduce CLIENT_IMMERSIVE_CONFIRMATION flag to control whether client or the server side of immersive confirmation window shall be used. The flag will only be in effect if the CLIENT_TRANSIENT flag is not true, otherwise, the client side confirmation window will always be used. Bug: 277290737 Test: build Test: enable CLIENT_TRANSIENT and check the existance of the immersive mode confirmation window. Change-Id: I99f6097cb867f9808aecf4c7105289550001f452
This commit is contained in:
@@ -310,6 +310,16 @@ public final class ViewRootImpl implements ViewParent,
|
||||
public static final boolean CLIENT_TRANSIENT =
|
||||
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
|
||||
|
||||
@@ -3097,6 +3097,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
|
||||
@@ -3241,6 +3251,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,
|
||||
@@ -3324,6 +3335,10 @@ public interface WindowManager extends ViewManager {
|
||||
mask = PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -159,6 +160,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
|
||||
|
||||
@@ -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
|
||||
@@ -782,6 +795,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -516,6 +516,21 @@ public class CommandQueueTest extends SysuiTestCase {
|
||||
verify(mCallbacks).setNavigationBarLumaSamplingEnabled(eq(1), eq(true));
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
@@ -139,6 +139,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.
|
||||
*
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -229,7 +230,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();
|
||||
@@ -469,6 +471,9 @@ public class ImmersiveModeConfirmation {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (CLIENT_TRANSIENT) {
|
||||
return;
|
||||
}
|
||||
switch(msg.what) {
|
||||
case SHOW:
|
||||
handleShow(msg.arg1);
|
||||
|
||||
Reference in New Issue
Block a user