Introduce Client Transient flag (1/n)

This is a feature flag to develop transient related features on the
system UI side. Currently it's handled by the system server.

This change introduced the flag and set it by default to false, in the
meanwhile, wrapped existing server side logic with the flag to make the
future clean-up easier.

If the flag is flipped without system UI side feature complete, the
transient animation will not work. Other insets related functionality
will not be affected.

SystemGesturesPointerEventListener should be simplified or changed to a
class instead of an interface with the feature development.

Bug: 277290737
Test: DisplayPolicyTests
Test: DisplayPolicyInsetsTests
Change-Id: I243407d233d71b40807d29660090e674b8dd5d31
This commit is contained in:
Yunfan Chen
2023-04-17 07:09:36 +00:00
parent 99a5d2d1cd
commit 4defffd043
3 changed files with 163 additions and 132 deletions

View File

@@ -300,6 +300,14 @@ public final class ViewRootImpl implements ViewParent,
public static final boolean CAPTION_ON_SHELL =
SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", true);
/**
* Whether the client (system UI) is handling the transient gesture and the corresponding
* animation.
* @hide
*/
public static final boolean CLIENT_TRANSIENT =
SystemProperties.getBoolean("persist.wm.debug.client_transient", false);
/**
* Whether the client should compute the window frame on its own.
* @hide

View File

@@ -22,6 +22,7 @@ import static android.view.InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE;
import static android.view.InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS;
import static android.view.InsetsFrameProvider.SOURCE_DISPLAY;
import static android.view.InsetsFrameProvider.SOURCE_FRAME;
import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -214,7 +215,8 @@ public class DisplayPolicy {
}
}
private final SystemGesturesPointerEventListener mSystemGestures;
// Will be null in client transient mode.
private SystemGesturesPointerEventListener mSystemGestures;
final DecorInsets mDecorInsets;
@@ -411,158 +413,162 @@ public class DisplayPolicy {
final Looper looper = UiThread.getHandler().getLooper();
mHandler = new PolicyHandler(looper);
// TODO(b/181821798) Migrate SystemGesturesPointerEventListener to use window context.
mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
new SystemGesturesPointerEventListener.Callbacks() {
if (!CLIENT_TRANSIENT) {
SystemGesturesPointerEventListener.Callbacks gesturesPointerEventCallbacks =
new SystemGesturesPointerEventListener.Callbacks() {
private static final long MOUSE_GESTURE_DELAY_MS = 500;
private static final long MOUSE_GESTURE_DELAY_MS = 500;
private Runnable mOnSwipeFromLeft = this::onSwipeFromLeft;
private Runnable mOnSwipeFromTop = this::onSwipeFromTop;
private Runnable mOnSwipeFromRight = this::onSwipeFromRight;
private Runnable mOnSwipeFromBottom = this::onSwipeFromBottom;
private Runnable mOnSwipeFromLeft = this::onSwipeFromLeft;
private Runnable mOnSwipeFromTop = this::onSwipeFromTop;
private Runnable mOnSwipeFromRight = this::onSwipeFromRight;
private Runnable mOnSwipeFromBottom = this::onSwipeFromBottom;
private Insets getControllableInsets(WindowState win) {
if (win == null) {
return Insets.NONE;
}
final InsetsSourceProvider provider = win.getControllableInsetProvider();
if (provider == null) {
return Insets.NONE;
}
return provider.getSource().calculateInsets(win.getBounds(),
true /* ignoreVisibility */);
private Insets getControllableInsets(WindowState win) {
if (win == null) {
return Insets.NONE;
}
final InsetsSourceProvider provider = win.getControllableInsetProvider();
if (provider == null) {
return Insets.NONE;
}
return provider.getSource().calculateInsets(win.getBounds(),
true /* ignoreVisibility */);
}
@Override
public void onSwipeFromTop() {
synchronized (mLock) {
requestTransientBars(mTopGestureHost,
getControllableInsets(mTopGestureHost).top > 0);
@Override
public void onSwipeFromTop() {
synchronized (mLock) {
requestTransientBars(mTopGestureHost,
getControllableInsets(mTopGestureHost).top > 0);
}
}
@Override
public void onSwipeFromBottom() {
synchronized (mLock) {
requestTransientBars(mBottomGestureHost,
getControllableInsets(mBottomGestureHost).bottom > 0);
}
}
private boolean allowsSideSwipe(Region excludedRegion) {
return mNavigationBarAlwaysShowOnSideGesture
&& !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
}
@Override
public void onSwipeFromRight() {
final Region excludedRegion = Region.obtain();
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
final boolean hasWindow =
getControllableInsets(mRightGestureHost).right > 0;
if (hasWindow || allowsSideSwipe(excludedRegion)) {
requestTransientBars(mRightGestureHost, hasWindow);
}
}
excludedRegion.recycle();
}
@Override
public void onSwipeFromBottom() {
synchronized (mLock) {
requestTransientBars(mBottomGestureHost,
getControllableInsets(mBottomGestureHost).bottom > 0);
@Override
public void onSwipeFromLeft() {
final Region excludedRegion = Region.obtain();
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
final boolean hasWindow =
getControllableInsets(mLeftGestureHost).left > 0;
if (hasWindow || allowsSideSwipe(excludedRegion)) {
requestTransientBars(mLeftGestureHost, hasWindow);
}
}
excludedRegion.recycle();
}
private boolean allowsSideSwipe(Region excludedRegion) {
return mNavigationBarAlwaysShowOnSideGesture
&& !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
@Override
public void onFling(int duration) {
if (mService.mPowerManagerInternal != null) {
mService.mPowerManagerInternal.setPowerBoost(
Boost.INTERACTION, duration);
}
}
@Override
public void onSwipeFromRight() {
final Region excludedRegion = Region.obtain();
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
final boolean hasWindow =
getControllableInsets(mRightGestureHost).right > 0;
if (hasWindow || allowsSideSwipe(excludedRegion)) {
requestTransientBars(mRightGestureHost, hasWindow);
}
}
excludedRegion.recycle();
}
@Override
public void onDebug() {
// no-op
}
@Override
public void onSwipeFromLeft() {
final Region excludedRegion = Region.obtain();
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
final boolean hasWindow =
getControllableInsets(mLeftGestureHost).left > 0;
if (hasWindow || allowsSideSwipe(excludedRegion)) {
requestTransientBars(mLeftGestureHost, hasWindow);
}
}
excludedRegion.recycle();
}
private WindowOrientationListener getOrientationListener() {
final DisplayRotation rotation = mDisplayContent.getDisplayRotation();
return rotation != null ? rotation.getOrientationListener() : null;
}
@Override
public void onFling(int duration) {
if (mService.mPowerManagerInternal != null) {
mService.mPowerManagerInternal.setPowerBoost(
Boost.INTERACTION, duration);
}
@Override
public void onDown() {
final WindowOrientationListener listener = getOrientationListener();
if (listener != null) {
listener.onTouchStart();
}
}
@Override
public void onDebug() {
// no-op
@Override
public void onUpOrCancel() {
final WindowOrientationListener listener = getOrientationListener();
if (listener != null) {
listener.onTouchEnd();
}
}
private WindowOrientationListener getOrientationListener() {
final DisplayRotation rotation = mDisplayContent.getDisplayRotation();
return rotation != null ? rotation.getOrientationListener() : null;
}
@Override
public void onMouseHoverAtLeft() {
mHandler.removeCallbacks(mOnSwipeFromLeft);
mHandler.postDelayed(mOnSwipeFromLeft, MOUSE_GESTURE_DELAY_MS);
}
@Override
public void onDown() {
final WindowOrientationListener listener = getOrientationListener();
if (listener != null) {
listener.onTouchStart();
}
}
@Override
public void onMouseHoverAtTop() {
mHandler.removeCallbacks(mOnSwipeFromTop);
mHandler.postDelayed(mOnSwipeFromTop, MOUSE_GESTURE_DELAY_MS);
}
@Override
public void onUpOrCancel() {
final WindowOrientationListener listener = getOrientationListener();
if (listener != null) {
listener.onTouchEnd();
}
}
@Override
public void onMouseHoverAtRight() {
mHandler.removeCallbacks(mOnSwipeFromRight);
mHandler.postDelayed(mOnSwipeFromRight, MOUSE_GESTURE_DELAY_MS);
}
@Override
public void onMouseHoverAtLeft() {
mHandler.removeCallbacks(mOnSwipeFromLeft);
mHandler.postDelayed(mOnSwipeFromLeft, MOUSE_GESTURE_DELAY_MS);
}
@Override
public void onMouseHoverAtBottom() {
mHandler.removeCallbacks(mOnSwipeFromBottom);
mHandler.postDelayed(mOnSwipeFromBottom, MOUSE_GESTURE_DELAY_MS);
}
@Override
public void onMouseHoverAtTop() {
mHandler.removeCallbacks(mOnSwipeFromTop);
mHandler.postDelayed(mOnSwipeFromTop, MOUSE_GESTURE_DELAY_MS);
}
@Override
public void onMouseLeaveFromLeft() {
mHandler.removeCallbacks(mOnSwipeFromLeft);
}
@Override
public void onMouseHoverAtRight() {
mHandler.removeCallbacks(mOnSwipeFromRight);
mHandler.postDelayed(mOnSwipeFromRight, MOUSE_GESTURE_DELAY_MS);
}
@Override
public void onMouseLeaveFromTop() {
mHandler.removeCallbacks(mOnSwipeFromTop);
}
@Override
public void onMouseHoverAtBottom() {
mHandler.removeCallbacks(mOnSwipeFromBottom);
mHandler.postDelayed(mOnSwipeFromBottom, MOUSE_GESTURE_DELAY_MS);
}
@Override
public void onMouseLeaveFromRight() {
mHandler.removeCallbacks(mOnSwipeFromRight);
}
@Override
public void onMouseLeaveFromLeft() {
mHandler.removeCallbacks(mOnSwipeFromLeft);
}
@Override
public void onMouseLeaveFromTop() {
mHandler.removeCallbacks(mOnSwipeFromTop);
}
@Override
public void onMouseLeaveFromRight() {
mHandler.removeCallbacks(mOnSwipeFromRight);
}
@Override
public void onMouseLeaveFromBottom() {
mHandler.removeCallbacks(mOnSwipeFromBottom);
}
});
displayContent.registerPointerEventListener(mSystemGestures);
@Override
public void onMouseLeaveFromBottom() {
mHandler.removeCallbacks(mOnSwipeFromBottom);
}
};
mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
gesturesPointerEventCallbacks);
displayContent.registerPointerEventListener(mSystemGestures);
}
mAppTransitionListener = new WindowManagerInternal.AppTransitionListener() {
private Runnable mAppTransitionPending = () -> {
@@ -648,7 +654,9 @@ public class DisplayPolicy {
mContext, () -> {
synchronized (mLock) {
onConfigurationChanged();
mSystemGestures.onConfigurationChanged();
if (!CLIENT_TRANSIENT) {
mSystemGestures.onConfigurationChanged();
}
mDisplayContent.updateSystemGestureExclusion();
}
});
@@ -670,7 +678,9 @@ public class DisplayPolicy {
}
void systemReady() {
mSystemGestures.systemReady();
if (!CLIENT_TRANSIENT) {
mSystemGestures.systemReady();
}
if (mService.mPointerLocationEnabled) {
setPointerLocationEnabled(true);
}
@@ -1317,7 +1327,9 @@ public class DisplayPolicy {
}
void onDisplayInfoChanged(DisplayInfo info) {
mSystemGestures.onDisplayInfoChanged(info);
if (!CLIENT_TRANSIENT) {
mSystemGestures.onDisplayInfoChanged(info);
}
}
/**
@@ -1690,7 +1702,9 @@ public class DisplayPolicy {
// Update the latest display size, cutout.
mDisplayContent.updateDisplayInfo();
onConfigurationChanged();
mSystemGestures.onConfigurationChanged();
if (!CLIENT_TRANSIENT) {
mSystemGestures.onConfigurationChanged();
}
}
/**
@@ -1963,6 +1977,9 @@ public class DisplayPolicy {
@VisibleForTesting
void requestTransientBars(WindowState swipeTarget, boolean isGestureOnSystemBar) {
if (CLIENT_TRANSIENT) {
return;
}
if (swipeTarget == null || !mService.mPolicy.isUserSetupComplete()) {
// Swipe-up for navigation bar is disabled during setup
return;
@@ -2613,7 +2630,9 @@ public class DisplayPolicy {
final DecorInsets.Info info = mDecorInsets.mInfoForRotation[rotation];
pw.println(prefixInner + Surface.rotationToString(rotation) + "=" + info);
}
mSystemGestures.dump(pw, prefix);
if (!CLIENT_TRANSIENT) {
mSystemGestures.dump(pw, prefix);
}
pw.print(prefix); pw.println("Looper state:");
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");

View File

@@ -22,6 +22,7 @@ import static android.view.RoundedCorners.NO_ROUNDED_CORNERS;
import static android.view.Surface.ROTATION_0;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -61,6 +62,7 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -379,6 +381,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
@SetupWindows(addWindows = { W_ACTIVITY, W_NAVIGATION_BAR })
@Test
public void testCanSystemBarsBeShownByUser() {
Assume.assumeFalse(CLIENT_TRANSIENT);
((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true;
mAppWindow.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
mAppWindow.setRequestedVisibleTypes(0, navigationBars());
@@ -400,6 +403,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
@UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
@Test
public void testTransientBarsSuppressedOnDreams() {
Assume.assumeFalse(CLIENT_TRANSIENT);
final WindowState win = createDreamWindow();
((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true;