| /* |
| * Copyright (C) 2018 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.server.wm; |
| |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; |
| import static android.content.res.Configuration.UI_MODE_TYPE_CAR; |
| import static android.content.res.Configuration.UI_MODE_TYPE_MASK; |
| import static android.view.Display.TYPE_INTERNAL; |
| import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; |
| import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; |
| import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; |
| import static android.view.InsetsState.ITYPE_CAPTION_BAR; |
| import static android.view.InsetsState.ITYPE_CLIMATE_BAR; |
| import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; |
| import static android.view.InsetsState.ITYPE_IME; |
| import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; |
| import static android.view.InsetsState.ITYPE_LEFT_GESTURES; |
| import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; |
| import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT; |
| import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; |
| import static android.view.InsetsState.ITYPE_STATUS_BAR; |
| import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT; |
| import static android.view.InsetsState.ITYPE_TOP_GESTURES; |
| import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT; |
| import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; |
| import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; |
| import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; |
| import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; |
| import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; |
| import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; |
| import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; |
| import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; |
| import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; |
| import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; |
| import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; |
| import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; |
| import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; |
| import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; |
| import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; |
| import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; |
| import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; |
| import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; |
| import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; |
| import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; |
| import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; |
| import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; |
| import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; |
| import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; |
| import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; |
| import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; |
| import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; |
| import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; |
| import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; |
| import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; |
| import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; |
| import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; |
| import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; |
| import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; |
| import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; |
| import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; |
| import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; |
| import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; |
| import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; |
| import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; |
| import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; |
| import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; |
| import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; |
| import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; |
| import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; |
| import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_TOAST; |
| import static android.view.WindowManager.LayoutParams.TYPE_TRUSTED_APPLICATION_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; |
| import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; |
| import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; |
| import static android.view.WindowManagerGlobal.ADD_OKAY; |
| import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED; |
| import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM; |
| import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT; |
| import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT; |
| import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP; |
| import static android.view.WindowManagerPolicyConstants.ALT_BAR_UNKNOWN; |
| import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE; |
| import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; |
| import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; |
| import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; |
| |
| import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT; |
| import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; |
| import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER; |
| import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT; |
| import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE; |
| import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE; |
| import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW; |
| import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; |
| import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; |
| |
| import android.Manifest.permission; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.Px; |
| import android.app.ActivityManager; |
| import android.app.ActivityThread; |
| import android.app.LoadedApk; |
| import android.app.ResourcesManager; |
| import android.app.StatusBarManager; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.res.Resources; |
| import android.graphics.Insets; |
| import android.graphics.PixelFormat; |
| import android.graphics.Rect; |
| import android.graphics.Region; |
| import android.hardware.input.InputManager; |
| import android.hardware.power.V1_0.PowerHint; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.UserHandle; |
| import android.util.ArraySet; |
| import android.util.IntArray; |
| import android.util.Pair; |
| import android.util.PrintWriterPrinter; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.view.DisplayCutout; |
| import android.view.Gravity; |
| import android.view.InputChannel; |
| import android.view.InputDevice; |
| import android.view.InputEvent; |
| import android.view.InputEventReceiver; |
| import android.view.InsetsFlags; |
| import android.view.InsetsSource; |
| import android.view.InsetsState; |
| import android.view.InsetsState.InternalInsetsType; |
| import android.view.MotionEvent; |
| import android.view.PointerIcon; |
| import android.view.Surface; |
| import android.view.View; |
| import android.view.ViewRootImpl; |
| import android.view.WindowInsets.Side; |
| import android.view.WindowInsets.Side.InsetsSide; |
| import android.view.WindowInsets.Type; |
| import android.view.WindowInsets.Type.InsetsType; |
| import android.view.WindowInsetsController.Appearance; |
| import android.view.WindowManager; |
| import android.view.WindowManager.LayoutParams; |
| import android.view.WindowManagerGlobal; |
| import android.view.WindowManagerPolicyConstants; |
| import android.view.accessibility.AccessibilityManager; |
| |
| import com.android.internal.R; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.policy.GestureNavigationSettingsObserver; |
| import com.android.internal.policy.ScreenDecorationsUtils; |
| import com.android.internal.util.ScreenshotHelper; |
| import com.android.internal.util.function.TriConsumer; |
| import com.android.internal.view.AppearanceRegion; |
| import com.android.internal.widget.PointerLocationView; |
| import com.android.server.LocalServices; |
| import com.android.server.UiThread; |
| import com.android.server.policy.WindowManagerPolicy; |
| import com.android.server.policy.WindowManagerPolicy.InputConsumer; |
| import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition; |
| import com.android.server.policy.WindowManagerPolicy.ScreenOnListener; |
| import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs; |
| import com.android.server.policy.WindowOrientationListener; |
| import com.android.server.protolog.common.ProtoLog; |
| import com.android.server.statusbar.StatusBarManagerInternal; |
| import com.android.server.wallpaper.WallpaperManagerInternal; |
| import com.android.server.wm.utils.InsetUtils; |
| |
| import java.io.PrintWriter; |
| import java.util.function.Consumer; |
| |
| /** |
| * The policy that provides the basic behaviors and states of a display to show UI. |
| */ |
| public class DisplayPolicy { |
| private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM; |
| private static final boolean DEBUG = false; |
| |
| private static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false; |
| |
| // The panic gesture may become active only after the keyguard is dismissed and the immersive |
| // app shows again. If that doesn't happen for 30s we drop the gesture. |
| private static final long PANIC_GESTURE_EXPIRATION = 30000; |
| |
| // Controls navigation bar opacity depending on which workspace stacks are currently |
| // visible. |
| // Nav bar is always opaque when either the freeform stack or docked stack is visible. |
| private static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0; |
| // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque. |
| private static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1; |
| // Nav bar is never forced opaque. |
| private static final int NAV_BAR_FORCE_TRANSPARENT = 2; |
| |
| /** Don't apply window animation (see {@link #selectAnimation}). */ |
| static final int ANIMATION_NONE = -1; |
| /** Use the transit animation in style resource (see {@link #selectAnimation}). */ |
| static final int ANIMATION_STYLEABLE = 0; |
| |
| /** |
| * These are the system UI flags that, when changing, can cause the layout |
| * of the screen to change. |
| */ |
| private static final int SYSTEM_UI_CHANGING_LAYOUT = |
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_FULLSCREEN |
| | View.STATUS_BAR_TRANSLUCENT |
| | View.NAVIGATION_BAR_TRANSLUCENT |
| | View.STATUS_BAR_TRANSPARENT |
| | View.NAVIGATION_BAR_TRANSPARENT; |
| |
| private static final int[] SHOW_TYPES_FOR_SWIPE = |
| {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR, ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR}; |
| private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR}; |
| |
| private final WindowManagerService mService; |
| private final Context mContext; |
| private final Context mUiContext; |
| private final DisplayContent mDisplayContent; |
| private final Object mLock; |
| private final Handler mHandler; |
| |
| private Resources mCurrentUserResources; |
| |
| private final boolean mCarDockEnablesAccelerometer; |
| private final boolean mDeskDockEnablesAccelerometer; |
| private final AccessibilityManager mAccessibilityManager; |
| private final ImmersiveModeConfirmation mImmersiveModeConfirmation; |
| private final ScreenshotHelper mScreenshotHelper; |
| |
| private final Object mServiceAcquireLock = new Object(); |
| private StatusBarManagerInternal mStatusBarManagerInternal; |
| |
| @Px |
| private int mBottomGestureAdditionalInset; |
| @Px |
| private int mLeftGestureInset; |
| @Px |
| private int mRightGestureInset; |
| |
| private boolean mNavButtonForcedVisible; |
| |
| StatusBarManagerInternal getStatusBarManagerInternal() { |
| synchronized (mServiceAcquireLock) { |
| if (mStatusBarManagerInternal == null) { |
| mStatusBarManagerInternal = |
| LocalServices.getService(StatusBarManagerInternal.class); |
| } |
| return mStatusBarManagerInternal; |
| } |
| } |
| |
| private final SystemGesturesPointerEventListener mSystemGestures; |
| |
| private volatile int mLidState = LID_ABSENT; |
| private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED; |
| private volatile boolean mHdmiPlugged; |
| |
| private volatile boolean mHasStatusBar; |
| private volatile boolean mHasNavigationBar; |
| // Can the navigation bar ever move to the side? |
| private volatile boolean mNavigationBarCanMove; |
| private volatile boolean mNavigationBarLetsThroughTaps; |
| private volatile boolean mNavigationBarAlwaysShowOnSideGesture; |
| |
| // Written by vr manager thread, only read in this class. |
| private volatile boolean mPersistentVrModeEnabled; |
| |
| private volatile boolean mAwake; |
| private volatile boolean mScreenOnEarly; |
| private volatile boolean mScreenOnFully; |
| private volatile ScreenOnListener mScreenOnListener; |
| |
| private volatile boolean mKeyguardDrawComplete; |
| private volatile boolean mWindowManagerDrawComplete; |
| |
| private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>(); |
| private WindowState mStatusBar = null; |
| private WindowState mNotificationShade = null; |
| private final int[] mStatusBarHeightForRotation = new int[4]; |
| private WindowState mNavigationBar = null; |
| @NavigationBarPosition |
| private int mNavigationBarPosition = NAV_BAR_BOTTOM; |
| private int[] mNavigationBarHeightForRotationDefault = new int[4]; |
| private int[] mNavigationBarWidthForRotationDefault = new int[4]; |
| private int[] mNavigationBarHeightForRotationInCarMode = new int[4]; |
| private int[] mNavigationBarWidthForRotationInCarMode = new int[4]; |
| |
| // Alternative status bar for when flexible insets mapping is used to place the status bar on |
| // another side of the screen. |
| private WindowState mStatusBarAlt = null; |
| @WindowManagerPolicy.AltBarPosition |
| private int mStatusBarAltPosition = ALT_BAR_UNKNOWN; |
| // Alternative navigation bar for when flexible insets mapping is used to place the navigation |
| // bar elsewhere on the screen. |
| private WindowState mNavigationBarAlt = null; |
| @WindowManagerPolicy.AltBarPosition |
| private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN; |
| // Alternative climate bar for when flexible insets mapping is used to place a climate bar on |
| // the screen. |
| private WindowState mClimateBarAlt = null; |
| @WindowManagerPolicy.AltBarPosition |
| private int mClimateBarAltPosition = ALT_BAR_UNKNOWN; |
| // Alternative extra nav bar for when flexible insets mapping is used to place an extra nav bar |
| // on the screen. |
| private WindowState mExtraNavBarAlt = null; |
| @WindowManagerPolicy.AltBarPosition |
| private int mExtraNavBarAltPosition = ALT_BAR_UNKNOWN; |
| |
| /** See {@link #getNavigationBarFrameHeight} */ |
| private int[] mNavigationBarFrameHeightForRotationDefault = new int[4]; |
| |
| private boolean mIsFreeformWindowOverlappingWithNavBar; |
| |
| private boolean mLastImmersiveMode; |
| |
| private final StatusBarController mStatusBarController; |
| |
| private final BarController mNavigationBarController; |
| |
| private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener = |
| new BarController.OnBarVisibilityChangedListener() { |
| @Override |
| public void onBarVisibilityChanged(boolean visible) { |
| if (mAccessibilityManager == null) { |
| return; |
| } |
| mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible); |
| } |
| }; |
| |
| // The windows we were told about in focusChanged. |
| private WindowState mFocusedWindow; |
| private WindowState mLastFocusedWindow; |
| |
| // The states of decor windows from the last layout. These are used to generate another display |
| // layout in different bounds but with the same states. |
| private boolean mLastNavVisible; |
| private boolean mLastNavTranslucent; |
| private boolean mLastNavAllowedHidden; |
| private boolean mLastNotificationShadeForcesShowingNavigation; |
| |
| int mLastSystemUiFlags; |
| // Bits that we are in the process of clearing, so we want to prevent |
| // them from being set by applications until everything has been updated |
| // to have them clear. |
| private int mResettingSystemUiFlags = 0; |
| // Bits that we are currently always keeping cleared. |
| private int mForceClearedSystemUiFlags = 0; |
| private int mLastAppearance; |
| private int mLastFullscreenAppearance; |
| private int mLastDockedAppearance; |
| private int mLastBehavior; |
| private final Rect mNonDockedStackBounds = new Rect(); |
| private final Rect mDockedStackBounds = new Rect(); |
| private final Rect mLastNonDockedStackBounds = new Rect(); |
| private final Rect mLastDockedStackBounds = new Rect(); |
| |
| // What we last reported to system UI about whether the focused window is fullscreen/immersive. |
| private boolean mLastFocusIsFullscreen = false; |
| private boolean mLastFocusIsImmersive = false; |
| |
| // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending. |
| private long mPendingPanicGestureUptime; |
| |
| private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect(); |
| private static final Rect sTmpRect = new Rect(); |
| private static final Rect sTmpNavFrame = new Rect(); |
| private static final Rect sTmpLastParentFrame = new Rect(); |
| |
| private WindowState mTopFullscreenOpaqueWindowState; |
| private WindowState mTopFullscreenOpaqueOrDimmingWindowState; |
| private WindowState mTopDockedOpaqueWindowState; |
| private WindowState mTopDockedOpaqueOrDimmingWindowState; |
| private boolean mTopIsFullscreen; |
| private boolean mForceStatusBar; |
| private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED; |
| private boolean mForcingShowNavBar; |
| private int mForcingShowNavBarLayer; |
| private boolean mForceShowSystemBars; |
| |
| private boolean mShowingDream; |
| private boolean mLastShowingDream; |
| private boolean mDreamingLockscreen; |
| private boolean mAllowLockscreenWhenOn; |
| |
| @VisibleForTesting |
| InputConsumer mInputConsumer = null; |
| |
| private PointerLocationView mPointerLocationView; |
| |
| /** |
| * The area covered by system windows which belong to another display. Forwarded insets is set |
| * in case this is a virtual display, this is displayed on another display that has insets, and |
| * the bounds of this display is overlapping with the insets of the host display (e.g. IME is |
| * displayed on the host display, and it covers a part of this virtual display.) |
| * The forwarded insets is used to compute display frames of this virtual display, which will |
| * be then used to layout windows in the virtual display. |
| */ |
| @NonNull private Insets mForwardedInsets = Insets.NONE; |
| |
| private RefreshRatePolicy mRefreshRatePolicy; |
| |
| // -------- PolicyHandler -------- |
| private static final int MSG_REQUEST_TRANSIENT_BARS = 2; |
| private static final int MSG_DISPOSE_INPUT_CONSUMER = 3; |
| private static final int MSG_ENABLE_POINTER_LOCATION = 4; |
| private static final int MSG_DISABLE_POINTER_LOCATION = 5; |
| |
| private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0; |
| private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1; |
| |
| private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver; |
| |
| private class PolicyHandler extends Handler { |
| |
| PolicyHandler(Looper looper) { |
| super(looper); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_REQUEST_TRANSIENT_BARS: |
| synchronized (mLock) { |
| WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS) |
| ? getStatusBar() : getNavigationBar(); |
| if (targetBar != null) { |
| requestTransientBars(targetBar); |
| } |
| } |
| break; |
| case MSG_DISPOSE_INPUT_CONSUMER: |
| disposeInputConsumer((InputConsumer) msg.obj); |
| break; |
| case MSG_ENABLE_POINTER_LOCATION: |
| enablePointerLocation(); |
| break; |
| case MSG_DISABLE_POINTER_LOCATION: |
| disablePointerLocation(); |
| break; |
| } |
| } |
| } |
| |
| DisplayPolicy(WindowManagerService service, DisplayContent displayContent) { |
| mService = service; |
| mContext = displayContent.isDefaultDisplay ? service.mContext |
| : service.mContext.createDisplayContext(displayContent.getDisplay()); |
| mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext |
| : service.mAtmService.mSystemThread |
| .createSystemUiContext(displayContent.getDisplayId()); |
| mDisplayContent = displayContent; |
| mLock = service.getWindowManagerLock(); |
| |
| final int displayId = displayContent.getDisplayId(); |
| mStatusBarController = new StatusBarController(displayId); |
| mNavigationBarController = new BarController("NavigationBar", |
| displayId, |
| View.NAVIGATION_BAR_TRANSIENT, |
| View.NAVIGATION_BAR_UNHIDE, |
| View.NAVIGATION_BAR_TRANSLUCENT, |
| StatusBarManager.WINDOW_NAVIGATION_BAR, |
| TYPE_NAVIGATION_BAR, |
| FLAG_TRANSLUCENT_NAVIGATION, |
| View.NAVIGATION_BAR_TRANSPARENT); |
| |
| final Resources r = mContext.getResources(); |
| mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer); |
| mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer); |
| |
| mAccessibilityManager = (AccessibilityManager) mContext.getSystemService( |
| Context.ACCESSIBILITY_SERVICE); |
| if (!displayContent.isDefaultDisplay) { |
| mAwake = true; |
| mScreenOnEarly = true; |
| mScreenOnFully = true; |
| } |
| |
| final Looper looper = UiThread.getHandler().getLooper(); |
| mHandler = new PolicyHandler(looper); |
| mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler, |
| new SystemGesturesPointerEventListener.Callbacks() { |
| @Override |
| public void onSwipeFromTop() { |
| synchronized (mLock) { |
| if (mStatusBar != null) { |
| requestTransientBars(mStatusBar); |
| } |
| checkAltBarSwipeForTransientBars(ALT_BAR_TOP); |
| } |
| } |
| |
| @Override |
| public void onSwipeFromBottom() { |
| synchronized (mLock) { |
| if (mNavigationBar != null |
| && mNavigationBarPosition == NAV_BAR_BOTTOM) { |
| requestTransientBars(mNavigationBar); |
| } |
| checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM); |
| } |
| } |
| |
| @Override |
| public void onSwipeFromRight() { |
| final Region excludedRegion = Region.obtain(); |
| synchronized (mLock) { |
| mDisplayContent.calculateSystemGestureExclusion( |
| excludedRegion, null /* outUnrestricted */); |
| final boolean excluded = |
| mSystemGestures.currentGestureStartedInRegion(excludedRegion); |
| if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_RIGHT |
| || !excluded && mNavigationBarAlwaysShowOnSideGesture)) { |
| requestTransientBars(mNavigationBar); |
| } |
| checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT); |
| } |
| excludedRegion.recycle(); |
| } |
| |
| @Override |
| public void onSwipeFromLeft() { |
| final Region excludedRegion = Region.obtain(); |
| synchronized (mLock) { |
| mDisplayContent.calculateSystemGestureExclusion( |
| excludedRegion, null /* outUnrestricted */); |
| final boolean excluded = |
| mSystemGestures.currentGestureStartedInRegion(excludedRegion); |
| if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_LEFT |
| || !excluded && mNavigationBarAlwaysShowOnSideGesture)) { |
| requestTransientBars(mNavigationBar); |
| } |
| checkAltBarSwipeForTransientBars(ALT_BAR_LEFT); |
| } |
| excludedRegion.recycle(); |
| } |
| |
| @Override |
| public void onFling(int duration) { |
| if (mService.mPowerManagerInternal != null) { |
| mService.mPowerManagerInternal.powerHint( |
| PowerHint.INTERACTION, duration); |
| } |
| } |
| |
| @Override |
| public void onDebug() { |
| // no-op |
| } |
| |
| private WindowOrientationListener getOrientationListener() { |
| final DisplayRotation rotation = mDisplayContent.getDisplayRotation(); |
| return rotation != null ? rotation.getOrientationListener() : null; |
| } |
| |
| @Override |
| public void onDown() { |
| final WindowOrientationListener listener = getOrientationListener(); |
| if (listener != null) { |
| listener.onTouchStart(); |
| } |
| } |
| |
| @Override |
| public void onUpOrCancel() { |
| final WindowOrientationListener listener = getOrientationListener(); |
| if (listener != null) { |
| listener.onTouchEnd(); |
| } |
| } |
| |
| @Override |
| public void onMouseHoverAtTop() { |
| mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); |
| Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS); |
| msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS; |
| mHandler.sendMessageDelayed(msg, 500 /* delayMillis */); |
| } |
| |
| @Override |
| public void onMouseHoverAtBottom() { |
| mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); |
| Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS); |
| msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION; |
| mHandler.sendMessageDelayed(msg, 500 /* delayMillis */); |
| } |
| |
| @Override |
| public void onMouseLeaveFromEdge() { |
| mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS); |
| } |
| }); |
| displayContent.registerPointerEventListener(mSystemGestures); |
| displayContent.mAppTransition.registerListenerLocked( |
| mStatusBarController.getAppTransitionListener()); |
| mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper, |
| mService.mVrModeEnabled); |
| |
| // TODO: Make it can take screenshot on external display |
| mScreenshotHelper = displayContent.isDefaultDisplay |
| ? new ScreenshotHelper(mContext) : null; |
| |
| if (mDisplayContent.isDefaultDisplay) { |
| mHasStatusBar = true; |
| mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar); |
| |
| // Allow a system property to override this. Used by the emulator. |
| // See also hasNavigationBar(). |
| String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); |
| if ("1".equals(navBarOverride)) { |
| mHasNavigationBar = false; |
| } else if ("0".equals(navBarOverride)) { |
| mHasNavigationBar = true; |
| } |
| } else { |
| mHasStatusBar = false; |
| mHasNavigationBar = mDisplayContent.supportsSystemDecorations(); |
| } |
| |
| mRefreshRatePolicy = new RefreshRatePolicy(mService, |
| mDisplayContent.getDisplayInfo(), |
| mService.mHighRefreshRateBlacklist); |
| |
| mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler, |
| mContext, () -> { |
| synchronized (mLock) { |
| onConfigurationChanged(); |
| mSystemGestures.onConfigurationChanged(); |
| mDisplayContent.updateSystemGestureExclusion(); |
| } |
| }); |
| mHandler.post(mGestureNavigationSettingsObserver::register); |
| } |
| |
| private void checkAltBarSwipeForTransientBars(@WindowManagerPolicy.AltBarPosition int pos) { |
| if (mStatusBarAlt != null && mStatusBarAltPosition == pos) { |
| requestTransientBars(mStatusBarAlt); |
| } |
| if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) { |
| requestTransientBars(mNavigationBarAlt); |
| } |
| if (mClimateBarAlt != null && mClimateBarAltPosition == pos) { |
| requestTransientBars(mClimateBarAlt); |
| } |
| if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) { |
| requestTransientBars(mExtraNavBarAlt); |
| } |
| } |
| |
| void systemReady() { |
| mSystemGestures.systemReady(); |
| if (mService.mPointerLocationEnabled) { |
| setPointerLocationEnabled(true); |
| } |
| } |
| |
| private int getDisplayId() { |
| return mDisplayContent.getDisplayId(); |
| } |
| |
| public void setHdmiPlugged(boolean plugged) { |
| setHdmiPlugged(plugged, false /* force */); |
| } |
| |
| public void setHdmiPlugged(boolean plugged, boolean force) { |
| if (force || mHdmiPlugged != plugged) { |
| mHdmiPlugged = plugged; |
| mService.updateRotation(true /* alwaysSendConfiguration */, true /* forceRelayout */); |
| final Intent intent = new Intent(ACTION_HDMI_PLUGGED); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); |
| intent.putExtra(EXTRA_HDMI_PLUGGED_STATE, plugged); |
| mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| } |
| } |
| |
| boolean isHdmiPlugged() { |
| return mHdmiPlugged; |
| } |
| |
| boolean isCarDockEnablesAccelerometer() { |
| return mCarDockEnablesAccelerometer; |
| } |
| |
| boolean isDeskDockEnablesAccelerometer() { |
| return mDeskDockEnablesAccelerometer; |
| } |
| |
| public void setPersistentVrModeEnabled(boolean persistentVrModeEnabled) { |
| mPersistentVrModeEnabled = persistentVrModeEnabled; |
| } |
| |
| public boolean isPersistentVrModeEnabled() { |
| return mPersistentVrModeEnabled; |
| } |
| |
| public void setDockMode(int dockMode) { |
| mDockMode = dockMode; |
| } |
| |
| public int getDockMode() { |
| return mDockMode; |
| } |
| |
| public boolean hasNavigationBar() { |
| return mHasNavigationBar; |
| } |
| |
| public boolean hasStatusBar() { |
| return mHasStatusBar; |
| } |
| |
| boolean hasSideGestures() { |
| return mHasNavigationBar && (mLeftGestureInset > 0 || mRightGestureInset > 0); |
| } |
| |
| public boolean navigationBarCanMove() { |
| return mNavigationBarCanMove; |
| } |
| |
| public void setLidState(int lidState) { |
| mLidState = lidState; |
| } |
| |
| public int getLidState() { |
| return mLidState; |
| } |
| |
| public void setAwake(boolean awake) { |
| mAwake = awake; |
| } |
| |
| public boolean isAwake() { |
| return mAwake; |
| } |
| |
| public boolean isScreenOnEarly() { |
| return mScreenOnEarly; |
| } |
| |
| public boolean isScreenOnFully() { |
| return mScreenOnFully; |
| } |
| |
| public boolean isKeyguardDrawComplete() { |
| return mKeyguardDrawComplete; |
| } |
| |
| public boolean isWindowManagerDrawComplete() { |
| return mWindowManagerDrawComplete; |
| } |
| |
| public ScreenOnListener getScreenOnListener() { |
| return mScreenOnListener; |
| } |
| |
| public void screenTurnedOn(ScreenOnListener screenOnListener) { |
| synchronized (mLock) { |
| mScreenOnEarly = true; |
| mScreenOnFully = false; |
| mKeyguardDrawComplete = false; |
| mWindowManagerDrawComplete = false; |
| mScreenOnListener = screenOnListener; |
| } |
| } |
| |
| public void screenTurnedOff() { |
| synchronized (mLock) { |
| mScreenOnEarly = false; |
| mScreenOnFully = false; |
| mKeyguardDrawComplete = false; |
| mWindowManagerDrawComplete = false; |
| mScreenOnListener = null; |
| } |
| } |
| |
| /** Return false if we are not awake yet or we have already informed of this event. */ |
| public boolean finishKeyguardDrawn() { |
| synchronized (mLock) { |
| if (!mScreenOnEarly || mKeyguardDrawComplete) { |
| return false; |
| } |
| |
| mKeyguardDrawComplete = true; |
| mWindowManagerDrawComplete = false; |
| } |
| return true; |
| } |
| |
| /** Return false if screen is not turned on or we did already handle this case earlier. */ |
| public boolean finishWindowsDrawn() { |
| synchronized (mLock) { |
| if (!mScreenOnEarly || mWindowManagerDrawComplete) { |
| return false; |
| } |
| |
| mWindowManagerDrawComplete = true; |
| } |
| return true; |
| } |
| |
| /** Return false if it is not ready to turn on. */ |
| public boolean finishScreenTurningOn() { |
| synchronized (mLock) { |
| ProtoLog.d(WM_DEBUG_SCREEN_ON, |
| "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, " |
| + "mScreenOnFully=%b, mKeyguardDrawComplete=%b, " |
| + "mWindowManagerDrawComplete=%b", |
| mAwake, mScreenOnEarly, mScreenOnFully, mKeyguardDrawComplete, |
| mWindowManagerDrawComplete); |
| |
| if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete |
| || (mAwake && !mKeyguardDrawComplete)) { |
| return false; |
| } |
| |
| ProtoLog.i(WM_DEBUG_SCREEN_ON, "Finished screen turning on..."); |
| mScreenOnListener = null; |
| mScreenOnFully = true; |
| } |
| return true; |
| } |
| |
| private boolean hasStatusBarServicePermission(int pid, int uid) { |
| return mContext.checkPermission(permission.STATUS_BAR_SERVICE, pid, uid) |
| == PackageManager.PERMISSION_GRANTED; |
| } |
| |
| /** |
| * Sanitize the layout parameters coming from a client. Allows the policy |
| * to do things like ensure that windows of a specific type can't take |
| * input focus. |
| * |
| * @param attrs The window layout parameters to be modified. These values |
| * are modified in-place. |
| */ |
| public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, |
| int callingPid, int callingUid) { |
| |
| final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; |
| if (mScreenDecorWindows.contains(win)) { |
| if (!isScreenDecor) { |
| // No longer has the flag set, so remove from the set. |
| mScreenDecorWindows.remove(win); |
| } |
| } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) { |
| mScreenDecorWindows.add(win); |
| } |
| |
| switch (attrs.type) { |
| case TYPE_SYSTEM_OVERLAY: |
| case TYPE_SECURE_SYSTEM_OVERLAY: |
| // These types of windows can't receive input events. |
| attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; |
| attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; |
| break; |
| case TYPE_WALLPAPER: |
| // Dreams and wallpapers don't have an app window token and can thus not be |
| // letterboxed. Hence always let them extend under the cutout. |
| attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| break; |
| case TYPE_NOTIFICATION_SHADE: |
| // If the Keyguard is in a hidden state (occluded by another window), we force to |
| // remove the wallpaper and keyguard flag so that any change in-flight after setting |
| // the keyguard as occluded wouldn't set these flags again. |
| // See {@link #processKeyguardSetHiddenResultLw}. |
| if (mService.mPolicy.isKeyguardOccluded()) { |
| attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; |
| } |
| break; |
| |
| case TYPE_TOAST: |
| // While apps should use the dedicated toast APIs to add such windows |
| // it possible legacy apps to add the window directly. Therefore, we |
| // make windows added directly by the app behave as a toast as much |
| // as possible in terms of timeout and animation. |
| if (attrs.hideTimeoutMilliseconds < 0 |
| || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) { |
| attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT; |
| } |
| // Accessibility users may need longer timeout duration. This api compares |
| // original timeout with user's preference and return longer one. It returns |
| // original timeout if there's no preference. |
| attrs.hideTimeoutMilliseconds = mAccessibilityManager.getRecommendedTimeoutMillis( |
| (int) attrs.hideTimeoutMilliseconds, |
| AccessibilityManager.FLAG_CONTENT_TEXT); |
| attrs.windowAnimations = com.android.internal.R.style.Animation_Toast; |
| // Toast can show with below conditions when the screen is locked. |
| if (canToastShowWhenLocked(callingPid)) { |
| attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; |
| } |
| // Toasts can't be clickable |
| attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; |
| break; |
| |
| case TYPE_BASE_APPLICATION: |
| |
| // A non-translucent main app window isn't allowed to fit insets, as it would create |
| // a hole on the display! |
| if (attrs.isFullscreen() && win.mActivityRecord != null |
| && win.mActivityRecord.fillsParent() |
| && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 |
| && attrs.getFitInsetsTypes() != 0) { |
| throw new RuntimeException("Illegal attributes: Main activity window that isn't" |
| + " translucent trying to fit insets: " |
| + attrs.getFitInsetsTypes() |
| + " attrs=" + attrs); |
| } |
| break; |
| } |
| |
| // Check if alternate bars positions were updated. |
| if (mStatusBarAlt == win) { |
| mStatusBarAltPosition = getAltBarPosition(attrs); |
| } |
| if (mNavigationBarAlt == win) { |
| mNavigationBarAltPosition = getAltBarPosition(attrs); |
| } |
| if (mClimateBarAlt == win) { |
| mClimateBarAltPosition = getAltBarPosition(attrs); |
| } |
| if (mExtraNavBarAlt == win) { |
| mExtraNavBarAltPosition = getAltBarPosition(attrs); |
| } |
| } |
| |
| /** |
| * @return {@code true} if the calling activity initiate toast and is visible with |
| * {@link WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} flag. |
| */ |
| boolean canToastShowWhenLocked(int callingPid) { |
| return mDisplayContent.forAllWindows(w -> { |
| return callingPid == w.mSession.mPid && w.isVisible() && w.canShowWhenLocked(); |
| }, true /* traverseTopToBottom */); |
| } |
| |
| /** |
| * Check if a window can be added to the system. |
| * |
| * Currently enforces that two window types are singletons per display: |
| * <ul> |
| * <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li> |
| * <li>{@link WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}</li> |
| * <li>{@link WindowManager.LayoutParams#TYPE_NAVIGATION_BAR}</li> |
| * </ul> |
| * |
| * @param attrs Information about the window to be added. |
| * |
| * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons, |
| * WindowManagerImpl.ADD_MULTIPLE_SINGLETON |
| */ |
| int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) { |
| if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { |
| mContext.enforcePermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, |
| "DisplayPolicy"); |
| } |
| |
| switch (attrs.type) { |
| case TYPE_STATUS_BAR: |
| mContext.enforcePermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, |
| "DisplayPolicy"); |
| if ((mStatusBar != null && mStatusBar.isAlive()) |
| || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| break; |
| case TYPE_NOTIFICATION_SHADE: |
| mContext.enforcePermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, |
| "DisplayPolicy"); |
| if (mNotificationShade != null) { |
| if (mNotificationShade.isAlive()) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| } |
| break; |
| case TYPE_NAVIGATION_BAR: |
| mContext.enforcePermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, |
| "DisplayPolicy"); |
| if ((mNavigationBar != null && mNavigationBar.isAlive()) |
| || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| break; |
| case TYPE_NAVIGATION_BAR_PANEL: |
| // Check for permission if the caller is not the recents component. |
| if (!mService.mAtmInternal.isCallerRecents(callingUid)) { |
| mContext.enforcePermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, |
| "DisplayPolicy"); |
| } |
| break; |
| case TYPE_STATUS_BAR_ADDITIONAL: |
| case TYPE_STATUS_BAR_SUB_PANEL: |
| case TYPE_VOICE_INTERACTION_STARTING: |
| mContext.enforcePermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, |
| "DisplayPolicy"); |
| break; |
| case TYPE_TRUSTED_APPLICATION_OVERLAY: |
| mContext.enforcePermission( |
| android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid, |
| "DisplayPolicy"); |
| break; |
| case TYPE_STATUS_BAR_PANEL: |
| return WindowManagerGlobal.ADD_INVALID_TYPE; |
| } |
| |
| if (attrs.providesInsetsTypes != null) { |
| mContext.enforcePermission( |
| android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, |
| "DisplayPolicy"); |
| enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes); |
| |
| for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) { |
| switch (insetType) { |
| case ITYPE_STATUS_BAR: |
| if ((mStatusBar != null && mStatusBar.isAlive()) |
| || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| break; |
| case ITYPE_NAVIGATION_BAR: |
| if ((mNavigationBar != null && mNavigationBar.isAlive()) |
| || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| break; |
| case ITYPE_CLIMATE_BAR: |
| if (mClimateBarAlt != null && mClimateBarAlt.isAlive()) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| break; |
| case ITYPE_EXTRA_NAVIGATION_BAR: |
| if (mExtraNavBarAlt != null && mExtraNavBarAlt.isAlive()) { |
| return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; |
| } |
| break; |
| } |
| } |
| } |
| return ADD_OKAY; |
| } |
| |
| /** |
| * Called when a window is being added to the system. Must not throw an exception. |
| * |
| * @param win The window being added. |
| * @param attrs Information about the window to be added. |
| */ |
| void addWindowLw(WindowState win, WindowManager.LayoutParams attrs) { |
| if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { |
| mScreenDecorWindows.add(win); |
| } |
| |
| switch (attrs.type) { |
| case TYPE_NOTIFICATION_SHADE: |
| mNotificationShade = win; |
| if (mDisplayContent.isDefaultDisplay) { |
| mService.mPolicy.setKeyguardCandidateLw(win); |
| } |
| break; |
| case TYPE_STATUS_BAR: |
| mStatusBar = win; |
| mStatusBarController.setWindow(win); |
| final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider = |
| (displayFrames, windowState, rect) -> { |
| rect.top = 0; |
| rect.bottom = getStatusBarHeight(displayFrames); |
| }; |
| mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider); |
| mDisplayContent.setInsetProvider(ITYPE_TOP_GESTURES, win, frameProvider); |
| mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider); |
| break; |
| case TYPE_NAVIGATION_BAR: |
| mNavigationBar = win; |
| mNavigationBarController.setWindow(win); |
| mNavigationBarController.setOnBarVisibilityChangedListener( |
| mNavBarVisibilityListener, true); |
| mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, |
| (displayFrames, windowState, inOutFrame) -> { |
| |
| // In Gesture Nav, navigation bar frame is larger than frame to |
| // calculate inset. |
| if (navigationBarPosition(displayFrames.mDisplayWidth, |
| displayFrames.mDisplayHeight, |
| displayFrames.mRotation) == NAV_BAR_BOTTOM |
| && !mNavButtonForcedVisible) { |
| |
| sTmpRect.set(displayFrames.mUnrestricted); |
| sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe); |
| inOutFrame.top = sTmpRect.bottom |
| - getNavigationBarHeight(displayFrames.mRotation, |
| mDisplayContent.getConfiguration().uiMode); |
| } |
| }, |
| |
| // For IME we use regular frame. |
| (displayFrames, windowState, inOutFrame) -> |
| inOutFrame.set(windowState.getFrameLw())); |
| |
| mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win, |
| (displayFrames, windowState, inOutFrame) -> { |
| inOutFrame.top -= mBottomGestureAdditionalInset; |
| }); |
| mDisplayContent.setInsetProvider(ITYPE_LEFT_GESTURES, win, |
| (displayFrames, windowState, inOutFrame) -> { |
| inOutFrame.left = 0; |
| inOutFrame.top = 0; |
| inOutFrame.bottom = displayFrames.mDisplayHeight; |
| inOutFrame.right = displayFrames.mUnrestricted.left + mLeftGestureInset; |
| }); |
| mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win, |
| (displayFrames, windowState, inOutFrame) -> { |
| inOutFrame.left = displayFrames.mUnrestricted.right |
| - mRightGestureInset; |
| inOutFrame.top = 0; |
| inOutFrame.bottom = displayFrames.mDisplayHeight; |
| inOutFrame.right = displayFrames.mDisplayWidth; |
| }); |
| mDisplayContent.setInsetProvider(ITYPE_BOTTOM_TAPPABLE_ELEMENT, win, |
| (displayFrames, windowState, inOutFrame) -> { |
| if ((windowState.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0 |
| || mNavigationBarLetsThroughTaps) { |
| inOutFrame.setEmpty(); |
| } |
| }); |
| if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); |
| break; |
| default: |
| if (attrs.providesInsetsTypes != null) { |
| for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { |
| switch (insetsType) { |
| case ITYPE_STATUS_BAR: |
| mStatusBarAlt = win; |
| mStatusBarController.setWindow(mStatusBarAlt); |
| mStatusBarAltPosition = getAltBarPosition(attrs); |
| break; |
| case ITYPE_NAVIGATION_BAR: |
| mNavigationBarAlt = win; |
| mNavigationBarController.setWindow(mNavigationBarAlt); |
| mNavigationBarAltPosition = getAltBarPosition(attrs); |
| break; |
| case ITYPE_CLIMATE_BAR: |
| mClimateBarAlt = win; |
| mClimateBarAltPosition = getAltBarPosition(attrs); |
| break; |
| case ITYPE_EXTRA_NAVIGATION_BAR: |
| mExtraNavBarAlt = win; |
| mExtraNavBarAltPosition = getAltBarPosition(attrs); |
| break; |
| } |
| mDisplayContent.setInsetProvider(insetsType, win, null); |
| } |
| } |
| break; |
| } |
| } |
| |
| @WindowManagerPolicy.AltBarPosition |
| private int getAltBarPosition(WindowManager.LayoutParams params) { |
| switch (params.gravity) { |
| case Gravity.LEFT: |
| return ALT_BAR_LEFT; |
| case Gravity.RIGHT: |
| return ALT_BAR_RIGHT; |
| case Gravity.BOTTOM: |
| return ALT_BAR_BOTTOM; |
| case Gravity.TOP: |
| return ALT_BAR_TOP; |
| default: |
| return ALT_BAR_UNKNOWN; |
| } |
| } |
| |
| TriConsumer<DisplayFrames, WindowState, Rect> getImeSourceFrameProvider() { |
| return (displayFrames, windowState, inOutFrame) -> { |
| if (mNavigationBar != null && navigationBarPosition(displayFrames.mDisplayWidth, |
| displayFrames.mDisplayHeight, |
| displayFrames.mRotation) == NAV_BAR_BOTTOM) { |
| // In gesture navigation, nav bar frame is larger than frame to calculate insets. |
| // IME should not provide frame which is smaller than the nav bar frame. Otherwise, |
| // nav bar might be overlapped with the content of the client when IME is shown. |
| sTmpRect.set(inOutFrame); |
| sTmpRect.intersectUnchecked(mNavigationBar.getFrameLw()); |
| inOutFrame.inset(windowState.getGivenContentInsetsLw()); |
| inOutFrame.union(sTmpRect); |
| } else { |
| inOutFrame.inset(windowState.getGivenContentInsetsLw()); |
| } |
| }; |
| } |
| |
| private static void enforceSingleInsetsTypeCorrespondingToWindowType(int[] insetsTypes) { |
| int count = 0; |
| for (int insetsType : insetsTypes) { |
| switch (insetsType) { |
| case ITYPE_NAVIGATION_BAR: |
| case ITYPE_STATUS_BAR: |
| case ITYPE_CLIMATE_BAR: |
| case ITYPE_EXTRA_NAVIGATION_BAR: |
| case ITYPE_CAPTION_BAR: |
| if (++count > 1) { |
| throw new IllegalArgumentException( |
| "Multiple InsetsTypes corresponding to Window type"); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Called when a window is being removed from a window manager. Must not |
| * throw an exception -- clean up as much as possible. |
| * |
| * @param win The window being removed. |
| */ |
| void removeWindowLw(WindowState win) { |
| if (mStatusBar == win || mStatusBarAlt == win) { |
| mStatusBar = null; |
| mStatusBarAlt = null; |
| mStatusBarController.setWindow(null); |
| mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null); |
| } else if (mNavigationBar == win || mNavigationBarAlt == win) { |
| mNavigationBar = null; |
| mNavigationBarAlt = null; |
| mNavigationBarController.setWindow(null); |
| mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null); |
| } else if (mNotificationShade == win) { |
| mNotificationShade = null; |
| if (mDisplayContent.isDefaultDisplay) { |
| mService.mPolicy.setKeyguardCandidateLw(null); |
| } |
| } else if (mClimateBarAlt == win) { |
| mClimateBarAlt = null; |
| mDisplayContent.setInsetProvider(ITYPE_CLIMATE_BAR, null, null); |
| } else if (mExtraNavBarAlt == win) { |
| mExtraNavBarAlt = null; |
| mDisplayContent.setInsetProvider(ITYPE_EXTRA_NAVIGATION_BAR, null, null); |
| } |
| if (mLastFocusedWindow == win) { |
| mLastFocusedWindow = null; |
| } |
| mScreenDecorWindows.remove(win); |
| } |
| |
| private int getStatusBarHeight(DisplayFrames displayFrames) { |
| return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation], |
| displayFrames.mDisplayCutoutSafe.top); |
| } |
| |
| @VisibleForTesting |
| StatusBarController getStatusBarController() { |
| return mStatusBarController; |
| } |
| |
| WindowState getStatusBar() { |
| return mStatusBar != null ? mStatusBar : mStatusBarAlt; |
| } |
| |
| WindowState getNotificationShade() { |
| return mNotificationShade; |
| } |
| |
| WindowState getNavigationBar() { |
| return mNavigationBar != null ? mNavigationBar : mNavigationBarAlt; |
| } |
| |
| /** |
| * Control the animation to run when a window's state changes. Return a positive number to |
| * force the animation to a specific resource ID, {@link #ANIMATION_STYLEABLE} to use the |
| * style resource defining the animation, or {@link #ANIMATION_NONE} for no animation. |
| * |
| * @param win The window that is changing. |
| * @param transit What is happening to the window: |
| * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_ENTER}, |
| * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_EXIT}, |
| * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or |
| * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}. |
| * |
| * @return Resource ID of the actual animation to use, or {@link #ANIMATION_NONE} for none. |
| */ |
| int selectAnimation(WindowState win, int transit) { |
| if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win |
| + ": transit=" + transit); |
| if (win == mStatusBar) { |
| if (transit == TRANSIT_EXIT |
| || transit == TRANSIT_HIDE) { |
| return R.anim.dock_top_exit; |
| } else if (transit == TRANSIT_ENTER |
| || transit == TRANSIT_SHOW) { |
| return R.anim.dock_top_enter; |
| } |
| } else if (win == mNavigationBar) { |
| if (win.getAttrs().windowAnimations != 0) { |
| return ANIMATION_STYLEABLE; |
| } |
| // This can be on either the bottom or the right or the left. |
| if (mNavigationBarPosition == NAV_BAR_BOTTOM) { |
| if (transit == TRANSIT_EXIT |
| || transit == TRANSIT_HIDE) { |
| if (mService.mPolicy.isKeyguardShowingAndNotOccluded()) { |
| return R.anim.dock_bottom_exit_keyguard; |
| } else { |
| return R.anim.dock_bottom_exit; |
| } |
| } else if (transit == TRANSIT_ENTER |
| || transit == TRANSIT_SHOW) { |
| return R.anim.dock_bottom_enter; |
| } |
| } else if (mNavigationBarPosition == NAV_BAR_RIGHT) { |
| if (transit == TRANSIT_EXIT |
| || transit == TRANSIT_HIDE) { |
| return R.anim.dock_right_exit; |
| } else if (transit == TRANSIT_ENTER |
| || transit == TRANSIT_SHOW) { |
| return R.anim.dock_right_enter; |
| } |
| } else if (mNavigationBarPosition == NAV_BAR_LEFT) { |
| if (transit == TRANSIT_EXIT |
| || transit == TRANSIT_HIDE) { |
| return R.anim.dock_left_exit; |
| } else if (transit == TRANSIT_ENTER |
| || transit == TRANSIT_SHOW) { |
| return R.anim.dock_left_enter; |
| } |
| } |
| } else if (win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt |
| || win == mExtraNavBarAlt) { |
| if (win.getAttrs().windowAnimations != 0) { |
| return ANIMATION_STYLEABLE; |
| } |
| |
| int pos = (win == mStatusBarAlt) ? mStatusBarAltPosition : mNavigationBarAltPosition; |
| |
| boolean isExitOrHide = transit == TRANSIT_EXIT || transit == TRANSIT_HIDE; |
| boolean isEnterOrShow = transit == TRANSIT_ENTER || transit == TRANSIT_SHOW; |
| |
| switch (pos) { |
| case ALT_BAR_LEFT: |
| if (isExitOrHide) { |
| return R.anim.dock_left_exit; |
| } else if (isEnterOrShow) { |
| return R.anim.dock_left_enter; |
| } |
| break; |
| case ALT_BAR_RIGHT: |
| if (isExitOrHide) { |
| return R.anim.dock_right_exit; |
| } else if (isEnterOrShow) { |
| return R.anim.dock_right_enter; |
| } |
| break; |
| case ALT_BAR_BOTTOM: |
| if (isExitOrHide) { |
| return R.anim.dock_bottom_exit; |
| } else if (isEnterOrShow) { |
| return R.anim.dock_bottom_enter; |
| } |
| break; |
| case ALT_BAR_TOP: |
| if (isExitOrHide) { |
| return R.anim.dock_top_exit; |
| } else if (isEnterOrShow) { |
| return R.anim.dock_top_enter; |
| } |
| break; |
| } |
| } |
| |
| if (transit == TRANSIT_PREVIEW_DONE) { |
| if (win.hasAppShownWindows()) { |
| if (win.isActivityTypeHome()) { |
| // Dismiss the starting window as soon as possible to avoid the crossfade out |
| // with old content because home is easier to have different UI states. |
| return ANIMATION_NONE; |
| } |
| if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT"); |
| return R.anim.app_starting_exit; |
| } |
| } |
| |
| return ANIMATION_STYLEABLE; |
| } |
| |
| /** |
| * Called when a new system UI visibility is being reported, allowing |
| * the policy to adjust what is actually reported. |
| * @param visibility The raw visibility reported by the status bar. |
| * @return The new desired visibility. |
| */ |
| public int adjustSystemUiVisibilityLw(int visibility) { |
| mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); |
| mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); |
| |
| // Reset any bits in mForceClearingStatusBarVisibility that |
| // are now clear. |
| mResettingSystemUiFlags &= visibility; |
| // Clear any bits in the new visibility that are currently being |
| // force cleared, before reporting it. |
| return visibility & ~mResettingSystemUiFlags |
| & ~mForceClearedSystemUiFlags; |
| } |
| |
| /** |
| * @return true if the system bars are forced to stay visible |
| */ |
| public boolean areSystemBarsForcedShownLw(WindowState windowState) { |
| return mForceShowSystemBars; |
| } |
| |
| // TODO: Should probably be moved into DisplayFrames. |
| /** |
| * Return the layout hints for a newly added window. These values are computed on the |
| * most recent layout, so they are not guaranteed to be correct. |
| * |
| * @param attrs The LayoutParams of the window. |
| * @param windowToken The token of the window. |
| * @param outFrame The frame of the window. |
| * @param outContentInsets The areas covered by system windows, expressed as positive insets. |
| * @param outStableInsets The areas covered by stable system windows irrespective of their |
| * current visibility. Expressed as positive insets. |
| * @param outDisplayCutout The area that has been cut away from the display. |
| * @return Whether to always consume the system bars. |
| * See {@link #areSystemBarsForcedShownLw(WindowState)}. |
| */ |
| boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame, |
| Rect outContentInsets, Rect outStableInsets, |
| DisplayCutout.ParcelableWrapper outDisplayCutout) { |
| final int fl = PolicyControl.getWindowFlags(null, attrs); |
| final int pfl = attrs.privateFlags; |
| final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs); |
| final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs); |
| |
| final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0; |
| final boolean layoutInScreenAndInsetDecor = layoutInScreen |
| && (fl & FLAG_LAYOUT_INSET_DECOR) != 0; |
| final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; |
| |
| final boolean isFixedRotationTransforming = |
| windowToken != null && windowToken.isFixedRotationTransforming(); |
| final ActivityRecord activity = windowToken != null ? windowToken.asActivityRecord() : null; |
| final Task task = activity != null ? activity.getTask() : null; |
| final Rect taskBounds = isFixedRotationTransforming |
| // Use token (activity) bounds if it is rotated because its task is not rotated. |
| ? windowToken.getBounds() |
| : (task != null ? task.getBounds() : null); |
| final DisplayFrames displayFrames = isFixedRotationTransforming |
| ? windowToken.getFixedRotationTransformDisplayFrames() |
| : mDisplayContent.mDisplayFrames; |
| |
| if (layoutInScreenAndInsetDecor && !screenDecor) { |
| if ((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 |
| || (attrs.getFitInsetsTypes() & Type.navigationBars()) == 0) { |
| outFrame.set(displayFrames.mUnrestricted); |
| } else { |
| outFrame.set(displayFrames.mRestricted); |
| } |
| |
| final boolean isFloatingTask = task != null && task.isFloating(); |
| final Rect sf = isFloatingTask ? null : displayFrames.mStable; |
| final Rect cf; |
| if (isFloatingTask) { |
| cf = null; |
| } else if ((sysUiVis & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { |
| if ((fl & FLAG_FULLSCREEN) != 0) { |
| cf = displayFrames.mStableFullscreen; |
| } else { |
| cf = displayFrames.mStable; |
| } |
| } else if ((fl & FLAG_FULLSCREEN) != 0) { |
| cf = displayFrames.mUnrestricted; |
| } else { |
| cf = displayFrames.mCurrent; |
| } |
| |
| if (taskBounds != null) { |
| outFrame.intersect(taskBounds); |
| } |
| InsetUtils.insetsBetweenFrames(outFrame, cf, outContentInsets); |
| InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets); |
| outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame) |
| .getDisplayCutout()); |
| return mForceShowSystemBars; |
| } else { |
| if (layoutInScreen) { |
| outFrame.set(displayFrames.mUnrestricted); |
| } else { |
| outFrame.set(displayFrames.mStable); |
| } |
| if (taskBounds != null) { |
| outFrame.intersect(taskBounds); |
| } |
| |
| outContentInsets.setEmpty(); |
| outStableInsets.setEmpty(); |
| outDisplayCutout.set(DisplayCutout.NO_CUTOUT); |
| return mForceShowSystemBars; |
| } |
| } |
| |
| // TODO(b/118118435): remove after migration |
| private static int getImpliedSysUiFlagsForLayout(LayoutParams attrs) { |
| int impliedFlags = 0; |
| final boolean forceWindowDrawsBarBackgrounds = |
| (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 |
| && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT; |
| if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 |
| || forceWindowDrawsBarBackgrounds) { |
| impliedFlags |= SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; |
| impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; |
| } |
| return impliedFlags; |
| } |
| |
| private final Runnable mClearHideNavigationFlag = new Runnable() { |
| @Override |
| public void run() { |
| synchronized (mLock) { |
| mForceClearedSystemUiFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; |
| mDisplayContent.reevaluateStatusBarVisibility(); |
| } |
| } |
| }; |
| |
| /** |
| * Input handler used while nav bar is hidden. Captures any touch on the screen, |
| * to determine when the nav bar should be shown and prevent applications from |
| * receiving those touches. |
| */ |
| private final class HideNavInputEventReceiver extends InputEventReceiver { |
| HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) { |
| super(inputChannel, looper); |
| } |
| |
| @Override |
| public void onInputEvent(InputEvent event) { |
| try { |
| if (event instanceof MotionEvent |
| && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { |
| final MotionEvent motionEvent = (MotionEvent) event; |
| if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { |
| // When the user taps down, we re-show the nav bar. |
| boolean changed = false; |
| synchronized (mLock) { |
| if (mInputConsumer == null) { |
| return; |
| } |
| showSystemBars(); |
| // Any user activity always causes us to show the |
| // navigation controls, if they had been hidden. |
| // We also clear the low profile and only content |
| // flags so that tapping on the screen will atomically |
| // restore all currently hidden screen decorations. |
| int newVal = mResettingSystemUiFlags |
| | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_LOW_PROFILE |
| | View.SYSTEM_UI_FLAG_FULLSCREEN; |
| if (mResettingSystemUiFlags != newVal) { |
| mResettingSystemUiFlags = newVal; |
| changed = true; |
| } |
| // We don't allow the system's nav bar to be hidden |
| // again for 1 second, to prevent applications from |
| // spamming us and keeping it from being shown. |
| newVal = mForceClearedSystemUiFlags |
| | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; |
| if (mForceClearedSystemUiFlags != newVal) { |
| mForceClearedSystemUiFlags = newVal; |
| changed = true; |
| mHandler.postDelayed(mClearHideNavigationFlag, 1000); |
| } |
| if (changed) { |
| mDisplayContent.reevaluateStatusBarVisibility(); |
| } |
| } |
| } |
| } |
| } finally { |
| finishInputEvent(event, false /* handled */); |
| } |
| } |
| |
| private void showSystemBars() { |
| final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() |
| .peekSourceProvider(ITYPE_NAVIGATION_BAR); |
| final InsetsControlTarget target = |
| provider != null ? provider.getControlTarget() : null; |
| if (target != null) { |
| target.showInsets(Type.systemBars(), false /* fromIme */); |
| } |
| } |
| } |
| |
| private void simulateLayoutDecorWindow(WindowState win, DisplayFrames displayFrames, |
| InsetsState insetsState, WindowFrames simulatedWindowFrames, |
| SparseArray<Rect> contentFrames, Consumer<Rect> layout) { |
| win.setSimulatedWindowFrames(simulatedWindowFrames); |
| final Rect contentFrame = new Rect(); |
| try { |
| layout.accept(contentFrame); |
| } finally { |
| win.setSimulatedWindowFrames(null); |
| } |
| contentFrames.put(win.mAttrs.type, contentFrame); |
| mDisplayContent.getInsetsStateController().computeSimulatedState(insetsState, win, |
| displayFrames, simulatedWindowFrames); |
| } |
| |
| /** |
| * Computes the frames of display (its logical size, rotation and cutout should already be set) |
| * used to layout window. The result of display frames and insets state should be the same as |
| * using {@link #beginLayoutLw}, but this method only changes the given display frames, insets |
| * state and some temporal states. In other words, it doesn't change the window frames used to |
| * show on screen. |
| */ |
| void simulateLayoutDisplay(DisplayFrames displayFrames, InsetsState insetsState, |
| SparseArray<Rect> barContentFrames) { |
| displayFrames.onBeginLayout(); |
| updateInsetsStateForDisplayCutout(displayFrames, insetsState); |
| insetsState.setDisplayFrame(displayFrames.mUnrestricted); |
| final WindowFrames simulatedWindowFrames = new WindowFrames(); |
| if (mNavigationBar != null) { |
| simulateLayoutDecorWindow(mNavigationBar, displayFrames, insetsState, |
| simulatedWindowFrames, barContentFrames, |
| contentFrame -> layoutNavigationBar(displayFrames, |
| mDisplayContent.getConfiguration().uiMode, mLastNavVisible, |
| mLastNavTranslucent, mLastNavAllowedHidden, |
| mLastNotificationShadeForcesShowingNavigation, contentFrame)); |
| } |
| if (mStatusBar != null) { |
| simulateLayoutDecorWindow(mStatusBar, displayFrames, insetsState, |
| simulatedWindowFrames, barContentFrames, |
| contentFrame -> layoutStatusBar(displayFrames, mLastSystemUiFlags, |
| contentFrame)); |
| } |
| layoutScreenDecorWindows(displayFrames, simulatedWindowFrames); |
| postAdjustDisplayFrames(displayFrames); |
| } |
| |
| /** |
| * Called when layout of the windows is about to start. |
| * |
| * @param displayFrames frames of the display we are doing layout on. |
| * @param uiMode The current uiMode in configuration. |
| */ |
| public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { |
| displayFrames.onBeginLayout(); |
| updateInsetsStateForDisplayCutout(displayFrames, |
| mDisplayContent.getInsetsStateController().getRawInsetsState()); |
| mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); |
| mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); |
| |
| // For purposes of putting out fake window up to steal focus, we will |
| // drive nav being hidden only by whether it is requested. |
| final int sysui = mLastSystemUiFlags; |
| final int behavior = mLastBehavior; |
| final InsetsSourceProvider provider = |
| mDisplayContent.getInsetsStateController().peekSourceProvider(ITYPE_NAVIGATION_BAR); |
| boolean navVisible = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL |
| ? (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 |
| : provider != null |
| ? provider.isClientVisible() |
| : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR); |
| boolean navTranslucent = (sysui |
| & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0; |
| boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0 |
| || (behavior & BEHAVIOR_SHOW_BARS_BY_SWIPE) != 0; |
| boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0 |
| || (behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0; |
| boolean navAllowedHidden = immersive || immersiveSticky; |
| navTranslucent &= !immersiveSticky; // transient trumps translucent |
| boolean isKeyguardShowing = isKeyguardShowing() && !isKeyguardOccluded(); |
| boolean notificationShadeForcesShowingNavigation = |
| !isKeyguardShowing && mNotificationShade != null |
| && (mNotificationShade.getAttrs().privateFlags |
| & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; |
| |
| updateHideNavInputEventReceiver(); |
| |
| // For purposes of positioning and showing the nav bar, if we have decided that it can't |
| // be hidden (because of the screen aspect ratio), then take that into account. |
| navVisible |= !canHideNavigationBar(); |
| |
| boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible, |
| navTranslucent, navAllowedHidden, notificationShadeForcesShowingNavigation, |
| null /* simulatedContentFrame */); |
| if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock); |
| updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, |
| null /* simulatedContentFrame */); |
| if (updateSysUiVisibility) { |
| updateSystemUiVisibilityLw(); |
| } |
| layoutScreenDecorWindows(displayFrames, null /* simulatedFrames */); |
| postAdjustDisplayFrames(displayFrames); |
| mLastNavVisible = navVisible; |
| mLastNavTranslucent = navTranslucent; |
| mLastNavAllowedHidden = navAllowedHidden; |
| mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation; |
| } |
| |
| void updateHideNavInputEventReceiver() { |
| final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() |
| .peekSourceProvider(ITYPE_NAVIGATION_BAR); |
| final InsetsControlTarget navControlTarget = |
| provider != null ? provider.getControlTarget() : null; |
| final WindowState navControllingWin = |
| navControlTarget instanceof WindowState ? (WindowState) navControlTarget : null; |
| final InsetsState requestedState = navControllingWin != null |
| ? navControllingWin.getRequestedInsetsState() : null; |
| final boolean navVisible = requestedState != null |
| ? requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) |
| : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR); |
| final boolean showBarsByTouch = navControllingWin != null |
| && navControllingWin.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_BARS_BY_TOUCH; |
| // When the navigation bar isn't visible, we put up a fake input window to catch all |
| // touch events. This way we can detect when the user presses anywhere to bring back the |
| // nav bar and ensure the application doesn't see the event. |
| if (navVisible || !showBarsByTouch) { |
| if (mInputConsumer != null) { |
| mInputConsumer.dismiss(); |
| mHandler.sendMessage( |
| mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); |
| mInputConsumer = null; |
| Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " dismissed."); |
| } |
| } else if (mInputConsumer == null && getStatusBar() != null && canHideNavigationBar()) { |
| mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer( |
| mHandler.getLooper(), |
| INPUT_CONSUMER_NAVIGATION, |
| HideNavInputEventReceiver::new); |
| Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " created."); |
| // As long as mInputConsumer is active, hover events are not dispatched to the app |
| // and the pointer icon is likely to become stale. Hide it to avoid confusion. |
| InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); |
| } |
| } |
| |
| private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames, |
| InsetsState state) { |
| if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) { |
| state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT); |
| state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT); |
| state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT); |
| state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT); |
| return; |
| } |
| final Rect u = displayFrames.mUnrestricted; |
| final Rect s = displayFrames.mDisplayCutoutSafe; |
| state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(u.left, u.top, s.left, u.bottom); |
| state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(u.left, u.top, u.right, s.top); |
| state.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(s.right, u.top, u.right, u.bottom); |
| state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(u.left, s.bottom, u.right, u.bottom); |
| } |
| |
| /** Enforces the last layout policy for display frames. */ |
| private void postAdjustDisplayFrames(DisplayFrames displayFrames) { |
| if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { |
| // Make sure that the zone we're avoiding for the cutout is at least as tall as the |
| // status bar; otherwise fullscreen apps will end up cutting halfway into the status |
| // bar. |
| displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, |
| displayFrames.mStable.top); |
| } |
| |
| // In case this is a virtual display, and the host display has insets that overlap this |
| // virtual display, apply the insets of the overlapped area onto the current and content |
| // frame of this virtual display. This let us layout windows in the virtual display as |
| // expected when the window needs to avoid overlap with the system windows. |
| // TODO: Generalize the forwarded insets, so that we can handle system windows other than |
| // IME. |
| displayFrames.mCurrent.inset(mForwardedInsets); |
| displayFrames.mContent.inset(mForwardedInsets); |
| } |
| |
| /** |
| * Layout the decor windows with {@link #PRIVATE_FLAG_IS_SCREEN_DECOR}. |
| * |
| * @param displayFrames The display frames to be layouted. |
| * @param simulatedFrames Non-null if the caller only needs the result of display frames (see |
| * {@link WindowState#mSimulatedWindowFrames}). |
| */ |
| private void layoutScreenDecorWindows(DisplayFrames displayFrames, |
| WindowFrames simulatedFrames) { |
| if (mScreenDecorWindows.isEmpty()) { |
| return; |
| } |
| |
| sTmpRect.setEmpty(); |
| final int displayId = displayFrames.mDisplayId; |
| final Rect dockFrame = displayFrames.mDock; |
| final int displayHeight = displayFrames.mDisplayHeight; |
| final int displayWidth = displayFrames.mDisplayWidth; |
| |
| for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { |
| final WindowState w = mScreenDecorWindows.valueAt(i); |
| if (w.getDisplayId() != displayId || !w.isVisibleLw()) { |
| // Skip if not on the same display or not visible. |
| continue; |
| } |
| |
| final boolean isSimulatedLayout = simulatedFrames != null; |
| if (isSimulatedLayout) { |
| w.setSimulatedWindowFrames(simulatedFrames); |
| } |
| final WindowFrames windowFrames = w.getLayoutingWindowFrames(); |
| windowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */, |
| displayFrames.mUnrestricted /* displayFrame */, |
| displayFrames.mUnrestricted /* contentFrame */, |
| displayFrames.mUnrestricted /* visibleFrame */, sTmpRect /* decorFrame */, |
| displayFrames.mUnrestricted /* stableFrame */); |
| try { |
| w.computeFrame(displayFrames); |
| } finally { |
| if (isSimulatedLayout) { |
| w.setSimulatedWindowFrames(null); |
| } |
| } |
| final Rect frame = windowFrames.mFrame; |
| |
| if (frame.left <= 0 && frame.top <= 0) { |
| // Docked at left or top. |
| if (frame.bottom >= displayHeight) { |
| // Docked left. |
| dockFrame.left = Math.max(frame.right, dockFrame.left); |
| } else if (frame.right >= displayWidth) { |
| // Docked top. |
| dockFrame.top = Math.max(frame.bottom, dockFrame.top); |
| } else { |
| Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w |
| + " not docked on left or top of display. frame=" + frame |
| + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); |
| } |
| } else if (frame.right >= displayWidth && frame.bottom >= displayHeight) { |
| // Docked at right or bottom. |
| if (frame.top <= 0) { |
| // Docked right. |
| dockFrame.right = Math.min(frame.left, dockFrame.right); |
| } else if (frame.left <= 0) { |
| // Docked bottom. |
| dockFrame.bottom = Math.min(frame.top, dockFrame.bottom); |
| } else { |
| Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w |
| + " not docked on right or bottom" + " of display. frame=" + frame |
| + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); |
| } |
| } else { |
| // Screen decor windows are required to be docked on one of the sides of the screen. |
| Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w |
| + " not docked on one of the sides of the display. frame=" + frame |
| + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight); |
| } |
| } |
| |
| displayFrames.mRestricted.set(dockFrame); |
| displayFrames.mCurrent.set(dockFrame); |
| displayFrames.mVoiceContent.set(dockFrame); |
| displayFrames.mSystem.set(dockFrame); |
| displayFrames.mContent.set(dockFrame); |
| } |
| |
| private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui, |
| Rect simulatedContentFrame) { |
| // decide where the status bar goes ahead of time |
| if (mStatusBar == null) { |
| return false; |
| } |
| // apply any navigation bar insets |
| sTmpRect.setEmpty(); |
| final WindowFrames windowFrames = mStatusBar.getLayoutingWindowFrames(); |
| windowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */, |
| displayFrames.mUnrestricted /* displayFrame */, |
| displayFrames.mStable /* contentFrame */, |
| displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */, |
| displayFrames.mStable /* stableFrame */); |
| // Let the status bar determine its size. |
| mStatusBar.computeFrame(displayFrames); |
| |
| // For layout, the status bar is always at the top with our fixed height. |
| displayFrames.mStable.top = displayFrames.mUnrestricted.top |
| + mStatusBarHeightForRotation[displayFrames.mRotation]; |
| // Make sure the status bar covers the entire cutout height |
| displayFrames.mStable.top = Math.max(displayFrames.mStable.top, |
| displayFrames.mDisplayCutoutSafe.top); |
| |
| // Tell the bar controller where the collapsed status bar content is. |
| sTmpRect.set(windowFrames.mContentFrame); |
| sTmpRect.intersect(displayFrames.mDisplayCutoutSafe); |
| sTmpRect.top = windowFrames.mContentFrame.top; // Ignore top display cutout inset |
| sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size |
| if (simulatedContentFrame != null) { |
| simulatedContentFrame.set(sTmpRect); |
| } else { |
| mStatusBarController.setContentFrame(sTmpRect); |
| } |
| |
| boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0 |
| || mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR); |
| boolean statusBarTranslucent = (sysui |
| & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0; |
| |
| // If the status bar is hidden, we don't want to cause windows behind it to scroll. |
| if (mStatusBar.isVisibleLw() && !statusBarTransient) { |
| // Status bar may go away, so the screen area it occupies is available to apps but just |
| // covering them when the status bar is visible. |
| final Rect dockFrame = displayFrames.mDock; |
| dockFrame.top = displayFrames.mStable.top; |
| displayFrames.mContent.set(dockFrame); |
| displayFrames.mVoiceContent.set(dockFrame); |
| displayFrames.mCurrent.set(dockFrame); |
| |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format( |
| "dock=%s content=%s cur=%s", dockFrame.toString(), |
| displayFrames.mContent.toString(), displayFrames.mCurrent.toString())); |
| |
| if (!statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent() |
| && !mStatusBar.isAnimatingLw()) { |
| |
| // If the opaque status bar is currently requested to be visible, and not in the |
| // process of animating on or off, then we can tell the app that it is covered by |
| // it. |
| displayFrames.mSystem.top = displayFrames.mStable.top; |
| } |
| } |
| return mStatusBarController.checkHiddenLw(); |
| } |
| |
| private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible, |
| boolean navTranslucent, boolean navAllowedHidden, |
| boolean statusBarForcesShowingNavigation, Rect simulatedContentFrame) { |
| if (mNavigationBar == null) { |
| return false; |
| } |
| |
| final Rect navigationFrame = sTmpNavFrame; |
| boolean transientNavBarShowing = mNavigationBarController.isTransientShowing(); |
| // Force the navigation bar to its appropriate place and size. We need to do this directly, |
| // instead of relying on it to bubble up from the nav bar, because this needs to change |
| // atomically with screen rotations. |
| final int rotation = displayFrames.mRotation; |
| final int displayHeight = displayFrames.mDisplayHeight; |
| final int displayWidth = displayFrames.mDisplayWidth; |
| final Rect dockFrame = displayFrames.mDock; |
| final int navBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation); |
| |
| final Rect cutoutSafeUnrestricted = sTmpRect; |
| cutoutSafeUnrestricted.set(displayFrames.mUnrestricted); |
| cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe); |
| |
| if (navBarPosition == NAV_BAR_BOTTOM) { |
| // It's a system nav bar or a portrait screen; nav bar goes on bottom. |
| final int topNavBar = cutoutSafeUnrestricted.bottom |
| - getNavigationBarFrameHeight(rotation, uiMode); |
| final int top = mNavButtonForcedVisible |
| ? topNavBar |
| : cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode); |
| navigationFrame.set(0, topNavBar, displayWidth, displayFrames.mUnrestricted.bottom); |
| displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top; |
| if (transientNavBarShowing) { |
| mNavigationBarController.setBarShowingLw(true); |
| } else if (navVisible) { |
| mNavigationBarController.setBarShowingLw(true); |
| dockFrame.bottom = displayFrames.mRestricted.bottom = top; |
| } else { |
| // We currently want to hide the navigation UI - unless we expanded the status bar. |
| mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); |
| } |
| if (navVisible && !navTranslucent && !navAllowedHidden |
| && !mNavigationBar.isAnimatingLw() |
| && !mNavigationBarController.wasRecentlyTranslucent()) { |
| // If the opaque nav bar is currently requested to be visible and not in the process |
| // of animating on or off, then we can tell the app that it is covered by it. |
| displayFrames.mSystem.bottom = top; |
| } |
| } else if (navBarPosition == NAV_BAR_RIGHT) { |
| // Landscape screen; nav bar goes to the right. |
| final int left = cutoutSafeUnrestricted.right |
| - getNavigationBarWidth(rotation, uiMode); |
| navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight); |
| displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left; |
| if (transientNavBarShowing) { |
| mNavigationBarController.setBarShowingLw(true); |
| } else if (navVisible) { |
| mNavigationBarController.setBarShowingLw(true); |
| dockFrame.right = displayFrames.mRestricted.right = left; |
| } else { |
| // We currently want to hide the navigation UI - unless we expanded the status bar. |
| mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); |
| } |
| if (navVisible && !navTranslucent && !navAllowedHidden |
| && !mNavigationBar.isAnimatingLw() |
| && !mNavigationBarController.wasRecentlyTranslucent()) { |
| // If the nav bar is currently requested to be visible, and not in the process of |
| // animating on or off, then we can tell the app that it is covered by it. |
| displayFrames.mSystem.right = left; |
| } |
| } else if (navBarPosition == NAV_BAR_LEFT) { |
| // Seascape screen; nav bar goes to the left. |
| final int right = cutoutSafeUnrestricted.left |
| + getNavigationBarWidth(rotation, uiMode); |
| navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight); |
| displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right; |
| if (transientNavBarShowing) { |
| mNavigationBarController.setBarShowingLw(true); |
| } else if (navVisible) { |
| mNavigationBarController.setBarShowingLw(true); |
| dockFrame.left = displayFrames.mRestricted.left = right; |
| } else { |
| // We currently want to hide the navigation UI - unless we expanded the status bar. |
| mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation); |
| } |
| if (navVisible && !navTranslucent && !navAllowedHidden |
| && !mNavigationBar.isAnimatingLw() |
| && !mNavigationBarController.wasRecentlyTranslucent()) { |
| // If the nav bar is currently requested to be visible, and not in the process of |
| // animating on or off, then we can tell the app that it is covered by it. |
| displayFrames.mSystem.left = right; |
| } |
| } |
| |
| // Make sure the content and current rectangles are updated to account for the restrictions |
| // from the navigation bar. |
| displayFrames.mCurrent.set(dockFrame); |
| displayFrames.mVoiceContent.set(dockFrame); |
| displayFrames.mContent.set(dockFrame); |
| // And compute the final frame. |
| sTmpRect.setEmpty(); |
| final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames(); |
| windowFrames.setFrames(navigationFrame /* parentFrame */, |
| navigationFrame /* displayFrame */, |
| displayFrames.mDisplayCutoutSafe /* contentFrame */, |
| navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */, |
| navigationFrame /* stableFrame */); |
| mNavigationBar.computeFrame(displayFrames); |
| if (simulatedContentFrame != null) { |
| simulatedContentFrame.set(windowFrames.mContentFrame); |
| } else { |
| mNavigationBarPosition = navBarPosition; |
| mNavigationBarController.setContentFrame(windowFrames.mContentFrame); |
| } |
| |
| if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame); |
| return mNavigationBarController.checkHiddenLw(); |
| } |
| |
| private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached, |
| boolean insetDecors, Rect pf, Rect df, Rect cf, Rect vf, |
| DisplayFrames displayFrames) { |
| if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) { |
| // Here's a special case: if the child window is not the 'dock window' |
| // or input method target, and the window it is attached to is below |
| // the dock window, then the frames we computed for the window it is |
| // attached to can not be used because the dock is effectively part |
| // of the underlying window and the attached window is floating on top |
| // of the whole thing. So, we ignore the attached window and explicitly |
| // compute the frames that would be appropriate without the dock. |
| vf.set(displayFrames.mDock); |
| cf.set(displayFrames.mDock); |
| df.set(displayFrames.mDock); |
| } else { |
| |
| // In case we forced the window to draw behind the navigation bar, restrict df to |
| // DF.Restricted to simulate old compat behavior. |
| Rect parentDisplayFrame = attached.getDisplayFrameLw(); |
| final WindowManager.LayoutParams attachedAttrs = attached.mAttrs; |
| if ((attachedAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 |
| && (attachedAttrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 |
| && (attachedAttrs.systemUiVisibility |
| & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) { |
| parentDisplayFrame = new Rect(parentDisplayFrame); |
| parentDisplayFrame.intersect(displayFrames.mRestricted); |
| } |
| |
| // The effective display frame of the attached window depends on whether it is taking |
| // care of insetting its content. If not, we need to use the parent's content frame so |
| // that the entire window is positioned within that content. Otherwise we can use the |
| // parent display frame and let the attached window take care of positioning its content |
| // appropriately. |
| if (adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| // Set the content frame of the attached window to the parent's decor frame |
| // (same as content frame when IME isn't present) if specifically requested by |
| // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag. |
| // Otherwise, use the overscan frame. |
| cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0 |
| ? attached.getContentFrameLw() : parentDisplayFrame); |
| } else { |
| // If the window is resizing, then we want to base the content frame on our attached |
| // content frame to resize...however, things can be tricky if the attached window is |
| // NOT in resize mode, in which case its content frame will be larger. |
| // Ungh. So to deal with that, make sure the content frame we end up using is not |
| // covering the IM dock. |
| cf.set(attached.getContentFrameLw()); |
| if (attached.isVoiceInteraction()) { |
| cf.intersectUnchecked(displayFrames.mVoiceContent); |
| } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) { |
| cf.intersectUnchecked(displayFrames.mContent); |
| } |
| } |
| df.set(insetDecors ? parentDisplayFrame : cf); |
| vf.set(attached.getVisibleFrameLw()); |
| } |
| // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be |
| // positioned relative to its parent or the entire screen. |
| pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); |
| } |
| |
| private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) { |
| if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) { |
| return; |
| } |
| // If app is requesting a stable layout, don't let the content insets go below the stable |
| // values. |
| if ((fl & FLAG_FULLSCREEN) != 0) { |
| r.intersectUnchecked(displayFrames.mStableFullscreen); |
| } else { |
| r.intersectUnchecked(displayFrames.mStable); |
| } |
| } |
| |
| private boolean canReceiveInput(WindowState win) { |
| boolean notFocusable = |
| (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; |
| boolean altFocusableIm = |
| (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0; |
| boolean notFocusableForIm = notFocusable ^ altFocusableIm; |
| return !notFocusableForIm; |
| } |
| |
| /** |
| * Called for each window attached to the window manager as layout is proceeding. The |
| * implementation of this function must take care of setting the window's frame, either here or |
| * in finishLayout(). |
| * |
| * @param win The window being positioned. |
| * @param attached For sub-windows, the window it is attached to; this |
| * window will already have had layoutWindow() called on it |
| * so you can use its Rect. Otherwise null. |
| * @param displayFrames The display frames. |
| */ |
| public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { |
| // We've already done the navigation bar, status bar, and all screen decor windows. If the |
| // status bar can receive input, we need to layout it again to accommodate for the IME |
| // window. |
| if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar |
| || mScreenDecorWindows.contains(win)) { |
| return; |
| } |
| final WindowManager.LayoutParams attrs = win.getAttrs(); |
| |
| final int type = attrs.type; |
| final int fl = PolicyControl.getWindowFlags(win, attrs); |
| final int pfl = attrs.privateFlags; |
| final int sim = attrs.softInputMode; |
| final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs); |
| final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs); |
| |
| displayFrames = win.getDisplayFrames(displayFrames); |
| final WindowFrames windowFrames = win.getWindowFrames(); |
| |
| sTmpLastParentFrame.set(windowFrames.mParentFrame); |
| final Rect pf = windowFrames.mParentFrame; |
| final Rect df = windowFrames.mDisplayFrame; |
| final Rect cf = windowFrames.mContentFrame; |
| final Rect vf = windowFrames.mVisibleFrame; |
| final Rect dcf = windowFrames.mDecorFrame; |
| final Rect sf = windowFrames.mStableFrame; |
| dcf.setEmpty(); |
| windowFrames.setParentFrameWasClippedByDisplayCutout(false); |
| |
| final boolean hasNavBar = hasNavigationBar() && mNavigationBar != null |
| && mNavigationBar.isVisibleLw(); |
| |
| final int adjust = sim & SOFT_INPUT_MASK_ADJUST; |
| |
| final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN; |
| final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; |
| |
| sf.set(displayFrames.mStable); |
| |
| if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { |
| final @InsetsType int typesToFit = attrs.getFitInsetsTypes(); |
| final @InsetsSide int sidesToFit = attrs.getFitInsetsSides(); |
| final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit); |
| final Rect dfu = displayFrames.mUnrestricted; |
| Insets insets = Insets.of(0, 0, 0, 0); |
| for (int i = types.size() - 1; i >= 0; i--) { |
| final InsetsSource source = mDisplayContent.getInsetsPolicy() |
| .getInsetsForDispatch(win).peekSource(types.valueAt(i)); |
| if (source == null) { |
| continue; |
| } |
| insets = Insets.max(insets, source.calculateInsets( |
| dfu, attrs.isFitInsetsIgnoringVisibility())); |
| } |
| final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0; |
| final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0; |
| final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0; |
| final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0; |
| df.set(left, top, dfu.right - right, dfu.bottom - bottom); |
| if (attached == null) { |
| pf.set(df); |
| if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { |
| final InsetsSource source = mDisplayContent.getInsetsPolicy() |
| .getInsetsForDispatch(win).peekSource(ITYPE_IME); |
| if (source != null) { |
| pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */)); |
| } |
| } |
| vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING |
| ? displayFrames.mCurrent : displayFrames.mDock); |
| } else { |
| pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df); |
| vf.set(attached.getVisibleFrameLw()); |
| } |
| cf.set(adjust != SOFT_INPUT_ADJUST_RESIZE |
| ? displayFrames.mDock : displayFrames.mContent); |
| dcf.set(displayFrames.mSystem); |
| } else if (type == TYPE_INPUT_METHOD) { |
| vf.set(displayFrames.mDock); |
| cf.set(displayFrames.mDock); |
| df.set(displayFrames.mDock); |
| pf.set(displayFrames.mDock); |
| // IM dock windows layout below the nav bar... |
| pf.bottom = df.bottom = displayFrames.mUnrestricted.bottom; |
| // ...with content insets above the nav bar |
| cf.bottom = vf.bottom = displayFrames.mStable.bottom; |
| if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { |
| // The status bar forces the navigation bar while it's visible. Make sure the IME |
| // avoids the navigation bar in that case. |
| if (mNavigationBarPosition == NAV_BAR_RIGHT) { |
| pf.right = df.right = cf.right = vf.right = |
| displayFrames.mStable.right; |
| } else if (mNavigationBarPosition == NAV_BAR_LEFT) { |
| pf.left = df.left = cf.left = vf.left = displayFrames.mStable.left; |
| } |
| } |
| |
| // In case the navigation bar is on the bottom, we use the frame height instead of the |
| // regular height for the insets we send to the IME as we need some space to show |
| // additional buttons in SystemUI when the IME is up. |
| if (mNavigationBarPosition == NAV_BAR_BOTTOM) { |
| final int rotation = displayFrames.mRotation; |
| final int uimode = mService.mPolicy.getUiMode(); |
| final int navHeightOffset = getNavigationBarFrameHeight(rotation, uimode) |
| - getNavigationBarHeight(rotation, uimode); |
| if (navHeightOffset > 0) { |
| cf.bottom -= navHeightOffset; |
| sf.bottom -= navHeightOffset; |
| vf.bottom -= navHeightOffset; |
| dcf.bottom -= navHeightOffset; |
| } |
| } |
| |
| // IM dock windows always go to the bottom of the screen. |
| attrs.gravity = Gravity.BOTTOM; |
| } else if (type == TYPE_VOICE_INTERACTION) { |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| if (adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| cf.set(displayFrames.mDock); |
| } else { |
| cf.set(displayFrames.mContent); |
| } |
| if (adjust != SOFT_INPUT_ADJUST_NOTHING) { |
| vf.set(displayFrames.mCurrent); |
| } else { |
| vf.set(cf); |
| } |
| } else if (type == TYPE_WALLPAPER) { |
| layoutWallpaper(displayFrames, pf, df, cf); |
| } else if (win == mStatusBar || type == TYPE_NOTIFICATION_SHADE) { |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| cf.set(displayFrames.mStable); |
| vf.set(displayFrames.mStable); |
| |
| if (adjust == SOFT_INPUT_ADJUST_RESIZE) { |
| // cf.bottom should not be below the stable bottom, or the content might be obscured |
| // by the navigation bar. |
| if (cf.bottom > displayFrames.mContent.bottom) { |
| cf.bottom = displayFrames.mContent.bottom; |
| } |
| } else { |
| if (cf.bottom > displayFrames.mDock.bottom) { |
| cf.bottom = displayFrames.mDock.bottom; |
| } |
| if (vf.bottom > displayFrames.mContent.bottom) { |
| vf.bottom = displayFrames.mContent.bottom; |
| } |
| } |
| } else { |
| dcf.set(displayFrames.mSystem); |
| final boolean isAppWindow = |
| type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW; |
| final boolean topAtRest = |
| win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw(); |
| if (isAppWindow && !topAtRest) { |
| if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 |
| && (fl & FLAG_FULLSCREEN) == 0 |
| && (fl & FLAG_TRANSLUCENT_STATUS) == 0 |
| && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 |
| && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) { |
| // Ensure policy decor includes status bar |
| dcf.top = displayFrames.mStable.top; |
| } |
| if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0 |
| && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 |
| && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 |
| && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) { |
| // Ensure policy decor includes navigation bar |
| dcf.bottom = displayFrames.mStable.bottom; |
| dcf.right = displayFrames.mStable.right; |
| } |
| } |
| |
| if (layoutInScreen && layoutInsetDecor) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() |
| + "): IN_SCREEN, INSET_DECOR"); |
| // This is the case for a normal activity window: we want it to cover all of the |
| // screen space, and it can take care of moving its contents to account for screen |
| // decorations that intrude into that space. |
| if (attached != null) { |
| // If this window is attached to another, our display |
| // frame is the same as the one we are attached to. |
| setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, cf, vf, |
| displayFrames); |
| } else { |
| if (type == TYPE_STATUS_BAR_ADDITIONAL || type == TYPE_STATUS_BAR_SUB_PANEL) { |
| // Status bar panels are the only windows who can go on top of the status |
| // bar. They are protected by the STATUS_BAR_SERVICE permission, so they |
| // have the same privileges as the status bar itself. |
| // |
| // However, they should still dodge the navigation bar if it exists. |
| |
| pf.left = df.left = hasNavBar |
| ? displayFrames.mDock.left : displayFrames.mUnrestricted.left; |
| pf.top = df.top = displayFrames.mUnrestricted.top; |
| pf.right = df.right = hasNavBar |
| ? displayFrames.mRestricted.right |
| : displayFrames.mUnrestricted.right; |
| pf.bottom = df.bottom = hasNavBar |
| ? displayFrames.mRestricted.bottom |
| : displayFrames.mUnrestricted.bottom; |
| |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf); |
| } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 |
| && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW |
| || type == TYPE_VOLUME_OVERLAY |
| || type == TYPE_KEYGUARD_DIALOG)) { |
| // Asking for layout as if the nav bar is hidden, lets the application |
| // extend into the unrestricted overscan screen area. We only do this for |
| // application windows and certain system windows to ensure no window that |
| // can be above the nav bar can do this. |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| } else { |
| df.set(displayFrames.mRestricted); |
| pf.set(displayFrames.mRestricted); |
| } |
| |
| if ((fl & FLAG_FULLSCREEN) == 0) { |
| if (win.isVoiceInteraction()) { |
| cf.set(displayFrames.mVoiceContent); |
| } else { |
| // IME Insets are handled on the client for ADJUST_RESIZE in the new |
| // insets world |
| if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE |
| || adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| cf.set(displayFrames.mDock); |
| } else { |
| cf.set(displayFrames.mContent); |
| } |
| } |
| } else { |
| // Full screen windows are always given a layout that is as if the status |
| // bar and other transient decors are gone. This is to avoid bad states when |
| // moving from a window that is not hiding the status bar to one that is. |
| cf.set(displayFrames.mRestricted); |
| } |
| applyStableConstraints(sysUiFl, fl, cf, displayFrames); |
| if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE |
| && adjust != SOFT_INPUT_ADJUST_NOTHING) { |
| vf.set(displayFrames.mCurrent); |
| } else { |
| vf.set(cf); |
| } |
| } |
| } else if (layoutInScreen || (sysUiFl |
| & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
| | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() |
| + "): IN_SCREEN"); |
| // A window that has requested to fill the entire screen just |
| // gets everything, period. |
| if (type == TYPE_STATUS_BAR_ADDITIONAL || type == TYPE_STATUS_BAR_SUB_PANEL) { |
| cf.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| if (hasNavBar) { |
| pf.left = df.left = cf.left = displayFrames.mDock.left; |
| pf.right = df.right = cf.right = displayFrames.mRestricted.right; |
| pf.bottom = df.bottom = cf.bottom = |
| displayFrames.mRestricted.bottom; |
| } |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf); |
| } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) { |
| // The navigation bar has Real Ultimate Power. |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf); |
| } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT) |
| && ((fl & FLAG_FULLSCREEN) != 0)) { |
| // Fullscreen secure system overlays get what they ask for. Screenshot region |
| // selection overlay should also expand to full screen. |
| cf.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| } else if (type == TYPE_BOOT_PROGRESS) { |
| // Boot progress screen always covers entire display. |
| cf.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 |
| && (type == TYPE_NOTIFICATION_SHADE |
| || type == TYPE_TOAST |
| || type == TYPE_DOCK_DIVIDER |
| || type == TYPE_VOICE_INTERACTION_STARTING |
| || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) { |
| // Asking for layout as if the nav bar is hidden, lets the |
| // application extend into the unrestricted screen area. We |
| // only do this for application windows (or toasts) to ensure no window that |
| // can be above the nav bar can do this. |
| // XXX This assumes that an app asking for this will also |
| // ask for layout in only content. We can't currently figure out |
| // what the screen would be if only laying out to hide the nav bar. |
| cf.set(displayFrames.mUnrestricted); |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { |
| df.set(displayFrames.mRestricted); |
| pf.set(displayFrames.mRestricted); |
| |
| // IME Insets are handled on the client for ADJUST_RESIZE in the new insets |
| // world |
| if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE |
| || adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| cf.set(displayFrames.mDock); |
| } else { |
| cf.set(displayFrames.mContent); |
| } |
| } else { |
| cf.set(displayFrames.mRestricted); |
| df.set(displayFrames.mRestricted); |
| pf.set(displayFrames.mRestricted); |
| } |
| |
| applyStableConstraints(sysUiFl, fl, cf, displayFrames); |
| |
| if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE |
| && adjust != SOFT_INPUT_ADJUST_NOTHING) { |
| vf.set(displayFrames.mCurrent); |
| } else { |
| vf.set(cf); |
| } |
| } else if (attached != null) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() |
| + "): attached to " + attached); |
| // A child window should be placed inside of the same visible |
| // frame that its parent had. |
| setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, cf, vf, |
| displayFrames); |
| } else { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() |
| + "): normal window"); |
| // Otherwise, a normal window must be placed inside the content |
| // of all screen decorations. |
| if (type == TYPE_STATUS_BAR_ADDITIONAL) { |
| // Status bar panels can go on |
| // top of the status bar. They are protected by the STATUS_BAR_SERVICE |
| // permission, so they have the same privileges as the status bar itself. |
| cf.set(displayFrames.mRestricted); |
| df.set(displayFrames.mRestricted); |
| pf.set(displayFrames.mRestricted); |
| } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { |
| // These dialogs are stable to interim decor changes. |
| cf.set(displayFrames.mStable); |
| df.set(displayFrames.mStable); |
| pf.set(displayFrames.mStable); |
| } else { |
| pf.set(displayFrames.mContent); |
| if (win.isVoiceInteraction()) { |
| cf.set(displayFrames.mVoiceContent); |
| df.set(displayFrames.mVoiceContent); |
| } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) { |
| cf.set(displayFrames.mDock); |
| df.set(displayFrames.mDock); |
| } else { |
| cf.set(displayFrames.mContent); |
| df.set(displayFrames.mContent); |
| } |
| if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE |
| && adjust != SOFT_INPUT_ADJUST_NOTHING) { |
| vf.set(displayFrames.mCurrent); |
| } else { |
| vf.set(cf); |
| } |
| } |
| } |
| } |
| |
| final int cutoutMode = attrs.layoutInDisplayCutoutMode; |
| final boolean attachedInParent = attached != null && !layoutInScreen; |
| final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 |
| || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0 |
| || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL |
| && !win.getRequestedInsetsState().getSourceOrDefaultVisibility( |
| ITYPE_STATUS_BAR)); |
| final boolean requestedHideNavigation = |
| (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 |
| || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL |
| && !win.getRequestedInsetsState().getSourceOrDefaultVisibility( |
| ITYPE_NAVIGATION_BAR)); |
| |
| // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get |
| // cropped / shifted to the displayFrame in WindowState. |
| final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen |
| && type != TYPE_BASE_APPLICATION; |
| // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in |
| // the cutout safe zone. |
| if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { |
| final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect; |
| displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); |
| if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) { |
| if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) { |
| displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; |
| displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; |
| } else { |
| displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; |
| displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; |
| } |
| } |
| |
| if (layoutInScreen && layoutInsetDecor && !requestedFullscreen |
| && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT |
| || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) { |
| // At the top we have the status bar, so apps that are |
| // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN |
| // already expect that there's an inset there and we don't need to exclude |
| // the window from that area. |
| displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE; |
| } |
| if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation |
| && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT |
| || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) { |
| // Same for the navigation bar. |
| switch (mNavigationBarPosition) { |
| case NAV_BAR_BOTTOM: |
| displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; |
| break; |
| case NAV_BAR_RIGHT: |
| displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE; |
| break; |
| case NAV_BAR_LEFT: |
| displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE; |
| break; |
| } |
| } |
| if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) { |
| // The IME can always extend under the bottom cutout if the navbar is there. |
| displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE; |
| } |
| // Windows that are attached to a parent and laid out in said parent already avoid |
| // the cutout according to that parent and don't need to be further constrained. |
| // Floating IN_SCREEN windows get what they ask for and lay out in the full screen. |
| // They will later be cropped or shifted using the displayFrame in WindowState, |
| // which prevents overlap with the DisplayCutout. |
| if (!attachedInParent && !floatingInScreenWindow) { |
| sTmpRect.set(pf); |
| pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars); |
| windowFrames.setParentFrameWasClippedByDisplayCutout(!sTmpRect.equals(pf)); |
| } |
| // Make sure that NO_LIMITS windows clipped to the display don't extend under the |
| // cutout. |
| df.intersectUnchecked(displayCutoutSafeExceptMaybeBars); |
| } |
| |
| // Content should never appear in the cutout. |
| cf.intersectUnchecked(displayFrames.mDisplayCutoutSafe); |
| |
| // TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it. |
| // Also, we don't allow windows in multi-window mode to extend out of the screen. |
| if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR |
| && !win.inMultiWindowMode()) { |
| df.left = df.top = -10000; |
| df.right = df.bottom = 10000; |
| if (type != TYPE_WALLPAPER) { |
| cf.left = cf.top = vf.left = vf.top = -10000; |
| cf.right = cf.bottom = vf.right = vf.bottom = 10000; |
| } |
| } |
| |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle() |
| + ": sim=#" + Integer.toHexString(sim) |
| + " attach=" + attached + " type=" + type |
| + String.format(" flags=0x%08x", fl) |
| + " pf=" + pf.toShortString() + " df=" + df.toShortString() |
| + " cf=" + cf.toShortString() + " vf=" + vf.toShortString() |
| + " dcf=" + dcf.toShortString() |
| + " sf=" + sf.toShortString()); |
| |
| if (!sTmpLastParentFrame.equals(pf)) { |
| windowFrames.setContentChanged(true); |
| } |
| |
| win.computeFrame(displayFrames); |
| |
| // When system bars are added to the Android device through {@link #layoutStatusBar} and |
| // {@link #layoutNavigationBar}, the displayFrames are adjusted to take the system bars into |
| // account. The call below adjusts the display frames for system bars which use flexible |
| // insets mapping instead of {@link #layoutStatusbar} and {@link #layoutNavigationBar}. Note |
| // that this call is a no-op if not using flexible insets mapping. |
| adjustDisplayFramesForFlexibleInsets(win, displayFrames); |
| |
| // Dock windows carve out the bottom of the screen, so normal windows |
| // can't appear underneath them. |
| if (type == TYPE_INPUT_METHOD && win.isVisibleLw() |
| && !win.getGivenInsetsPendingLw()) { |
| offsetInputMethodWindowLw(win, displayFrames); |
| } |
| if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() |
| && !win.getGivenInsetsPendingLw()) { |
| offsetVoiceInputWindowLw(win, displayFrames); |
| } |
| } |
| |
| private void adjustDisplayFramesForFlexibleInsets(WindowState win, |
| DisplayFrames displayFrames) { |
| if (win == mStatusBarAlt) { |
| adjustDisplayFramesForWindow(win, mStatusBarAltPosition, displayFrames); |
| } else if (win == mNavigationBarAlt) { |
| adjustDisplayFramesForWindow(win, mNavigationBarAltPosition, displayFrames); |
| } else if (win == mClimateBarAlt) { |
| adjustDisplayFramesForWindow(win, mClimateBarAltPosition, displayFrames); |
| } else if (win == mExtraNavBarAlt) { |
| adjustDisplayFramesForWindow(win, mExtraNavBarAltPosition, displayFrames); |
| } |
| } |
| |
| private static void adjustDisplayFramesForWindow(WindowState win, |
| @WindowManagerPolicy.AltBarPosition int position, DisplayFrames displayFrames) { |
| final Rect frame = win.getFrameLw(); |
| |
| // Note: This doesn't take into account display cutouts. |
| switch (position) { |
| case ALT_BAR_TOP: |
| displayFrames.mStable.top = frame.bottom; |
| break; |
| case ALT_BAR_BOTTOM: |
| displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = frame.top; |
| break; |
| case ALT_BAR_LEFT: |
| displayFrames.mStable.left = displayFrames.mStableFullscreen.left = frame.right; |
| break; |
| case ALT_BAR_RIGHT: |
| displayFrames.mStable.right = displayFrames.mStableFullscreen.right = frame.left; |
| break; |
| } |
| } |
| |
| private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect cf) { |
| // The wallpaper has Real Ultimate Power |
| df.set(displayFrames.mUnrestricted); |
| pf.set(displayFrames.mUnrestricted); |
| cf.set(displayFrames.mUnrestricted); |
| } |
| |
| private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) { |
| final int rotation = displayFrames.mRotation; |
| final int navBarPosition = navigationBarPosition(displayFrames.mDisplayWidth, |
| displayFrames.mDisplayHeight, rotation); |
| |
| int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); |
| top += win.getGivenContentInsetsLw().top; |
| displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top); |
| if (navBarPosition == NAV_BAR_BOTTOM) { |
| // Always account for the nav bar frame height on the bottom since in all navigation |
| // modes we make room to show the dismiss-ime button, even if the IME does not report |
| // insets (ie. when floating) |
| final int uimode = mService.mPolicy.getUiMode(); |
| final int navFrameHeight = getNavigationBarFrameHeight(rotation, uimode); |
| displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, |
| displayFrames.mUnrestricted.bottom - navFrameHeight); |
| } |
| displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); |
| top = win.getVisibleFrameLw().top; |
| top += win.getGivenVisibleInsetsLw().top; |
| displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top); |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom=" |
| + displayFrames.mDock.bottom + " mContentBottom=" |
| + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom); |
| } |
| |
| private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) { |
| int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top); |
| top += win.getGivenContentInsetsLw().top; |
| displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top); |
| } |
| |
| WindowState getTopFullscreenOpaqueWindow() { |
| return mTopFullscreenOpaqueWindowState; |
| } |
| |
| boolean isTopLayoutFullscreen() { |
| return mTopIsFullscreen; |
| } |
| |
| /** |
| * Called following layout of all windows before each window has policy applied. |
| */ |
| public void beginPostLayoutPolicyLw() { |
| mTopFullscreenOpaqueWindowState = null; |
| mTopFullscreenOpaqueOrDimmingWindowState = null; |
| mTopDockedOpaqueWindowState = null; |
| mTopDockedOpaqueOrDimmingWindowState = null; |
| mForceStatusBar = false; |
| mForcingShowNavBar = false; |
| mForcingShowNavBarLayer = -1; |
| |
| mAllowLockscreenWhenOn = false; |
| mShowingDream = false; |
| mIsFreeformWindowOverlappingWithNavBar = false; |
| } |
| |
| /** |
| * Called following layout of all window to apply policy to each window. |
| * |
| * @param win The window being positioned. |
| * @param attrs The LayoutParams of the window. |
| * @param attached For sub-windows, the window it is attached to. Otherwise null. |
| */ |
| public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs, |
| WindowState attached, WindowState imeTarget) { |
| final boolean affectsSystemUi = win.canAffectSystemUiFlags(); |
| if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi); |
| mService.mPolicy.applyKeyguardPolicyLw(win, imeTarget); |
| final int fl = PolicyControl.getWindowFlags(win, attrs); |
| if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi |
| && attrs.type == TYPE_INPUT_METHOD) { |
| mForcingShowNavBar = true; |
| mForcingShowNavBarLayer = win.getSurfaceLayer(); |
| } |
| |
| boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW |
| && attrs.type < FIRST_SYSTEM_WINDOW; |
| final int windowingMode = win.getWindowingMode(); |
| final boolean inFullScreenOrSplitScreenSecondaryWindowingMode = |
| windowingMode == WINDOWING_MODE_FULLSCREEN |
| || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; |
| if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) { |
| if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) { |
| mForceStatusBar = true; |
| } |
| if (win.isDreamWindow()) { |
| // If the lockscreen was showing when the dream started then wait |
| // for the dream to draw before hiding the lockscreen. |
| if (!mDreamingLockscreen |
| || (win.isVisibleLw() && win.hasDrawnLw())) { |
| mShowingDream = true; |
| appWindow = true; |
| } |
| } |
| |
| // For app windows that are not attached, we decide if all windows in the app they |
| // represent should be hidden or if we should hide the lockscreen. For attached app |
| // windows we defer the decision to the window it is attached to. |
| if (appWindow && attached == null) { |
| if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win); |
| mTopFullscreenOpaqueWindowState = win; |
| if (mTopFullscreenOpaqueOrDimmingWindowState == null) { |
| mTopFullscreenOpaqueOrDimmingWindowState = win; |
| } |
| if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { |
| mAllowLockscreenWhenOn = true; |
| } |
| } |
| } |
| } |
| |
| // Voice interaction overrides both top fullscreen and top docked. |
| if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION) { |
| if (mTopFullscreenOpaqueWindowState == null) { |
| mTopFullscreenOpaqueWindowState = win; |
| if (mTopFullscreenOpaqueOrDimmingWindowState == null) { |
| mTopFullscreenOpaqueOrDimmingWindowState = win; |
| } |
| } |
| if (mTopDockedOpaqueWindowState == null) { |
| mTopDockedOpaqueWindowState = win; |
| if (mTopDockedOpaqueOrDimmingWindowState == null) { |
| mTopDockedOpaqueOrDimmingWindowState = win; |
| } |
| } |
| } |
| |
| // Keep track of the window if it's dimming but not necessarily fullscreen. |
| if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi |
| && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) { |
| mTopFullscreenOpaqueOrDimmingWindowState = win; |
| } |
| |
| // We need to keep track of the top "fullscreen" opaque window for the docked stack |
| // separately, because both the "real fullscreen" opaque window and the one for the docked |
| // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR. |
| if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null |
| && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { |
| mTopDockedOpaqueWindowState = win; |
| if (mTopDockedOpaqueOrDimmingWindowState == null) { |
| mTopDockedOpaqueOrDimmingWindowState = win; |
| } |
| } |
| |
| // Check if the freeform window overlaps with the navigation bar area. |
| final WindowState navBarWin = hasNavigationBar() ? mNavigationBar : null; |
| if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode() |
| && isOverlappingWithNavBar(win, navBarWin)) { |
| mIsFreeformWindowOverlappingWithNavBar = true; |
| } |
| |
| // Also keep track of any windows that are dimming but not necessarily fullscreen in the |
| // docked stack. |
| if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming() |
| && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { |
| mTopDockedOpaqueOrDimmingWindowState = win; |
| } |
| } |
| |
| /** |
| * Called following layout of all windows and after policy has been applied |
| * to each window. If in this function you do |
| * something that may have modified the animation state of another window, |
| * be sure to return non-zero in order to perform another pass through layout. |
| * |
| * @return Return any bit set of |
| * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_LAYOUT}, |
| * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_CONFIG}, |
| * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER}, or |
| * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}. |
| */ |
| public int finishPostLayoutPolicyLw() { |
| int changes = 0; |
| boolean topIsFullscreen = false; |
| |
| // If we are not currently showing a dream then remember the current |
| // lockscreen state. We will use this to determine whether the dream |
| // started while the lockscreen was showing and remember this state |
| // while the dream is showing. |
| if (!mShowingDream) { |
| mDreamingLockscreen = mService.mPolicy.isKeyguardShowingAndNotOccluded(); |
| } |
| |
| if (getStatusBar() != null) { |
| if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar |
| + " top=" + mTopFullscreenOpaqueWindowState); |
| final boolean forceShowStatusBar = (getStatusBar().getAttrs().privateFlags |
| & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0; |
| final boolean notificationShadeForcesShowingNavigation = |
| mNotificationShade != null |
| && (mNotificationShade.getAttrs().privateFlags |
| & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; |
| |
| boolean topAppHidesStatusBar = topAppHidesStatusBar(); |
| if (mForceStatusBar || forceShowStatusBar) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced"); |
| if (mStatusBarController.setBarShowingLw(true)) { |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| // Maintain fullscreen layout until incoming animation is complete. |
| topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw(); |
| // Transient status bar is not allowed if notification shade is expecting the |
| // navigation keys from the user. |
| if (notificationShadeForcesShowingNavigation |
| && mStatusBarController.isTransientShowing()) { |
| mStatusBarController.updateVisibilityLw(false /*transientAllowed*/, |
| mLastSystemUiFlags, mLastSystemUiFlags); |
| } |
| } else if (mTopFullscreenOpaqueWindowState != null) { |
| topIsFullscreen = topAppHidesStatusBar; |
| // The subtle difference between the window for mTopFullscreenOpaqueWindowState |
| // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window |
| // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the |
| // case though. |
| if (mStatusBarController.isTransientShowing()) { |
| if (mStatusBarController.setBarShowingLw(true)) { |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| } else if (topIsFullscreen && !mDisplayContent.getDefaultTaskDisplayArea() |
| .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar"); |
| if (mStatusBarController.setBarShowingLw(false)) { |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } else { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding"); |
| } |
| } else { |
| if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen"); |
| if (mStatusBarController.setBarShowingLw(true)) { |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| topAppHidesStatusBar = false; |
| } |
| } |
| mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar); |
| } |
| |
| if (mTopIsFullscreen != topIsFullscreen) { |
| if (!topIsFullscreen) { |
| // Force another layout when status bar becomes fully shown. |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| mTopIsFullscreen = topIsFullscreen; |
| } |
| |
| if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) { |
| // If the navigation bar has been hidden or shown, we need to do another |
| // layout pass to update that window. |
| changes |= FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| |
| if (mShowingDream != mLastShowingDream) { |
| mLastShowingDream = mShowingDream; |
| mService.notifyShowingDreamChanged(); |
| } |
| |
| mService.mPolicy.setAllowLockscreenWhenOn(getDisplayId(), mAllowLockscreenWhenOn); |
| return changes; |
| } |
| |
| /** |
| * @return Whether the top app should hide the statusbar based on the top fullscreen opaque |
| * window. |
| */ |
| boolean topAppHidesStatusBar() { |
| if (mTopFullscreenOpaqueWindowState == null || mForceShowSystemBars) { |
| return false; |
| } |
| final LayoutParams attrs = mTopFullscreenOpaqueWindowState.getAttrs(); |
| final int fl = PolicyControl.getWindowFlags(null, attrs); |
| final int sysui = PolicyControl.getSystemUiVisibility(null, attrs); |
| final InsetsSource request = mTopFullscreenOpaqueWindowState.getRequestedInsetsState() |
| .peekSource(ITYPE_STATUS_BAR); |
| if (WindowManagerDebugConfig.DEBUG) { |
| Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()); |
| Slog.d(TAG, "attr: " + attrs + " request: " + request); |
| } |
| return (fl & LayoutParams.FLAG_FULLSCREEN) != 0 |
| || (sysui & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0 |
| || (request != null && !request.isVisible()); |
| } |
| |
| /** |
| * Called when the user is switched. |
| */ |
| public void switchUser() { |
| updateCurrentUserResources(); |
| } |
| |
| /** |
| * Called when the resource overlays change. |
| */ |
| public void onOverlayChangedLw() { |
| updateCurrentUserResources(); |
| onConfigurationChanged(); |
| mSystemGestures.onConfigurationChanged(); |
| } |
| |
| /** |
| * Called when the configuration has changed, and it's safe to load new values from resources. |
| */ |
| public void onConfigurationChanged() { |
| final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); |
| |
| final Resources res = getCurrentUserResources(); |
| final int portraitRotation = displayRotation.getPortraitRotation(); |
| final int upsideDownRotation = displayRotation.getUpsideDownRotation(); |
| final int landscapeRotation = displayRotation.getLandscapeRotation(); |
| final int seascapeRotation = displayRotation.getSeascapeRotation(); |
| final int uiMode = mService.mPolicy.getUiMode(); |
| |
| if (hasStatusBar()) { |
| mStatusBarHeightForRotation[portraitRotation] = |
| mStatusBarHeightForRotation[upsideDownRotation] = |
| res.getDimensionPixelSize(R.dimen.status_bar_height_portrait); |
| mStatusBarHeightForRotation[landscapeRotation] = |
| mStatusBarHeightForRotation[seascapeRotation] = |
| res.getDimensionPixelSize(R.dimen.status_bar_height_landscape); |
| } else { |
| mStatusBarHeightForRotation[portraitRotation] = |
| mStatusBarHeightForRotation[upsideDownRotation] = |
| mStatusBarHeightForRotation[landscapeRotation] = |
| mStatusBarHeightForRotation[seascapeRotation] = 0; |
| } |
| |
| // Height of the navigation bar when presented horizontally at bottom |
| mNavigationBarHeightForRotationDefault[portraitRotation] = |
| mNavigationBarHeightForRotationDefault[upsideDownRotation] = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_height); |
| mNavigationBarHeightForRotationDefault[landscapeRotation] = |
| mNavigationBarHeightForRotationDefault[seascapeRotation] = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape); |
| |
| // Height of the navigation bar frame when presented horizontally at bottom |
| mNavigationBarFrameHeightForRotationDefault[portraitRotation] = |
| mNavigationBarFrameHeightForRotationDefault[upsideDownRotation] = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height); |
| mNavigationBarFrameHeightForRotationDefault[landscapeRotation] = |
| mNavigationBarFrameHeightForRotationDefault[seascapeRotation] = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height_landscape); |
| |
| // Width of the navigation bar when presented vertically along one side |
| mNavigationBarWidthForRotationDefault[portraitRotation] = |
| mNavigationBarWidthForRotationDefault[upsideDownRotation] = |
| mNavigationBarWidthForRotationDefault[landscapeRotation] = |
| mNavigationBarWidthForRotationDefault[seascapeRotation] = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_width); |
| |
| if (ALTERNATE_CAR_MODE_NAV_SIZE) { |
| // Height of the navigation bar when presented horizontally at bottom |
| mNavigationBarHeightForRotationInCarMode[portraitRotation] = |
| mNavigationBarHeightForRotationInCarMode[upsideDownRotation] = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_height_car_mode); |
| mNavigationBarHeightForRotationInCarMode[landscapeRotation] = |
| mNavigationBarHeightForRotationInCarMode[seascapeRotation] = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_height_landscape_car_mode); |
| |
| // Width of the navigation bar when presented vertically along one side |
| mNavigationBarWidthForRotationInCarMode[portraitRotation] = |
| mNavigationBarWidthForRotationInCarMode[upsideDownRotation] = |
| mNavigationBarWidthForRotationInCarMode[landscapeRotation] = |
| mNavigationBarWidthForRotationInCarMode[seascapeRotation] = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); |
| } |
| |
| mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode); |
| mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res); |
| mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res); |
| mNavButtonForcedVisible = |
| mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible(); |
| mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough); |
| mNavigationBarAlwaysShowOnSideGesture = |
| res.getBoolean(R.bool.config_navBarAlwaysShowOnSideEdgeGesture); |
| |
| // This should calculate how much above the frame we accept gestures. |
| mBottomGestureAdditionalInset = |
| res.getDimensionPixelSize(R.dimen.navigation_bar_gesture_height) |
| - getNavigationBarFrameHeight(portraitRotation, uiMode); |
| |
| updateConfigurationAndScreenSizeDependentBehaviors(); |
| } |
| |
| void updateConfigurationAndScreenSizeDependentBehaviors() { |
| final Resources res = getCurrentUserResources(); |
| mNavigationBarCanMove = |
| mDisplayContent.mBaseDisplayWidth != mDisplayContent.mBaseDisplayHeight |
| && res.getBoolean(R.bool.config_navBarCanMove); |
| mDisplayContent.getDisplayRotation().updateUserDependentConfiguration(res); |
| } |
| |
| /** |
| * Updates the current user's resources to pick up any changes for the current user (including |
| * overlay paths) |
| */ |
| private void updateCurrentUserResources() { |
| final int userId = mService.mAmInternal.getCurrentUserId(); |
| final Context uiContext = getSystemUiContext(); |
| |
| if (userId == UserHandle.USER_SYSTEM) { |
| // Skip the (expensive) recreation of resources for the system user below and just |
| // use the resources from the system ui context |
| mCurrentUserResources = uiContext.getResources(); |
| return; |
| } |
| |
| // For non-system users, ensure that the resources are loaded from the current |
| // user's package info (see ContextImpl.createDisplayContext) |
| final LoadedApk pi = ActivityThread.currentActivityThread().getPackageInfo( |
| uiContext.getPackageName(), null, 0, userId); |
| mCurrentUserResources = ResourcesManager.getInstance().getResources(null, |
| pi.getResDir(), |
| null /* splitResDirs */, |
| pi.getOverlayDirs(), |
| pi.getApplicationInfo().sharedLibraryFiles, |
| mDisplayContent.getDisplayId(), |
| null /* overrideConfig */, |
| uiContext.getResources().getCompatibilityInfo(), |
| null /* classLoader */, |
| null /* loaders */); |
| } |
| |
| @VisibleForTesting |
| Resources getCurrentUserResources() { |
| if (mCurrentUserResources == null) { |
| updateCurrentUserResources(); |
| } |
| return mCurrentUserResources; |
| } |
| |
| @VisibleForTesting |
| Context getContext() { |
| return mContext; |
| } |
| |
| Context getSystemUiContext() { |
| return mUiContext; |
| } |
| |
| private int getNavigationBarWidth(int rotation, int uiMode) { |
| if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { |
| return mNavigationBarWidthForRotationInCarMode[rotation]; |
| } else { |
| return mNavigationBarWidthForRotationDefault[rotation]; |
| } |
| } |
| |
| void notifyDisplayReady() { |
| mHandler.post(() -> { |
| final int displayId = getDisplayId(); |
| getStatusBarManagerInternal().onDisplayReady(displayId); |
| final WallpaperManagerInternal wpMgr = LocalServices |
| .getService(WallpaperManagerInternal.class); |
| if (wpMgr != null) { |
| wpMgr.onDisplayReady(displayId); |
| } |
| }); |
| } |
| |
| /** |
| * Return the display width available after excluding any screen |
| * decorations that could never be removed in Honeycomb. That is, system bar or |
| * button bar. |
| */ |
| public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, |
| DisplayCutout displayCutout) { |
| int width = fullWidth; |
| if (hasNavigationBar()) { |
| final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); |
| if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) { |
| width -= getNavigationBarWidth(rotation, uiMode); |
| } |
| } |
| if (displayCutout != null) { |
| width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight(); |
| } |
| return width; |
| } |
| |
| private int getNavigationBarHeight(int rotation, int uiMode) { |
| if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { |
| return mNavigationBarHeightForRotationInCarMode[rotation]; |
| } else { |
| return mNavigationBarHeightForRotationDefault[rotation]; |
| } |
| } |
| |
| /** |
| * Get the Navigation Bar Frame height. This dimension is the height of the navigation bar that |
| * is used for spacing to show additional buttons on the navigation bar (such as the ime |
| * switcher when ime is visible) while {@link #getNavigationBarHeight} is used for the visible |
| * height that we send to the app as content insets that can be smaller. |
| * <p> |
| * In car mode it will return the same height as {@link #getNavigationBarHeight} |
| * |
| * @param rotation specifies rotation to return dimension from |
| * @param uiMode to determine if in car mode |
| * @return navigation bar frame height |
| */ |
| private int getNavigationBarFrameHeight(int rotation, int uiMode) { |
| if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { |
| return mNavigationBarHeightForRotationInCarMode[rotation]; |
| } else { |
| return mNavigationBarFrameHeightForRotationDefault[rotation]; |
| } |
| } |
| |
| /** |
| * Return the display height available after excluding any screen |
| * decorations that could never be removed in Honeycomb. That is, system bar or |
| * button bar. |
| */ |
| public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, |
| DisplayCutout displayCutout) { |
| int height = fullHeight; |
| if (hasNavigationBar()) { |
| final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); |
| if (navBarPosition == NAV_BAR_BOTTOM) { |
| height -= getNavigationBarHeight(rotation, uiMode); |
| } |
| } |
| if (displayCutout != null) { |
| height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom(); |
| } |
| return height; |
| } |
| |
| /** |
| * Return the available screen width that we should report for the |
| * configuration. This must be no larger than |
| * {@link #getNonDecorDisplayWidth(int, int, int, int, DisplayCutout)}; it may be smaller |
| * than that to account for more transient decoration like a status bar. |
| */ |
| public int getConfigDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode, |
| DisplayCutout displayCutout) { |
| return getNonDecorDisplayWidth(fullWidth, fullHeight, rotation, uiMode, displayCutout); |
| } |
| |
| /** |
| * Return the available screen height that we should report for the |
| * configuration. This must be no larger than |
| * {@link #getNonDecorDisplayHeight(int, int, int, int, DisplayCutout)}; it may be smaller |
| * than that to account for more transient decoration like a status bar. |
| */ |
| public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode, |
| DisplayCutout displayCutout) { |
| // There is a separate status bar at the top of the display. We don't count that as part |
| // of the fixed decor, since it can hide; however, for purposes of configurations, |
| // we do want to exclude it since applications can't generally use that part |
| // of the screen. |
| int statusBarHeight = mStatusBarHeightForRotation[rotation]; |
| if (displayCutout != null) { |
| // If there is a cutout, it may already have accounted for some part of the status |
| // bar height. |
| statusBarHeight = Math.max(0, statusBarHeight - displayCutout.getSafeInsetTop()); |
| } |
| return getNonDecorDisplayHeight(fullWidth, fullHeight, rotation, uiMode, displayCutout) |
| - statusBarHeight; |
| } |
| |
| /** |
| * Return corner radius in pixels that should be used on windows in order to cover the display. |
| * |
| * The radius is only valid for internal displays, since the corner radius of external displays |
| * is not known at build time when window corners are configured. |
| */ |
| float getWindowCornerRadius() { |
| return mDisplayContent.getDisplay().getType() == TYPE_INTERNAL |
| ? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f; |
| } |
| |
| boolean isShowingDreamLw() { |
| return mShowingDream; |
| } |
| |
| /** |
| * Calculates the stable insets if we already have the non-decor insets. |
| * |
| * @param inOutInsets The known non-decor insets. It will be modified to stable insets. |
| * @param rotation The current display rotation. |
| */ |
| void convertNonDecorInsetsToStableInsets(Rect inOutInsets, int rotation) { |
| inOutInsets.top = Math.max(inOutInsets.top, mStatusBarHeightForRotation[rotation]); |
| } |
| |
| /** |
| * Calculates the stable insets without running a layout. |
| * |
| * @param displayRotation the current display rotation |
| * @param displayWidth the current display width |
| * @param displayHeight the current display height |
| * @param displayCutout the current display cutout |
| * @param outInsets the insets to return |
| */ |
| public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight, |
| DisplayCutout displayCutout, Rect outInsets) { |
| outInsets.setEmpty(); |
| |
| // Navigation bar and status bar. |
| getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets); |
| convertNonDecorInsetsToStableInsets(outInsets, displayRotation); |
| } |
| |
| /** |
| * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system |
| * bar or button bar. See {@link #getNonDecorDisplayWidth}. |
| * |
| * @param displayRotation the current display rotation |
| * @param displayWidth the current display width |
| * @param displayHeight the current display height |
| * @param displayCutout the current display cutout |
| * @param outInsets the insets to return |
| */ |
| public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight, |
| DisplayCutout displayCutout, Rect outInsets) { |
| outInsets.setEmpty(); |
| |
| // Only navigation bar |
| if (hasNavigationBar()) { |
| final int uiMode = mService.mPolicy.getUiMode(); |
| int position = navigationBarPosition(displayWidth, displayHeight, displayRotation); |
| if (position == NAV_BAR_BOTTOM) { |
| outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode); |
| } else if (position == NAV_BAR_RIGHT) { |
| outInsets.right = getNavigationBarWidth(displayRotation, uiMode); |
| } else if (position == NAV_BAR_LEFT) { |
| outInsets.left = getNavigationBarWidth(displayRotation, uiMode); |
| } |
| } |
| |
| if (displayCutout != null) { |
| outInsets.left += displayCutout.getSafeInsetLeft(); |
| outInsets.top += displayCutout.getSafeInsetTop(); |
| outInsets.right += displayCutout.getSafeInsetRight(); |
| outInsets.bottom += displayCutout.getSafeInsetBottom(); |
| } |
| } |
| |
| /** |
| * @see IWindowManager#setForwardedInsets |
| */ |
| public void setForwardedInsets(@NonNull Insets forwardedInsets) { |
| mForwardedInsets = forwardedInsets; |
| } |
| |
| @NonNull |
| public Insets getForwardedInsets() { |
| return mForwardedInsets; |
| } |
| |
| @NavigationBarPosition |
| int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { |
| if (navigationBarCanMove() && displayWidth > displayHeight) { |
| if (displayRotation == Surface.ROTATION_270) { |
| return NAV_BAR_LEFT; |
| } else if (displayRotation == Surface.ROTATION_90) { |
| return NAV_BAR_RIGHT; |
| } |
| } |
| return NAV_BAR_BOTTOM; |
| } |
| |
| /** |
| * @return The side of the screen where navigation bar is positioned. |
| * @see WindowManagerPolicyConstants#NAV_BAR_LEFT |
| * @see WindowManagerPolicyConstants#NAV_BAR_RIGHT |
| * @see WindowManagerPolicyConstants#NAV_BAR_BOTTOM |
| */ |
| @NavigationBarPosition |
| public int getNavBarPosition() { |
| return mNavigationBarPosition; |
| } |
| |
| @WindowManagerPolicy.AltBarPosition |
| int getAlternateStatusBarPosition() { |
| return mStatusBarAltPosition; |
| } |
| |
| @WindowManagerPolicy.AltBarPosition |
| int getAlternateNavBarPosition() { |
| return mNavigationBarAltPosition; |
| } |
| |
| /** |
| * A new window has been focused. |
| */ |
| public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { |
| mFocusedWindow = newFocus; |
| mLastFocusedWindow = lastFocus; |
| if (mDisplayContent.isDefaultDisplay) { |
| mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus); |
| } |
| if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) { |
| // If the navigation bar has been hidden or shown, we need to do another |
| // layout pass to update that window. |
| return FINISH_LAYOUT_REDO_LAYOUT; |
| } |
| return 0; |
| } |
| |
| private void requestTransientBars(WindowState swipeTarget) { |
| if (!mService.mPolicy.isUserSetupComplete()) { |
| // Swipe-up for navigation bar is disabled during setup |
| return; |
| } |
| if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { |
| final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider(); |
| final InsetsControlTarget controlTarget = provider != null |
| ? provider.getControlTarget() : null; |
| |
| if (controlTarget == null || controlTarget == getNotificationShade()) { |
| // No transient mode on lockscreen (in notification shade window). |
| return; |
| } |
| |
| final InsetsState requestedState = controlTarget.getRequestedInsetsState(); |
| final InsetsSource nbSource = requestedState.peekSource(ITYPE_NAVIGATION_BAR); |
| final InsetsSource sbSource = requestedState.peekSource(ITYPE_STATUS_BAR); |
| final InsetsSource enbSource = requestedState.peekSource(ITYPE_EXTRA_NAVIGATION_BAR); |
| final InsetsSource cbSource = requestedState.peekSource(ITYPE_CLIMATE_BAR); |
| final @InsetsType int restorePositionTypes = |
| (nbSource != null && nbSource.isVisible() ? Type.navigationBars() : 0) |
| | (sbSource != null && sbSource.isVisible() ? Type.statusBars() : 0) |
| | (mExtraNavBarAlt != null && enbSource != null && enbSource.isVisible() |
| ? Type.navigationBars() : 0) |
| | (mClimateBarAlt != null && cbSource != null && cbSource.isVisible() |
| ? Type.statusBars() : 0); |
| |
| if (swipeTarget == mNavigationBar |
| && (restorePositionTypes & Type.navigationBars()) != 0) { |
| // Don't show status bar when swiping on already visible navigation bar. |
| // But restore the position of navigation bar if it has been moved by the control |
| // target. |
| controlTarget.showInsets(Type.navigationBars(), false); |
| return; |
| } |
| |
| if (controlTarget.canShowTransient()) { |
| // Show transient bars if they are hidden; restore position if they are visible. |
| mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE); |
| controlTarget.showInsets(restorePositionTypes, false); |
| } else { |
| // Restore visibilities and positions of system bars. |
| controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false); |
| } |
| } else { |
| boolean sb = mStatusBarController.checkShowTransientBarLw(); |
| boolean nb = mNavigationBarController.checkShowTransientBarLw() |
| && !isNavBarEmpty(mLastSystemUiFlags); |
| if (sb || nb) { |
| // Don't show status bar when swiping on already visible navigation bar |
| if (!nb && swipeTarget == mNavigationBar) { |
| if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target"); |
| return; |
| } |
| if (sb) mStatusBarController.showTransient(); |
| if (nb) mNavigationBarController.showTransient(); |
| updateSystemUiVisibilityLw(); |
| } |
| } |
| mImmersiveModeConfirmation.confirmCurrentPrompt(); |
| } |
| |
| private void disposeInputConsumer(InputConsumer inputConsumer) { |
| if (inputConsumer != null) { |
| inputConsumer.dispose(); |
| } |
| } |
| |
| boolean isKeyguardShowing() { |
| return mService.mPolicy.isKeyguardShowing(); |
| } |
| private boolean isKeyguardOccluded() { |
| // TODO (b/113840485): Handle per display keyguard. |
| return mService.mPolicy.isKeyguardOccluded(); |
| } |
| |
| InsetsPolicy getInsetsPolicy() { |
| return mDisplayContent.getInsetsPolicy(); |
| } |
| |
| void resetSystemUiVisibilityLw() { |
| mLastSystemUiFlags = 0; |
| updateSystemUiVisibilityLw(); |
| } |
| |
| int updateSystemUiVisibilityLw() { |
| // If there is no window focused, there will be nobody to handle the events |
| // anyway, so just hang on in whatever state we're in until things settle down. |
| WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow |
| : mTopFullscreenOpaqueWindowState; |
| if (winCandidate == null) { |
| return 0; |
| } |
| |
| // The 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()) { |
| |
| // The immersive mode confirmation took the focus from mLastFocusedWindow which was |
| // controlling the system ui visibility. So if mLastFocusedWindow can still receive |
| // keys, we let it keep controlling the visibility. |
| final boolean lastFocusCanReceiveKeys = |
| (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()); |
| winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade |
| : lastFocusCanReceiveKeys ? mLastFocusedWindow |
| : mTopFullscreenOpaqueWindowState; |
| if (winCandidate == null) { |
| return 0; |
| } |
| } |
| final WindowState win = winCandidate; |
| |
| mDisplayContent.getInsetsPolicy().updateBarControlTarget(win); |
| |
| int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null) |
| & ~mResettingSystemUiFlags |
| & ~mForceClearedSystemUiFlags; |
| if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) { |
| tmpVisibility |
| &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS); |
| } |
| |
| final int fullscreenAppearance = updateLightStatusBarAppearanceLw(0 /* vis */, |
| mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState); |
| final int dockedAppearance = updateLightStatusBarAppearanceLw(0 /* vis */, |
| mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState); |
| final boolean inSplitScreen = |
| mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated(); |
| if (inSplitScreen) { |
| mService.getStackBounds(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, |
| mDockedStackBounds); |
| } else { |
| mDockedStackBounds.setEmpty(); |
| } |
| mService.getStackBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY |
| : WINDOWING_MODE_FULLSCREEN, |
| ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds); |
| final Pair<Integer, WindowState> result = |
| updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); |
| final int visibility = result.first; |
| final WindowState navColorWin = result.second; |
| final boolean isNavbarColorManagedByIme = |
| navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow; |
| final int opaqueAppearance = InsetsFlags.getAppearance(visibility) |
| & (APPEARANCE_OPAQUE_STATUS_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS); |
| final int appearance = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL |
| ? updateLightNavigationBarAppearanceLw(win.mAttrs.insetsFlags.appearance, |
| mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, |
| mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance |
| : InsetsFlags.getAppearance(visibility); |
| final int diff = visibility ^ mLastSystemUiFlags; |
| final InsetsPolicy insetsPolicy = getInsetsPolicy(); |
| final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) != 0 |
| || (PolicyControl.getWindowFlags(win, win.mAttrs) & FLAG_FULLSCREEN) != 0 |
| || (getStatusBar() != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR)) |
| || (getNavigationBar() != null && insetsPolicy.isHidden( |
| ITYPE_NAVIGATION_BAR)); |
| final int behavior = win.mAttrs.insetsFlags.behavior; |
| final boolean isImmersive = (visibility & (View.SYSTEM_UI_FLAG_IMMERSIVE |
| | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)) != 0 |
| || behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE |
| || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; |
| if (diff == 0 |
| && mLastAppearance == appearance |
| && mLastFullscreenAppearance == fullscreenAppearance |
| && mLastDockedAppearance == dockedAppearance |
| && mLastBehavior == behavior |
| && mLastFocusIsFullscreen == isFullscreen |
| && mLastFocusIsImmersive == isImmersive |
| && mLastNonDockedStackBounds.equals(mNonDockedStackBounds) |
| && mLastDockedStackBounds.equals(mDockedStackBounds)) { |
| return 0; |
| } |
| |
| // Obtains which types should show transient and which types should abort transient. |
| // If there is no transient state change, this pair will contain two empty arrays. |
| final Pair<int[], int[]> transientState = getTransientState(visibility, mLastSystemUiFlags); |
| |
| mLastSystemUiFlags = visibility; |
| mLastAppearance = appearance; |
| mLastFullscreenAppearance = fullscreenAppearance; |
| mLastDockedAppearance = dockedAppearance; |
| mLastBehavior = behavior; |
| mLastFocusIsFullscreen = isFullscreen; |
| mLastFocusIsImmersive = isImmersive; |
| mLastNonDockedStackBounds.set(mNonDockedStackBounds); |
| mLastDockedStackBounds.set(mDockedStackBounds); |
| final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); |
| final Rect dockedStackBounds = new Rect(mDockedStackBounds); |
| final AppearanceRegion[] appearanceRegions = inSplitScreen |
| ? new AppearanceRegion[]{ |
| new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds), |
| new AppearanceRegion(dockedAppearance, dockedStackBounds)} |
| : new AppearanceRegion[]{ |
| new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds)}; |
| String cause = win.toString(); |
| mHandler.post(() -> { |
| StatusBarManagerInternal statusBar = getStatusBarManagerInternal(); |
| if (statusBar != null) { |
| final int displayId = getDisplayId(); |
| statusBar.setDisableFlags(displayId, visibility & StatusBarManager.DISABLE_MASK, |
| cause); |
| if (transientState.first.length > 0) { |
| statusBar.showTransient(displayId, transientState.first); |
| } |
| if (transientState.second.length > 0) { |
| statusBar.abortTransient(displayId, transientState.second); |
| } |
| statusBar.onSystemBarAppearanceChanged(displayId, appearance, |
| appearanceRegions, isNavbarColorManagedByIme); |
| statusBar.topAppWindowChanged(displayId, isFullscreen, isImmersive); |
| |
| // TODO(b/118118435): Remove this after removing system UI visibilities. |
| synchronized (mLock) { |
| mDisplayContent.statusBarVisibilityChanged( |
| visibility & ~(View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE)); |
| } |
| } |
| }); |
| return diff; |
| } |
| |
| private static Pair<int[], int[]> getTransientState(int vis, int oldVis) { |
| final IntArray typesToShow = new IntArray(0); |
| final IntArray typesToAbort = new IntArray(0); |
| updateTransientState(vis, oldVis, View.STATUS_BAR_TRANSIENT, ITYPE_STATUS_BAR, typesToShow, |
| typesToAbort); |
| updateTransientState(vis, oldVis, View.NAVIGATION_BAR_TRANSIENT, |
| ITYPE_NAVIGATION_BAR, typesToShow, typesToAbort); |
| return Pair.create(typesToShow.toArray(), typesToAbort.toArray()); |
| } |
| |
| private static void updateTransientState(int vis, int oldVis, int transientFlag, |
| @InternalInsetsType int type, IntArray typesToShow, IntArray typesToAbort) { |
| final boolean wasTransient = (oldVis & transientFlag) != 0; |
| final boolean isTransient = (vis & transientFlag) != 0; |
| if (!wasTransient && isTransient) { |
| typesToShow.add(type); |
| } else if (wasTransient && !isTransient) { |
| typesToAbort.add(type); |
| } |
| } |
| |
| private int updateLightStatusBarAppearanceLw(@Appearance int appearance, WindowState opaque, |
| WindowState opaqueOrDimming) { |
| final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded(); |
| final WindowState statusColorWin = onKeyguard ? mNotificationShade : opaqueOrDimming; |
| if (statusColorWin != null) { |
| if (statusColorWin == opaque || onKeyguard) { |
| // If the top fullscreen-or-dimming window is also the top fullscreen, respect |
| // its light flag. |
| appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; |
| final int legacyAppearance = InsetsFlags.getAppearance( |
| PolicyControl.getSystemUiVisibility(statusColorWin, null)); |
| appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance) |
| & APPEARANCE_LIGHT_STATUS_BARS; |
| } else if (statusColorWin.isDimming()) { |
| // Otherwise if it's dimming, clear the light flag. |
| appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; |
| } |
| if (!mStatusBarController.isLightAppearanceAllowed(statusColorWin)) { |
| appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; |
| } |
| } |
| return appearance; |
| } |
| |
| @VisibleForTesting |
| @Nullable |
| static WindowState chooseNavigationColorWindowLw(WindowState opaque, |
| WindowState opaqueOrDimming, WindowState imeWindow, |
| @NavigationBarPosition int navBarPosition) { |
| // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME |
| // window can be navigation color window. |
| final boolean imeWindowCanNavColorWindow = imeWindow != null |
| && imeWindow.isVisibleLw() |
| && navBarPosition == NAV_BAR_BOTTOM |
| && (PolicyControl.getWindowFlags(imeWindow, null) |
| & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; |
| |
| if (opaque != null && opaqueOrDimming == opaque) { |
| // If the top fullscreen-or-dimming window is also the top fullscreen, respect it |
| // unless IME window is also eligible, since currently the IME window is always show |
| // above the opaque fullscreen app window, regardless of the IME target window. |
| // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed. |
| return imeWindowCanNavColorWindow ? imeWindow : opaque; |
| } |
| |
| if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) { |
| // No dimming window is involved. Determine the result only with the IME window. |
| return imeWindowCanNavColorWindow ? imeWindow : null; |
| } |
| |
| if (!imeWindowCanNavColorWindow) { |
| // No IME window is involved. Determine the result only with opaqueOrDimming. |
| return opaqueOrDimming; |
| } |
| |
| // The IME window and the dimming window are competing. Check if the dimming window can be |
| // IME target or not. |
| if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) { |
| // The IME window is above the dimming window. |
| return imeWindow; |
| } else { |
| // The dimming window is above the IME window. |
| return opaqueOrDimming; |
| } |
| } |
| |
| @VisibleForTesting |
| static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming, |
| WindowState imeWindow, WindowState navColorWin) { |
| |
| if (navColorWin != null) { |
| if (navColorWin == imeWindow || navColorWin == opaque) { |
| // Respect the light flag. |
| vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; |
| vis |= PolicyControl.getSystemUiVisibility(navColorWin, null) |
| & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; |
| } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) { |
| // Clear the light flag for dimming window. |
| vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; |
| } |
| } |
| return vis; |
| } |
| |
| private int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque, |
| WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) { |
| |
| if (navColorWin != null) { |
| if (navColorWin == imeWindow || navColorWin == opaque) { |
| // Respect the light flag. |
| appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; |
| appearance |= navColorWin.mAttrs.insetsFlags.appearance |
| & APPEARANCE_LIGHT_NAVIGATION_BARS; |
| } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) { |
| // Clear the light flag for dimming window. |
| appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; |
| } |
| if (!mNavigationBarController.isLightAppearanceAllowed(navColorWin)) { |
| appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; |
| } |
| } |
| return appearance; |
| } |
| |
| private Pair<Integer, WindowState> updateSystemBarsLw(WindowState win, int oldVis, int vis) { |
| final boolean dockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea() |
| .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); |
| final boolean freeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea() |
| .isStackVisible(WINDOWING_MODE_FREEFORM); |
| final boolean resizing = mDisplayContent.getDockedDividerController().isResizing(); |
| |
| // We need to force system bars when the docked stack is visible, when the freeform stack |
| // is focused but also when we are resizing for the transitions when docked stack |
| // visibility changes. |
| mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing; |
| final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing(); |
| |
| // apply translucent bar vis flags |
| WindowState fullscreenTransWin = isKeyguardShowing() && !isKeyguardOccluded() |
| ? mNotificationShade |
| : mTopFullscreenOpaqueWindowState; |
| vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); |
| vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis); |
| int dockedVis = mStatusBarController.applyTranslucentFlagLw( |
| mTopDockedOpaqueWindowState, 0, 0); |
| dockedVis = mNavigationBarController.applyTranslucentFlagLw( |
| mTopDockedOpaqueWindowState, dockedVis, 0); |
| |
| final boolean fullscreenDrawsStatusBarBackground = |
| drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState); |
| final boolean dockedDrawsStatusBarBackground = |
| drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState); |
| final boolean fullscreenDrawsNavBarBackground = |
| drawsNavigationBarBackground(vis, mTopFullscreenOpaqueWindowState); |
| final boolean dockedDrawsNavigationBarBackground = |
| drawsNavigationBarBackground(dockedVis, mTopDockedOpaqueWindowState); |
| |
| // prevent status bar interaction from clearing certain flags |
| int type = win.getAttrs().type; |
| boolean notificationShadeHasFocus = type == TYPE_NOTIFICATION_SHADE; |
| if (notificationShadeHasFocus && !isKeyguardShowing()) { |
| int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
| | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
| | View.SYSTEM_UI_FLAG_IMMERSIVE |
| | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
| | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; |
| if (isKeyguardOccluded()) { |
| flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT; |
| } |
| vis = (vis & ~flags) | (oldVis & flags); |
| } |
| |
| if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) { |
| vis |= View.STATUS_BAR_TRANSPARENT; |
| vis &= ~View.STATUS_BAR_TRANSLUCENT; |
| } else if (forceOpaqueStatusBar) { |
| vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT); |
| } |
| |
| vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing, |
| fullscreenDrawsNavBarBackground, dockedDrawsNavigationBarBackground); |
| |
| // update status bar |
| boolean immersiveSticky = |
| (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; |
| final boolean hideStatusBarWM = |
| mTopFullscreenOpaqueWindowState != null |
| && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null) |
| & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; |
| final boolean hideStatusBarSysui = |
| (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0; |
| final boolean hideNavBarSysui = |
| (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; |
| |
| final boolean transientStatusBarAllowed = getStatusBar() != null |
| && (notificationShadeHasFocus || (!mForceShowSystemBars |
| && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky)))); |
| |
| final boolean transientNavBarAllowed = mNavigationBar != null |
| && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky; |
| |
| final long now = SystemClock.uptimeMillis(); |
| final boolean pendingPanic = mPendingPanicGestureUptime != 0 |
| && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION; |
| final DisplayPolicy defaultDisplayPolicy = |
| mService.getDefaultDisplayContentLocked().getDisplayPolicy(); |
| if (pendingPanic && hideNavBarSysui && !isKeyguardShowing() |
| // TODO (b/111955725): Show keyguard presentation on all external displays |
| && defaultDisplayPolicy.isKeyguardDrawComplete()) { |
| // The user performed the panic gesture recently, we're about to hide the bars, |
| // we're no longer on the Keyguard and the screen is ready. We can now request the bars. |
| mPendingPanicGestureUptime = 0; |
| mStatusBarController.showTransient(); |
| if (!isNavBarEmpty(vis)) { |
| mNavigationBarController.showTransient(); |
| } |
| } |
| |
| final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested() |
| && !transientStatusBarAllowed && hideStatusBarSysui; |
| final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested() |
| && !transientNavBarAllowed; |
| if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) { |
| // clear the clearable flags instead |
| clearClearableFlagsLw(); |
| vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS; |
| } |
| |
| final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; |
| immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; |
| final boolean navAllowedHidden = immersive || immersiveSticky; |
| |
| if (hideNavBarSysui && !navAllowedHidden |
| && mService.mPolicy.getWindowLayerLw(win) |
| > mService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) { |
| // We can't hide the navbar from this window otherwise the input consumer would not get |
| // the input events. |
| vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); |
| } |
| |
| vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis); |
| |
| // update navigation bar |
| boolean newInsetsMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL; |
| boolean oldImmersiveMode = newInsetsMode ? mLastImmersiveMode : isImmersiveMode(oldVis); |
| boolean newImmersiveMode = newInsetsMode ? isImmersiveMode(win) : isImmersiveMode(vis); |
| if (oldImmersiveMode != newImmersiveMode) { |
| mLastImmersiveMode = newImmersiveMode; |
| final String pkg = win.getOwningPackage(); |
| mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode, |
| mService.mPolicy.isUserSetupComplete(), |
| isNavBarEmpty(win.getSystemUiVisibility())); |
| } |
| |
| vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis); |
| |
| final WindowState navColorWin = chooseNavigationColorWindowLw( |
| mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, |
| mDisplayContent.mInputMethodWindow, mNavigationBarPosition); |
| vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState, |
| mTopFullscreenOpaqueOrDimmingWindowState, |
| mDisplayContent.mInputMethodWindow, navColorWin); |
| |
| return Pair.create(vis, navColorWin); |
| } |
| |
| private boolean drawsBarBackground(int vis, WindowState win, BarController controller, |
| int translucentFlag) { |
| if (!controller.isTransparentAllowed(win)) { |
| return false; |
| } |
| if (win == null) { |
| return true; |
| } |
| |
| final boolean drawsSystemBars = |
| (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; |
| final boolean forceDrawsSystemBars = |
| (win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; |
| |
| return forceDrawsSystemBars || drawsSystemBars && (vis & translucentFlag) == 0; |
| } |
| |
| private boolean drawsStatusBarBackground(int vis, WindowState win) { |
| return drawsBarBackground(vis, win, mStatusBarController, FLAG_TRANSLUCENT_STATUS); |
| } |
| |
| private boolean drawsNavigationBarBackground(int vis, WindowState win) { |
| return drawsBarBackground(vis, win, mNavigationBarController, FLAG_TRANSLUCENT_NAVIGATION); |
| } |
| |
| /** |
| * @return the current visibility flags with the nav-bar opacity related flags toggled based |
| * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}. |
| */ |
| private int configureNavBarOpacity(int visibility, boolean dockedStackVisible, |
| boolean freeformStackVisible, boolean isDockedDividerResizing, |
| boolean fullscreenDrawsBackground, boolean dockedDrawsNavigationBarBackground) { |
| if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) { |
| if (fullscreenDrawsBackground && dockedDrawsNavigationBarBackground) { |
| visibility = setNavBarTransparentFlag(visibility); |
| } else if (dockedStackVisible) { |
| visibility = setNavBarOpaqueFlag(visibility); |
| } |
| } else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) { |
| if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) { |
| if (mIsFreeformWindowOverlappingWithNavBar) { |
| visibility = setNavBarTranslucentFlag(visibility); |
| } else { |
| visibility = setNavBarOpaqueFlag(visibility); |
| } |
| } else if (fullscreenDrawsBackground) { |
| visibility = setNavBarTransparentFlag(visibility); |
| } |
| } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) { |
| if (isDockedDividerResizing) { |
| visibility = setNavBarOpaqueFlag(visibility); |
| } else if (freeformStackVisible) { |
| visibility = setNavBarTranslucentFlag(visibility); |
| } else { |
| visibility = setNavBarOpaqueFlag(visibility); |
| } |
| } |
| |
| return visibility; |
| } |
| |
| private int setNavBarOpaqueFlag(int visibility) { |
| return visibility & ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT); |
| } |
| |
| private int setNavBarTranslucentFlag(int visibility) { |
| visibility &= ~View.NAVIGATION_BAR_TRANSPARENT; |
| return visibility | View.NAVIGATION_BAR_TRANSLUCENT; |
| } |
| |
| private int setNavBarTransparentFlag(int visibility) { |
| visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT; |
| return visibility | View.NAVIGATION_BAR_TRANSPARENT; |
| } |
| |
| private void clearClearableFlagsLw() { |
| int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS; |
| if (newVal != mResettingSystemUiFlags) { |
| mResettingSystemUiFlags = newVal; |
| mDisplayContent.reevaluateStatusBarVisibility(); |
| } |
| } |
| |
| // TODO(b/118118435): Remove this after migration |
| private boolean isImmersiveMode(int vis) { |
| final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; |
| return getNavigationBar() != null |
| && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 |
| && (vis & flags) != 0 |
| && canHideNavigationBar(); |
| } |
| |
| private boolean isImmersiveMode(WindowState win) { |
| final int behavior = win.mAttrs.insetsFlags.behavior; |
| return getNavigationBar() != null |
| && canHideNavigationBar() |
| && (behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE |
| || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) |
| && getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR) |
| && win != getNotificationShade() |
| && !win.isActivityTypeDream(); |
| } |
| |
| /** |
| * @return whether the navigation bar can be hidden, e.g. the device has a navigation bar |
| */ |
| private boolean canHideNavigationBar() { |
| return hasNavigationBar(); |
| } |
| |
| private static boolean isNavBarEmpty(int systemUiFlags) { |
| final int disableNavigationBar = (View.STATUS_BAR_DISABLE_HOME |
| | View.STATUS_BAR_DISABLE_BACK |
| | View.STATUS_BAR_DISABLE_RECENT); |
| |
| return (systemUiFlags & disableNavigationBar) == disableNavigationBar; |
| } |
| |
| private final Runnable mHiddenNavPanic = new Runnable() { |
| @Override |
| public void run() { |
| synchronized (mLock) { |
| if (!mService.mPolicy.isUserSetupComplete()) { |
| // Swipe-up for navigation bar is disabled during setup |
| return; |
| } |
| mPendingPanicGestureUptime = SystemClock.uptimeMillis(); |
| if (!isNavBarEmpty(mLastSystemUiFlags)) { |
| mNavigationBarController.showTransient(); |
| mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC); |
| } |
| } |
| } |
| }; |
| |
| 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(mLastSystemUiFlags), |
| isNavBarEmpty(mLastSystemUiFlags)); |
| if (panic) { |
| mHandler.post(mHiddenNavPanic); |
| } |
| } |
| |
| void onVrStateChangedLw(boolean enabled) { |
| mImmersiveModeConfirmation.onVrStateChangedLw(enabled); |
| } |
| |
| /** |
| * Called when the state of lock task mode changes. This should be used to disable immersive |
| * mode confirmation. |
| * |
| * @param lockTaskState the new lock task mode state. One of |
| * {@link ActivityManager#LOCK_TASK_MODE_NONE}, |
| * {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, |
| * {@link ActivityManager#LOCK_TASK_MODE_PINNED}. |
| */ |
| public void onLockTaskStateChangedLw(int lockTaskState) { |
| mImmersiveModeConfirmation.onLockTaskModeChangedLw(lockTaskState); |
| } |
| |
| /** |
| * Request a screenshot be taken. |
| * |
| * @param screenshotType The type of screenshot, for example either |
| * {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or |
| * {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} |
| * @param source Where the screenshot originated from (see WindowManager.ScreenshotSource) |
| */ |
| public void takeScreenshot(int screenshotType, int source) { |
| if (mScreenshotHelper != null) { |
| mScreenshotHelper.takeScreenshot(screenshotType, |
| getStatusBar() != null && getStatusBar().isVisibleLw(), |
| getNavigationBar() != null && getNavigationBar().isVisibleLw(), |
| source, mHandler, null /* completionConsumer */); |
| } |
| } |
| |
| RefreshRatePolicy getRefreshRatePolicy() { |
| return mRefreshRatePolicy; |
| } |
| |
| void dump(String prefix, PrintWriter pw) { |
| pw.print(prefix); pw.println("DisplayPolicy"); |
| prefix += " "; |
| pw.print(prefix); |
| pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer); |
| pw.print(" mDeskDockEnablesAccelerometer="); |
| pw.println(mDeskDockEnablesAccelerometer); |
| pw.print(prefix); pw.print("mDockMode="); pw.print(Intent.dockStateToString(mDockMode)); |
| pw.print(" mLidState="); pw.println(WindowManagerFuncs.lidStateToString(mLidState)); |
| pw.print(prefix); pw.print("mAwake="); pw.print(mAwake); |
| pw.print(" mScreenOnEarly="); pw.print(mScreenOnEarly); |
| pw.print(" mScreenOnFully="); pw.println(mScreenOnFully); |
| pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete); |
| pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete); |
| pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged); |
| if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0 |
| || mForceClearedSystemUiFlags != 0) { |
| pw.print(prefix); pw.print("mLastSystemUiFlags=0x"); |
| pw.print(Integer.toHexString(mLastSystemUiFlags)); |
| pw.print(" mResettingSystemUiFlags=0x"); |
| pw.print(Integer.toHexString(mResettingSystemUiFlags)); |
| pw.print(" mForceClearedSystemUiFlags=0x"); |
| pw.println(Integer.toHexString(mForceClearedSystemUiFlags)); |
| } |
| pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream); |
| pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen); |
| if (mStatusBar != null) { |
| pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar); |
| } |
| if (mStatusBarAlt != null) { |
| pw.print(prefix); pw.print("mStatusBarAlt="); pw.print(mStatusBarAlt); |
| pw.print(prefix); pw.print("mStatusBarAltPosition="); |
| pw.println(mStatusBarAltPosition); |
| } |
| if (mNotificationShade != null) { |
| pw.print(prefix); pw.print("mExpandedPanel="); pw.print(mNotificationShade); |
| } |
| pw.print(" isKeyguardShowing="); pw.println(isKeyguardShowing()); |
| if (mNavigationBar != null) { |
| pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar); |
| pw.print(prefix); pw.print("mNavBarOpacityMode="); pw.println(mNavBarOpacityMode); |
| pw.print(prefix); pw.print("mNavigationBarCanMove="); pw.println(mNavigationBarCanMove); |
| pw.print(prefix); pw.print("mNavigationBarPosition="); |
| pw.println(mNavigationBarPosition); |
| } |
| if (mNavigationBarAlt != null) { |
| pw.print(prefix); pw.print("mNavigationBarAlt="); pw.println(mNavigationBarAlt); |
| pw.print(prefix); pw.print("mNavigationBarAltPosition="); |
| pw.println(mNavigationBarAltPosition); |
| } |
| if (mClimateBarAlt != null) { |
| pw.print(prefix); pw.print("mClimateBarAlt="); pw.println(mClimateBarAlt); |
| pw.print(prefix); pw.print("mClimateBarAltPosition="); |
| pw.println(mClimateBarAltPosition); |
| } |
| if (mExtraNavBarAlt != null) { |
| pw.print(prefix); pw.print("mExtraNavBarAlt="); pw.println(mExtraNavBarAlt); |
| pw.print(prefix); pw.print("mExtraNavBarAltPosition="); |
| pw.println(mExtraNavBarAltPosition); |
| } |
| if (mFocusedWindow != null) { |
| pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow); |
| } |
| if (mTopFullscreenOpaqueWindowState != null) { |
| pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState="); |
| pw.println(mTopFullscreenOpaqueWindowState); |
| } |
| if (mTopFullscreenOpaqueOrDimmingWindowState != null) { |
| pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState="); |
| pw.println(mTopFullscreenOpaqueOrDimmingWindowState); |
| } |
| if (mForcingShowNavBar) { |
| pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar); |
| pw.print(prefix); pw.print("mForcingShowNavBarLayer="); |
| pw.println(mForcingShowNavBarLayer); |
| } |
| pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen); |
| pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar); |
| pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars"); |
| pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars()); |
| pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn); |
| mStatusBarController.dump(pw, prefix); |
| mNavigationBarController.dump(pw, prefix); |
| |
| pw.print(prefix); pw.println("Looper state:"); |
| mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " "); |
| } |
| |
| private boolean supportsPointerLocation() { |
| return mDisplayContent.isDefaultDisplay || !mDisplayContent.isPrivate(); |
| } |
| |
| void setPointerLocationEnabled(boolean pointerLocationEnabled) { |
| if (!supportsPointerLocation()) { |
| return; |
| } |
| |
| mHandler.sendEmptyMessage(pointerLocationEnabled |
| ? MSG_ENABLE_POINTER_LOCATION : MSG_DISABLE_POINTER_LOCATION); |
| } |
| |
| private void enablePointerLocation() { |
| if (mPointerLocationView != null) { |
| return; |
| } |
| |
| mPointerLocationView = new PointerLocationView(mContext); |
| mPointerLocationView.setPrintCoords(false); |
| final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( |
| WindowManager.LayoutParams.MATCH_PARENT, |
| WindowManager.LayoutParams.MATCH_PARENT); |
| lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; |
| lp.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
| | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; |
| lp.setFitInsetsTypes(0); |
| lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; |
| if (ActivityManager.isHighEndGfx()) { |
| lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; |
| lp.privateFlags |= |
| WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; |
| } |
| lp.format = PixelFormat.TRANSLUCENT; |
| lp.setTitle("PointerLocation - display " + getDisplayId()); |
| lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; |
| final WindowManager wm = mContext.getSystemService(WindowManager.class); |
| wm.addView(mPointerLocationView, lp); |
| mDisplayContent.registerPointerEventListener(mPointerLocationView); |
| } |
| |
| private void disablePointerLocation() { |
| if (mPointerLocationView == null) { |
| return; |
| } |
| |
| mDisplayContent.unregisterPointerEventListener(mPointerLocationView); |
| final WindowManager wm = mContext.getSystemService(WindowManager.class); |
| wm.removeView(mPointerLocationView); |
| mPointerLocationView = null; |
| } |
| |
| /** |
| * Check if the window could be excluded from checking if the display has content. |
| * |
| * @param w WindowState to check if should be excluded. |
| * @return True if the window type is PointerLocation which is excluded. |
| */ |
| boolean isWindowExcludedFromContent(WindowState w) { |
| if (w != null && mPointerLocationView != null) { |
| return w.mClient == mPointerLocationView.getWindowToken(); |
| } |
| |
| return false; |
| } |
| |
| void release() { |
| mHandler.post(mGestureNavigationSettingsObserver::unregister); |
| } |
| |
| @VisibleForTesting |
| static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) { |
| if (navBarWindow == null || !navBarWindow.isVisibleLw() |
| || targetWindow.mActivityRecord == null || !targetWindow.isVisibleLw()) { |
| return false; |
| } |
| |
| return Rect.intersects(targetWindow.getFrameLw(), navBarWindow.getFrameLw()); |
| } |
| } |