Merge "Support hide display cutout with display area"
This commit is contained in:
@@ -77,6 +77,12 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
|
||||
*/
|
||||
public static final int FEATURE_FULLSCREEN_MAGNIFICATION = FEATURE_SYSTEM_FIRST + 5;
|
||||
|
||||
/**
|
||||
* Display area for hiding display cutout feature
|
||||
* @hide
|
||||
*/
|
||||
public static final int FEATURE_HIDE_DISPLAY_CUTOUT = FEATURE_SYSTEM_FIRST + 6;
|
||||
|
||||
/**
|
||||
* The last boundary of display area for system features
|
||||
*/
|
||||
|
||||
@@ -4510,4 +4510,7 @@
|
||||
activity-level letterboxing (size-compat mode). Therefore this override can control the
|
||||
maximum screen area that can be occupied by the app in the letterbox mode. -->
|
||||
<item name="config_taskLetterboxAspectRatio" format="float" type="dimen">0.0</item>
|
||||
|
||||
<!-- If true, hide the display cutout with display area -->
|
||||
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
|
||||
</resources>
|
||||
|
||||
@@ -4095,4 +4095,6 @@
|
||||
<java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
|
||||
|
||||
<java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
|
||||
|
||||
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
|
||||
</resources>
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
package com.android.wm.shell;
|
||||
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
@@ -33,16 +32,19 @@ public class ShellDump {
|
||||
private final Optional<SplitScreen> mSplitScreenOptional;
|
||||
private final Optional<Pip> mPipOptional;
|
||||
private final Optional<OneHanded> mOneHandedOptional;
|
||||
private final Optional<HideDisplayCutout> mHideDisplayCutout;
|
||||
private final ShellTaskOrganizer mShellTaskOrganizer;
|
||||
|
||||
public ShellDump(ShellTaskOrganizer shellTaskOrganizer,
|
||||
Optional<SplitScreen> splitScreenOptional,
|
||||
Optional<Pip> pipOptional,
|
||||
Optional<OneHanded> oneHandedOptional) {
|
||||
Optional<OneHanded> oneHandedOptional,
|
||||
Optional<HideDisplayCutout> hideDisplayCutout) {
|
||||
mShellTaskOrganizer = shellTaskOrganizer;
|
||||
mSplitScreenOptional = splitScreenOptional;
|
||||
mPipOptional = pipOptional;
|
||||
mOneHandedOptional = oneHandedOptional;
|
||||
mHideDisplayCutout = hideDisplayCutout;
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
@@ -52,5 +54,6 @@ public class ShellDump {
|
||||
mPipOptional.ifPresent(pip -> pip.dump(pw));
|
||||
mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
|
||||
mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
|
||||
mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.hidedisplaycutout;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Interface to engage hide display cutout feature.
|
||||
*/
|
||||
public interface HideDisplayCutout {
|
||||
/**
|
||||
* Notifies {@link Configuration} changed.
|
||||
* @param newConfig
|
||||
*/
|
||||
void onConfigurationChanged(Configuration newConfig);
|
||||
|
||||
/**
|
||||
* Dumps hide display cutout status.
|
||||
*/
|
||||
void dump(@NonNull PrintWriter pw);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.hidedisplaycutout;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.SystemProperties;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Manages the hide display cutout status.
|
||||
*/
|
||||
public class HideDisplayCutoutController implements HideDisplayCutout {
|
||||
private static final String TAG = "HideDisplayCutoutController";
|
||||
|
||||
private final Context mContext;
|
||||
private final HideDisplayCutoutOrganizer mOrganizer;
|
||||
@VisibleForTesting
|
||||
boolean mEnabled;
|
||||
|
||||
HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer) {
|
||||
mContext = context;
|
||||
mOrganizer = organizer;
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link HideDisplayCutoutController}, returns {@code null} if the feature is not
|
||||
* supported.
|
||||
*/
|
||||
@Nullable
|
||||
public static HideDisplayCutoutController create(
|
||||
Context context, DisplayController displayController) {
|
||||
// The SystemProperty is set for devices that support this feature and is used to control
|
||||
// whether to create the HideDisplayCutout instance.
|
||||
// It's defined in the device.mk (e.g. device/google/crosshatch/device.mk).
|
||||
if (!SystemProperties.getBoolean("ro.support_hide_display_cutout", false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HideDisplayCutoutOrganizer organizer =
|
||||
new HideDisplayCutoutOrganizer(context, displayController);
|
||||
return new HideDisplayCutoutController(context, organizer);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateStatus() {
|
||||
// The config value is used for controlling enabling/disabling status of the feature and is
|
||||
// defined in the config.xml in a "Hide Display Cutout" overlay package (e.g. device/google/
|
||||
// crosshatch/crosshatch/overlay/packages/apps/overlays/NoCutoutOverlay).
|
||||
final boolean enabled = mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_hideDisplayCutoutWithDisplayArea);
|
||||
if (enabled == mEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEnabled = enabled;
|
||||
if (enabled) {
|
||||
mOrganizer.enableHideDisplayCutout();
|
||||
} else {
|
||||
mOrganizer.disableHideDisplayCutout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull PrintWriter pw) {
|
||||
final String prefix = " ";
|
||||
pw.print(TAG);
|
||||
pw.println(" states: ");
|
||||
pw.print(prefix);
|
||||
pw.print("mEnabled=");
|
||||
pw.println(mEnabled);
|
||||
mOrganizer.dump(pw);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.hidedisplaycutout;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.RotationUtils;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.DisplayAreaAppearedInfo;
|
||||
import android.window.DisplayAreaInfo;
|
||||
import android.window.DisplayAreaOrganizer;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.wm.shell.common.DisplayChangeController;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages the display areas of hide display cutout feature.
|
||||
*/
|
||||
class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer {
|
||||
private static final String TAG = "HideDisplayCutoutOrganizer";
|
||||
|
||||
private final Context mContext;
|
||||
private final DisplayController mDisplayController;
|
||||
|
||||
@VisibleForTesting
|
||||
@GuardedBy("this")
|
||||
ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaMap = new ArrayMap();
|
||||
// The default display bound in natural orientation.
|
||||
private final Rect mDefaultDisplayBounds = new Rect();
|
||||
@VisibleForTesting
|
||||
final Rect mCurrentDisplayBounds = new Rect();
|
||||
// The default display cutout in natural orientation.
|
||||
private Insets mDefaultCutoutInsets;
|
||||
private Insets mCurrentCutoutInsets;
|
||||
private boolean mIsDefaultPortrait;
|
||||
private int mStatusBarHeight;
|
||||
@VisibleForTesting
|
||||
int mOffsetX;
|
||||
@VisibleForTesting
|
||||
int mOffsetY;
|
||||
@VisibleForTesting
|
||||
int mRotation;
|
||||
|
||||
/**
|
||||
* Handles rotation based on OnDisplayChangingListener callback.
|
||||
*/
|
||||
private final DisplayChangeController.OnDisplayChangingListener mRotationController =
|
||||
(display, fromRotation, toRotation, wct) -> {
|
||||
mRotation = toRotation;
|
||||
updateBoundsAndOffsets(true /* enable */);
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
applyAllBoundsAndOffsets(wct, t);
|
||||
// Only apply t here since the server will do the wct.apply when the method
|
||||
// finishes.
|
||||
t.apply();
|
||||
};
|
||||
|
||||
HideDisplayCutoutOrganizer(Context context, DisplayController displayController) {
|
||||
mContext = context;
|
||||
mDisplayController = displayController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
|
||||
@NonNull SurfaceControl leash) {
|
||||
if (!addDisplayAreaInfoAndLeashToMap(displayAreaInfo, leash)) {
|
||||
return;
|
||||
}
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
|
||||
applyBoundsAndOffsets(displayAreaInfo.token, leash, wct, tx);
|
||||
applyTransaction(wct, tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
|
||||
synchronized (this) {
|
||||
if (!mDisplayAreaMap.containsKey(displayAreaInfo.token)) {
|
||||
Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
|
||||
return;
|
||||
}
|
||||
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
applyBoundsAndOffsets(
|
||||
displayAreaInfo.token, mDisplayAreaMap.get(displayAreaInfo.token), wct, t);
|
||||
applyTransaction(wct, t);
|
||||
mDisplayAreaMap.remove(displayAreaInfo.token);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplayAreaMap(List<DisplayAreaAppearedInfo> displayAreaInfos) {
|
||||
for (int i = 0; i < displayAreaInfos.size(); i++) {
|
||||
final DisplayAreaInfo info = displayAreaInfos.get(i).getDisplayAreaInfo();
|
||||
final SurfaceControl leash = displayAreaInfos.get(i).getLeash();
|
||||
addDisplayAreaInfoAndLeashToMap(info, leash);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean addDisplayAreaInfoAndLeashToMap(@NonNull DisplayAreaInfo displayAreaInfo,
|
||||
@NonNull SurfaceControl leash) {
|
||||
synchronized (this) {
|
||||
if (displayAreaInfo.displayId != DEFAULT_DISPLAY) {
|
||||
return false;
|
||||
}
|
||||
if (mDisplayAreaMap.containsKey(displayAreaInfo.token)) {
|
||||
Log.w(TAG, "Already appeared token: " + displayAreaInfo.token);
|
||||
return false;
|
||||
}
|
||||
mDisplayAreaMap.put(displayAreaInfo.token, leash);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables hide display cutout.
|
||||
*/
|
||||
void enableHideDisplayCutout() {
|
||||
mDisplayController.addDisplayChangingController(mRotationController);
|
||||
final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
|
||||
if (display != null) {
|
||||
mRotation = display.getRotation();
|
||||
}
|
||||
final List<DisplayAreaAppearedInfo> displayAreaInfos =
|
||||
registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
|
||||
updateDisplayAreaMap(displayAreaInfos);
|
||||
updateBoundsAndOffsets(true /* enabled */);
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
applyAllBoundsAndOffsets(wct, t);
|
||||
applyTransaction(wct, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables hide display cutout.
|
||||
*/
|
||||
void disableHideDisplayCutout() {
|
||||
updateBoundsAndOffsets(false /* enabled */);
|
||||
mDisplayController.removeDisplayChangingController(mRotationController);
|
||||
unregisterOrganizer();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Insets getDisplayCutoutInsetsOfNaturalOrientation() {
|
||||
final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
|
||||
if (display == null) {
|
||||
return Insets.NONE;
|
||||
}
|
||||
DisplayCutout cutout = display.getCutout();
|
||||
Insets insets = cutout != null ? Insets.of(cutout.getSafeInsets()) : Insets.NONE;
|
||||
return mRotation != Surface.ROTATION_0
|
||||
? RotationUtils.rotateInsets(insets, 4 /* total number of rotation */ - mRotation)
|
||||
: insets;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Rect getDisplayBoundsOfNaturalOrientation() {
|
||||
Point realSize = new Point(0, 0);
|
||||
final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
|
||||
if (display != null) {
|
||||
display.getRealSize(realSize);
|
||||
}
|
||||
final boolean isDisplaySizeFlipped = isDisplaySizeFlipped();
|
||||
return new Rect(
|
||||
0,
|
||||
0,
|
||||
isDisplaySizeFlipped ? realSize.y : realSize.x,
|
||||
isDisplaySizeFlipped ? realSize.x : realSize.y);
|
||||
}
|
||||
|
||||
private boolean isDisplaySizeFlipped() {
|
||||
return mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates bounds and offsets according to current state.
|
||||
*
|
||||
* @param enabled whether the hide display cutout feature is enabled.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void updateBoundsAndOffsets(boolean enabled) {
|
||||
if (!enabled) {
|
||||
resetBoundsAndOffsets();
|
||||
} else {
|
||||
initDefaultValuesIfNeeded();
|
||||
|
||||
// Reset to default values.
|
||||
mCurrentDisplayBounds.set(mDefaultDisplayBounds);
|
||||
mOffsetX = 0;
|
||||
mOffsetY = 0;
|
||||
|
||||
// Update bounds and insets according to the rotation.
|
||||
mCurrentCutoutInsets = RotationUtils.rotateInsets(mDefaultCutoutInsets, mRotation);
|
||||
if (isDisplaySizeFlipped()) {
|
||||
mCurrentDisplayBounds.set(
|
||||
mCurrentDisplayBounds.top,
|
||||
mCurrentDisplayBounds.left,
|
||||
mCurrentDisplayBounds.bottom,
|
||||
mCurrentDisplayBounds.right);
|
||||
}
|
||||
mCurrentDisplayBounds.inset(mCurrentCutoutInsets);
|
||||
|
||||
// Replace the top bound with the max(status bar height, cutout height) if there is
|
||||
// cutout on the top side.
|
||||
mStatusBarHeight = getStatusBarHeight();
|
||||
if (mCurrentCutoutInsets.top != 0) {
|
||||
mCurrentDisplayBounds.top = Math.max(mStatusBarHeight, mCurrentCutoutInsets.top);
|
||||
}
|
||||
mOffsetX = mCurrentDisplayBounds.left;
|
||||
mOffsetY = mCurrentDisplayBounds.top;
|
||||
}
|
||||
}
|
||||
|
||||
private void resetBoundsAndOffsets() {
|
||||
mCurrentDisplayBounds.setEmpty();
|
||||
mOffsetX = 0;
|
||||
mOffsetY = 0;
|
||||
}
|
||||
|
||||
private void initDefaultValuesIfNeeded() {
|
||||
if (!mDefaultDisplayBounds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
mDefaultDisplayBounds.set(getDisplayBoundsOfNaturalOrientation());
|
||||
mDefaultCutoutInsets = getDisplayCutoutInsetsOfNaturalOrientation();
|
||||
mIsDefaultPortrait = mDefaultDisplayBounds.width() < mDefaultDisplayBounds.height();
|
||||
}
|
||||
|
||||
private void applyAllBoundsAndOffsets(
|
||||
WindowContainerTransaction wct, SurfaceControl.Transaction t) {
|
||||
synchronized (this) {
|
||||
mDisplayAreaMap.forEach((token, leash) -> {
|
||||
applyBoundsAndOffsets(token, leash, wct, t);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void applyBoundsAndOffsets(WindowContainerToken token, SurfaceControl leash,
|
||||
WindowContainerTransaction wct, SurfaceControl.Transaction t) {
|
||||
wct.setBounds(token, mCurrentDisplayBounds.isEmpty() ? null : mCurrentDisplayBounds);
|
||||
t.setPosition(leash, mOffsetX, mOffsetY);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void applyTransaction(WindowContainerTransaction wct, SurfaceControl.Transaction t) {
|
||||
applyTransaction(wct);
|
||||
t.apply();
|
||||
}
|
||||
|
||||
private int getStatusBarHeight() {
|
||||
final boolean isLandscape =
|
||||
mIsDefaultPortrait ? isDisplaySizeFlipped() : !isDisplaySizeFlipped();
|
||||
return mContext.getResources().getDimensionPixelSize(
|
||||
isLandscape ? R.dimen.status_bar_height_landscape
|
||||
: R.dimen.status_bar_height_portrait);
|
||||
}
|
||||
|
||||
void dump(@NonNull PrintWriter pw) {
|
||||
final String prefix = " ";
|
||||
pw.print(TAG);
|
||||
pw.println(" states: ");
|
||||
synchronized (this) {
|
||||
pw.print(prefix);
|
||||
pw.print("mDisplayAreaMap=");
|
||||
pw.println(mDisplayAreaMap);
|
||||
}
|
||||
pw.print(prefix);
|
||||
pw.print("getDisplayBoundsOfNaturalOrientation()=");
|
||||
pw.println(getDisplayBoundsOfNaturalOrientation());
|
||||
pw.print(prefix);
|
||||
pw.print("mDefaultDisplayBounds=");
|
||||
pw.println(mDefaultDisplayBounds);
|
||||
pw.print(prefix);
|
||||
pw.print("mCurrentDisplayBounds=");
|
||||
pw.println(mCurrentDisplayBounds);
|
||||
pw.print(prefix);
|
||||
pw.print("mDefaultCutoutInsets=");
|
||||
pw.println(mDefaultCutoutInsets);
|
||||
pw.print(prefix);
|
||||
pw.print("mCurrentCutoutInsets=");
|
||||
pw.println(mCurrentCutoutInsets);
|
||||
pw.print(prefix);
|
||||
pw.print("mRotation=");
|
||||
pw.println(mRotation);
|
||||
pw.print(prefix);
|
||||
pw.print("mStatusBarHeight=");
|
||||
pw.println(mStatusBarHeight);
|
||||
pw.print(prefix);
|
||||
pw.print("mOffsetX=");
|
||||
pw.println(mOffsetX);
|
||||
pw.print(prefix);
|
||||
pw.print("mOffsetY=");
|
||||
pw.println(mOffsetY);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ android_test {
|
||||
"mockito-target-extended-minus-junit4",
|
||||
"truth-prebuilt",
|
||||
"testables",
|
||||
"platform-test-annotations",
|
||||
],
|
||||
|
||||
libs: [
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.hidedisplaycutout;
|
||||
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableContext;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class HideDisplayCutoutControllerTest {
|
||||
private TestableContext mContext = new TestableContext(
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
|
||||
|
||||
private HideDisplayCutoutController mHideDisplayCutoutController;
|
||||
@Mock
|
||||
private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mHideDisplayCutoutController = new HideDisplayCutoutController(
|
||||
mContext, mMockDisplayAreaOrganizer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleHideDisplayCutout_On() {
|
||||
mHideDisplayCutoutController.mEnabled = false;
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
com.android.internal.R.bool.config_hideDisplayCutoutWithDisplayArea, true);
|
||||
reset(mMockDisplayAreaOrganizer);
|
||||
mHideDisplayCutoutController.updateStatus();
|
||||
verify(mMockDisplayAreaOrganizer).enableHideDisplayCutout();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleHideDisplayCutout_Off() {
|
||||
mHideDisplayCutoutController.mEnabled = true;
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
com.android.internal.R.bool.config_hideDisplayCutoutWithDisplayArea, false);
|
||||
mHideDisplayCutoutController.updateStatus();
|
||||
verify(mMockDisplayAreaOrganizer).disableHideDisplayCutout();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.hidedisplaycutout;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.window.DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableContext;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.DisplayAreaAppearedInfo;
|
||||
import android.window.DisplayAreaInfo;
|
||||
import android.window.DisplayAreaOrganizer;
|
||||
import android.window.IWindowContainerToken;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
public class HideDisplayCutoutOrganizerTest {
|
||||
private TestableContext mContext = new TestableContext(
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
|
||||
|
||||
@Mock
|
||||
private DisplayController mMockDisplayController;
|
||||
private HideDisplayCutoutOrganizer mOrganizer;
|
||||
|
||||
private DisplayAreaInfo mDisplayAreaInfo;
|
||||
private SurfaceControl mLeash;
|
||||
|
||||
@Mock
|
||||
private Display mDisplay;
|
||||
@Mock
|
||||
private IWindowContainerToken mMockRealToken;
|
||||
private WindowContainerToken mToken;
|
||||
|
||||
private final Rect mFakeDefaultBounds = new Rect(0, 0, 100, 200);
|
||||
private final Insets mFakeDefaultCutoutInsets = Insets.of(0, 10, 0, 0);
|
||||
private final int mFakeStatusBarHeightPortrait = 15;
|
||||
private final int mFakeStatusBarHeightLandscape = 10;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
|
||||
|
||||
HideDisplayCutoutOrganizer organizer = new HideDisplayCutoutOrganizer(
|
||||
mContext, mMockDisplayController);
|
||||
mOrganizer = Mockito.spy(organizer);
|
||||
doNothing().when(mOrganizer).unregisterOrganizer();
|
||||
doNothing().when(mOrganizer).applyBoundsAndOffsets(any(), any(), any(), any());
|
||||
doNothing().when(mOrganizer).applyTransaction(any(), any());
|
||||
|
||||
// It will be called when mDisplayAreaMap.containKey(token) is called.
|
||||
Binder binder = new Binder();
|
||||
doReturn(binder).when(mMockRealToken).asBinder();
|
||||
mToken = new WindowContainerToken(mMockRealToken);
|
||||
mLeash = new SurfaceControl();
|
||||
mDisplayAreaInfo = new DisplayAreaInfo(
|
||||
mToken, DEFAULT_DISPLAY, FEATURE_HIDE_DISPLAY_CUTOUT);
|
||||
mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
|
||||
DisplayAreaAppearedInfo info = new DisplayAreaAppearedInfo(mDisplayAreaInfo, mLeash);
|
||||
ArrayList<DisplayAreaAppearedInfo> infoList = new ArrayList<>();
|
||||
infoList.add(info);
|
||||
doReturn(infoList).when(mOrganizer).registerOrganizer(
|
||||
DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnableHideDisplayCutout() {
|
||||
mOrganizer.enableHideDisplayCutout();
|
||||
|
||||
verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
|
||||
verify(mOrganizer).addDisplayAreaInfoAndLeashToMap(mDisplayAreaInfo, mLeash);
|
||||
verify(mOrganizer).updateBoundsAndOffsets(true);
|
||||
assertThat(mOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo.token)).isTrue();
|
||||
assertThat(mOrganizer.mDisplayAreaMap.containsValue(mLeash)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDisplayAreaAppeared() {
|
||||
mOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
|
||||
|
||||
assertThat(mOrganizer.mDisplayAreaMap.containsKey(mToken)).isTrue();
|
||||
assertThat(mOrganizer.mDisplayAreaMap.containsValue(mLeash)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDisplayAreaVanished() {
|
||||
mOrganizer.mDisplayAreaMap.put(mDisplayAreaInfo.token, mLeash);
|
||||
mOrganizer.onDisplayAreaVanished(mDisplayAreaInfo);
|
||||
|
||||
assertThat(mOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo.token)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleHideDisplayCutout_enable_rot0() {
|
||||
doReturn(mFakeDefaultBounds).when(mOrganizer).getDisplayBoundsOfNaturalOrientation();
|
||||
doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
|
||||
.getDisplayCutoutInsetsOfNaturalOrientation();
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait);
|
||||
doReturn(Surface.ROTATION_0).when(mDisplay).getRotation();
|
||||
mOrganizer.enableHideDisplayCutout();
|
||||
|
||||
verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
|
||||
verify(mOrganizer).addDisplayAreaInfoAndLeashToMap(mDisplayAreaInfo, mLeash);
|
||||
verify(mOrganizer).updateBoundsAndOffsets(true);
|
||||
assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(0, 15, 100, 200));
|
||||
assertThat(mOrganizer.mOffsetX).isEqualTo(0);
|
||||
assertThat(mOrganizer.mOffsetY).isEqualTo(15);
|
||||
assertThat(mOrganizer.mRotation).isEqualTo(Surface.ROTATION_0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleHideDisplayCutout_enable_rot90() {
|
||||
doReturn(mFakeDefaultBounds).when(mOrganizer).getDisplayBoundsOfNaturalOrientation();
|
||||
doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
|
||||
.getDisplayCutoutInsetsOfNaturalOrientation();
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
R.dimen.status_bar_height_landscape, mFakeStatusBarHeightLandscape);
|
||||
doReturn(Surface.ROTATION_90).when(mDisplay).getRotation();
|
||||
mOrganizer.enableHideDisplayCutout();
|
||||
|
||||
verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
|
||||
verify(mOrganizer).addDisplayAreaInfoAndLeashToMap(mDisplayAreaInfo, mLeash);
|
||||
verify(mOrganizer).updateBoundsAndOffsets(true);
|
||||
assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(10, 0, 200, 100));
|
||||
assertThat(mOrganizer.mOffsetX).isEqualTo(10);
|
||||
assertThat(mOrganizer.mOffsetY).isEqualTo(0);
|
||||
assertThat(mOrganizer.mRotation).isEqualTo(Surface.ROTATION_90);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleHideDisplayCutout_enable_rot270() {
|
||||
doReturn(mFakeDefaultBounds).when(mOrganizer).getDisplayBoundsOfNaturalOrientation();
|
||||
doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
|
||||
.getDisplayCutoutInsetsOfNaturalOrientation();
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
R.dimen.status_bar_height_landscape, mFakeStatusBarHeightLandscape);
|
||||
doReturn(Surface.ROTATION_270).when(mDisplay).getRotation();
|
||||
mOrganizer.enableHideDisplayCutout();
|
||||
|
||||
verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
|
||||
verify(mOrganizer).addDisplayAreaInfoAndLeashToMap(mDisplayAreaInfo, mLeash);
|
||||
verify(mOrganizer).updateBoundsAndOffsets(true);
|
||||
assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(0, 0, 190, 100));
|
||||
assertThat(mOrganizer.mOffsetX).isEqualTo(0);
|
||||
assertThat(mOrganizer.mOffsetY).isEqualTo(0);
|
||||
assertThat(mOrganizer.mRotation).isEqualTo(Surface.ROTATION_270);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToggleHideDisplayCutout_disable() {
|
||||
doReturn(mFakeDefaultBounds).when(mOrganizer).getDisplayBoundsOfNaturalOrientation();
|
||||
doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
|
||||
.getDisplayCutoutInsetsOfNaturalOrientation();
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait);
|
||||
mOrganizer.enableHideDisplayCutout();
|
||||
|
||||
// disable hide display cutout
|
||||
mOrganizer.disableHideDisplayCutout();
|
||||
verify(mOrganizer).updateBoundsAndOffsets(false);
|
||||
verify(mOrganizer).unregisterOrganizer();
|
||||
assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(0, 0, 0, 0));
|
||||
assertThat(mOrganizer.mOffsetX).isEqualTo(0);
|
||||
assertThat(mOrganizer.mOffsetY).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
@@ -109,6 +109,7 @@ public class SystemUIFactory {
|
||||
.setSplitScreen(mWMComponent.getSplitScreen())
|
||||
.setOneHanded(mWMComponent.getOneHanded())
|
||||
.setBubbles(mWMComponent.getBubbles())
|
||||
.setHideDisplayCutout(mWMComponent.getHideDisplayCutout())
|
||||
.setShellDump(mWMComponent.getShellDump());
|
||||
} else {
|
||||
// TODO: Call on prepareSysUIComponentBuilder but not with real components.
|
||||
@@ -116,6 +117,7 @@ public class SystemUIFactory {
|
||||
.setSplitScreen(Optional.ofNullable(null))
|
||||
.setOneHanded(Optional.ofNullable(null))
|
||||
.setBubbles(Optional.ofNullable(null))
|
||||
.setHideDisplayCutout(Optional.ofNullable(null))
|
||||
.setShellDump(Optional.ofNullable(null));
|
||||
}
|
||||
mSysUIComponent = builder.build();
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
import com.android.systemui.util.InjectionInflationController;
|
||||
import com.android.wm.shell.ShellDump;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
@@ -65,6 +66,9 @@ public interface SysUIComponent {
|
||||
@BindsInstance
|
||||
Builder setBubbles(Optional<Bubbles> b);
|
||||
|
||||
@BindsInstance
|
||||
Builder setHideDisplayCutout(Optional<HideDisplayCutout> h);
|
||||
|
||||
@BindsInstance
|
||||
Builder setShellDump(Optional<ShellDump> shellDump);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.android.systemui.wmshell.WMShellModule;
|
||||
import com.android.wm.shell.ShellDump;
|
||||
import com.android.wm.shell.ShellInit;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
@@ -71,4 +72,7 @@ public interface WMComponent {
|
||||
|
||||
@WMSingleton
|
||||
Optional<Bubbles> getBubbles();
|
||||
|
||||
@WMSingleton
|
||||
Optional<HideDisplayCutout> getHideDisplayCutout();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.systemui.wmshell;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
|
||||
|
||||
@@ -29,21 +27,14 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_O
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
|
||||
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.ActivityTaskManager.RootTaskInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -55,8 +46,6 @@ import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||
import com.android.systemui.model.SysUiState;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
import com.android.systemui.shared.tracing.ProtoTraceable;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
@@ -64,6 +53,7 @@ import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.tracing.ProtoTracer;
|
||||
import com.android.systemui.tracing.nano.SystemUiTraceProto;
|
||||
import com.android.wm.shell.ShellDump;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.nano.WmShellTraceProto;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.onehanded.OneHandedEvents;
|
||||
@@ -105,6 +95,7 @@ public final class WMShell extends SystemUI
|
||||
private final Optional<Pip> mPipOptional;
|
||||
private final Optional<SplitScreen> mSplitScreenOptional;
|
||||
private final Optional<OneHanded> mOneHandedOptional;
|
||||
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
|
||||
private final ProtoTracer mProtoTracer;
|
||||
private final Optional<ShellDump> mShellDump;
|
||||
|
||||
@@ -123,6 +114,7 @@ public final class WMShell extends SystemUI
|
||||
Optional<Pip> pipOptional,
|
||||
Optional<SplitScreen> splitScreenOptional,
|
||||
Optional<OneHanded> oneHandedOptional,
|
||||
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
|
||||
ProtoTracer protoTracer,
|
||||
Optional<ShellDump> shellDump) {
|
||||
super(context);
|
||||
@@ -135,6 +127,7 @@ public final class WMShell extends SystemUI
|
||||
mPipOptional = pipOptional;
|
||||
mSplitScreenOptional = splitScreenOptional;
|
||||
mOneHandedOptional = oneHandedOptional;
|
||||
mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
|
||||
mProtoTracer = protoTracer;
|
||||
mProtoTracer.add(this);
|
||||
mShellDump = shellDump;
|
||||
@@ -146,6 +139,7 @@ public final class WMShell extends SystemUI
|
||||
mPipOptional.ifPresent(this::initPip);
|
||||
mSplitScreenOptional.ifPresent(this::initSplitScreen);
|
||||
mOneHandedOptional.ifPresent(this::initOneHanded);
|
||||
mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -284,6 +278,16 @@ public final class WMShell extends SystemUI
|
||||
});
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initHideDisplayCutout(HideDisplayCutout hideDisplayCutout) {
|
||||
mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
|
||||
@Override
|
||||
public void onConfigChanged(Configuration newConfig) {
|
||||
hideDisplayCutout.onConfigurationChanged(newConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToProto(SystemUiTraceProto proto) {
|
||||
if (proto.wmShell == null) {
|
||||
|
||||
@@ -28,7 +28,6 @@ import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.systemui.dagger.WMSingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.wm.shell.ShellDump;
|
||||
import com.android.wm.shell.ShellInit;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
@@ -46,6 +45,8 @@ import com.android.wm.shell.common.SystemWindows;
|
||||
import com.android.wm.shell.common.TaskStackListenerImpl;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.onehanded.OneHandedController;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
@@ -90,9 +91,10 @@ public abstract class WMShellBaseModule {
|
||||
static Optional<ShellDump> provideShellDump(ShellTaskOrganizer shellTaskOrganizer,
|
||||
Optional<SplitScreen> splitScreenOptional,
|
||||
Optional<Pip> pipOptional,
|
||||
Optional<OneHanded> oneHandedOptional) {
|
||||
Optional<OneHanded> oneHandedOptional,
|
||||
Optional<HideDisplayCutout> hideDisplayCutout) {
|
||||
return Optional.of(new ShellDump(shellTaskOrganizer, splitScreenOptional, pipOptional,
|
||||
oneHandedOptional));
|
||||
oneHandedOptional, hideDisplayCutout));
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@@ -215,4 +217,10 @@ public abstract class WMShellBaseModule {
|
||||
return new HandlerExecutor(handler);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static Optional<HideDisplayCutout> provideHideDisplayCutoutController(Context context,
|
||||
DisplayController displayController) {
|
||||
return Optional.ofNullable(HideDisplayCutoutController.create(context, displayController));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
@@ -31,13 +30,11 @@ import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.keyguard.ScreenLifecycle;
|
||||
import com.android.systemui.model.SysUiState;
|
||||
import com.android.systemui.navigationbar.NavigationModeController;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
import com.android.systemui.tracing.ProtoTracer;
|
||||
import com.android.wm.shell.ShellDump;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.onehanded.OneHandedGestureHandler;
|
||||
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
|
||||
@@ -56,13 +53,11 @@ import java.util.Optional;
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class WMShellTest extends SysuiTestCase {
|
||||
InputConsumerController mInputConsumerController;
|
||||
WMShell mWMShell;
|
||||
|
||||
@Mock CommandQueue mCommandQueue;
|
||||
@Mock ConfigurationController mConfigurationController;
|
||||
@Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||
@Mock InputConsumerController mMockInputConsumerController;
|
||||
@Mock NavigationModeController mNavigationModeController;
|
||||
@Mock ScreenLifecycle mScreenLifecycle;
|
||||
@Mock SysUiState mSysUiState;
|
||||
@@ -70,6 +65,7 @@ public class WMShellTest extends SysuiTestCase {
|
||||
@Mock PipTouchHandler mPipTouchHandler;
|
||||
@Mock SplitScreen mSplitScreen;
|
||||
@Mock OneHanded mOneHanded;
|
||||
@Mock HideDisplayCutout mHideDisplayCutout;
|
||||
@Mock ProtoTracer mProtoTracer;
|
||||
@Mock ShellDump mShellDump;
|
||||
|
||||
@@ -80,7 +76,8 @@ public class WMShellTest extends SysuiTestCase {
|
||||
mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
|
||||
mKeyguardUpdateMonitor, mNavigationModeController,
|
||||
mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
|
||||
Optional.of(mOneHanded), mProtoTracer, Optional.of(mShellDump));
|
||||
Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), mProtoTracer,
|
||||
Optional.of(mShellDump));
|
||||
|
||||
when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
|
||||
}
|
||||
@@ -113,4 +110,12 @@ public class WMShellTest extends SysuiTestCase {
|
||||
OneHandedGestureHandler.OneHandedGestureEventCallback.class));
|
||||
verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initHideDisplayCutout_registersCallbacks() {
|
||||
mWMShell.initHideDisplayCutout(mHideDisplayCutout);
|
||||
|
||||
verify(mConfigurationController).addCallback(
|
||||
any(ConfigurationController.ConfigurationListener.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
|
||||
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_STATUS_BAR;
|
||||
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
|
||||
import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
|
||||
import static android.window.DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT;
|
||||
import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
|
||||
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
|
||||
|
||||
@@ -99,6 +102,12 @@ public abstract class DisplayAreaPolicy {
|
||||
// layer
|
||||
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
|
||||
.build())
|
||||
.addFeature(new Feature.Builder(wmService.mPolicy, "HideDisplayCutout",
|
||||
FEATURE_HIDE_DISPLAY_CUTOUT)
|
||||
.all()
|
||||
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
|
||||
TYPE_NOTIFICATION_SHADE)
|
||||
.build())
|
||||
.addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
|
||||
FEATURE_ONE_HANDED)
|
||||
.all()
|
||||
|
||||
@@ -2969,10 +2969,6 @@ public class DisplayPolicy {
|
||||
|
||||
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
|
||||
|
||||
final int fullscreenAppearance = updateLightStatusBarLw(0 /* appearance */,
|
||||
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
|
||||
final int dockedAppearance = updateLightStatusBarLw(0 /* appearance */,
|
||||
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
|
||||
final boolean inSplitScreen =
|
||||
mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
|
||||
if (inSplitScreen) {
|
||||
@@ -2984,6 +2980,12 @@ public class DisplayPolicy {
|
||||
mService.getStackBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
|
||||
: WINDOWING_MODE_FULLSCREEN,
|
||||
ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds);
|
||||
final int fullscreenAppearance = updateLightStatusBarLw(0 /* appearance */,
|
||||
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
|
||||
mNonDockedStackBounds);
|
||||
final int dockedAppearance = updateLightStatusBarLw(0 /* appearance */,
|
||||
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState,
|
||||
mDockedStackBounds);
|
||||
final int disableFlags = win.getSystemUiVisibility() & StatusBarManager.DISABLE_MASK;
|
||||
final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
|
||||
final WindowState navColorWin = chooseNavigationColorWindowLw(
|
||||
@@ -3049,10 +3051,14 @@ public class DisplayPolicy {
|
||||
}
|
||||
|
||||
private int updateLightStatusBarLw(@Appearance int appearance, WindowState opaque,
|
||||
WindowState opaqueOrDimming) {
|
||||
WindowState opaqueOrDimming, Rect stackBounds) {
|
||||
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
|
||||
final int statusBarHeight = mStatusBarHeightForRotation[displayRotation.getRotation()];
|
||||
final boolean stackBoundsContainStatusBar =
|
||||
stackBounds.isEmpty() ? false : stackBounds.top < statusBarHeight;
|
||||
final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded();
|
||||
final WindowState statusColorWin = onKeyguard ? mNotificationShade : opaqueOrDimming;
|
||||
if (statusColorWin != null) {
|
||||
if (stackBoundsContainStatusBar && statusColorWin != null) {
|
||||
if (statusColorWin == opaque || onKeyguard) {
|
||||
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
|
||||
// its light flag.
|
||||
|
||||
Reference in New Issue
Block a user