Merge "Fix flickers when changing control" into rvc-dev am: c322ed2173 am: 162040e358 am: b88c500fea am: 3cb2ddd649

Change-Id: I5a7d6e658add347463be56e4022d31ccc1e30a04
This commit is contained in:
TreeHugger Robot
2020-03-30 18:14:52 +00:00
committed by Automerger Merge Worker
10 changed files with 144 additions and 50 deletions

View File

@@ -456,6 +456,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return mState;
}
@Override
public boolean isRequestedVisible(int type) {
return getSourceConsumer(type).isRequestedVisible();
}
public InsetsState getLastDispatchedState() {
return mLastDispachedState;
}

View File

@@ -112,12 +112,12 @@ public class InsetsSourceConsumer {
hideTypes[0] |= toPublicType(getType());
}
} else {
// We are gaining control, but don't need to run an animation.
// However make sure that the leash visibility is still up to date.
if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
}
applyHiddenToControl();
// We are gaining control, but don't need to run an animation.
// However make sure that the leash visibility is still up to date.
if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
applyHiddenToControl();
}
}
}
if (lastControl != null) {

View File

@@ -107,6 +107,14 @@ public class PendingInsetsController implements WindowInsetsController {
return mDummyState;
}
@Override
public boolean isRequestedVisible(int type) {
// Method is only used once real insets controller is attached, so no need to traverse
// requests here.
return InsetsState.getDefaultVisibility(type);
}
@Override
public void addOnControllableInsetsChangedListener(
OnControllableInsetsChangedListener listener) {

View File

@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.graphics.Insets;
import android.inputmethodservice.InputMethodService;
import android.os.CancellationSignal;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
@@ -225,6 +226,13 @@ public interface WindowInsetsController {
*/
InsetsState getState();
/**
* @return Whether the specified insets source is currently requested to be visible by the
* application.
* @hide
*/
boolean isRequestedVisible(@InternalInsetsType int type);
/**
* Adds a {@link OnControllableInsetsChangedListener} to the window insets controller.
*

View File

@@ -120,6 +120,7 @@ import com.android.internal.widget.DecorCaptionView;
import com.android.internal.widget.FloatingToolbar;
import java.util.List;
import java.util.function.Function;
/** @hide */
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@@ -1101,7 +1102,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
final WindowInsetsController controller = getWindowInsetsController();
final InsetsState state = controller != null ? controller.getState() : null;
// IME is an exceptional floating window that requires color view.
final boolean isImeWindow =
@@ -1151,7 +1151,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
calculateNavigationBarColor(), mWindow.mNavigationBarDividerColor, navBarSize,
navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
0 /* sideInset */, animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds, state);
mForceWindowDrawsBarBackgrounds, controller);
boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
&& (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
@@ -1172,7 +1172,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
calculateStatusBarColor(), 0, mLastTopInset,
false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds, state);
mForceWindowDrawsBarBackgrounds, controller);
if (mHasCaption) {
final int captionColor = calculateStatusBarColor();
@@ -1193,7 +1193,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION to indicate whether the app wants to handle it by
// themselves.
boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
|| !(state == null || state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
|| !(controller == null || controller.isRequestedVisible(ITYPE_NAVIGATION_BAR));
boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
&& (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
@@ -1214,7 +1214,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
// fullscreen, as othrewise we can expect the app to handle it.
boolean fullscreen = (sysUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN) != 0
|| (attrs.flags & FLAG_FULLSCREEN) != 0
|| !(state == null || state.getSource(ITYPE_STATUS_BAR).isVisible());
|| !(controller == null || controller.isRequestedVisible(ITYPE_STATUS_BAR));
boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
&& (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
&& (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
@@ -1354,10 +1354,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
*/
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
boolean animate, boolean force, InsetsState insetsState) {
boolean animate, boolean force, WindowInsetsController controller) {
state.present = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL
? state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force)
: state.attributes.isPresent(insetsState, mWindow.getAttributes().flags, force);
: state.attributes.isPresent(
controller.isRequestedVisible(state.attributes.insetsType),
mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
mWindow.getAttributes().flags, force);
boolean showView = show && !isResizing() && !mHasCaption && size > 0;
@@ -2624,8 +2626,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
|| force);
}
public boolean isPresent(InsetsState state, int windowFlags, boolean force) {
return (state == null || state.getSource(insetsType).isVisible())
public boolean isPresent(boolean requestedVisible, int windowFlags, boolean force) {
return requestedVisible
&& ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 || force);
}
@@ -2642,7 +2644,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
public boolean isVisible(InsetsState state, int color, int windowFlags, boolean force) {
final boolean present = isPresent(state, windowFlags, force);
final boolean present = isPresent(state.getSource(insetsType).isVisible(), windowFlags,
force);
return isVisible(present, color, windowFlags, force);
}
}

View File

@@ -77,6 +77,9 @@ class InsetsPolicy {
/** Updates the target which can control system bars. */
void updateBarControlTarget(@Nullable WindowState focusedWin) {
if (mFocusedWin != focusedWin){
abortTransient();
}
mFocusedWin = focusedWin;
mStateController.onBarControlTargetChanged(getStatusControlTarget(focusedWin),
getFakeStatusControlTarget(focusedWin),
@@ -123,11 +126,13 @@ class InsetsPolicy {
mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
mShowingTransientTypes.toArray());
updateBarControlTarget(mFocusedWin);
InsetsState state = new InsetsState(mStateController.getRawInsetsState());
startAnimation(true /* show */, () -> {
synchronized (mDisplayContent.mWmService.mGlobalLock) {
mStateController.notifyInsetsChanged();
}
});
}, state);
mStateController.onInsetsModified(mDummyControlTarget, state);
}
}
@@ -135,13 +140,15 @@ class InsetsPolicy {
if (mShowingTransientTypes.size() == 0) {
return;
}
InsetsState state = new InsetsState(mStateController.getRawInsetsState());
startAnimation(false /* show */, () -> {
synchronized (mDisplayContent.mWmService.mGlobalLock) {
mShowingTransientTypes.clear();
mStateController.notifyInsetsChanged();
updateBarControlTarget(mFocusedWin);
}
});
}, state);
mStateController.onInsetsModified(mDummyControlTarget, state);
}
boolean isTransient(@InternalInsetsType int type) {
@@ -152,14 +159,22 @@ class InsetsPolicy {
* @see InsetsStateController#getInsetsForDispatch
*/
InsetsState getInsetsForDispatch(WindowState target) {
InsetsState state = mStateController.getInsetsForDispatch(target);
InsetsState originalState = mStateController.getInsetsForDispatch(target);
InsetsState state = originalState;
for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
state = new InsetsState(state);
state.setSourceVisible(mShowingTransientTypes.get(i), false);
}
if (mFocusedWin != null && getStatusControlTarget(mFocusedWin) == mDummyControlTarget) {
if (state == originalState) {
state = new InsetsState(state);
}
state.setSourceVisible(ITYPE_STATUS_BAR, mFocusedWin.getRequestedInsetsState());
}
if (mFocusedWin != null && getNavControlTarget(mFocusedWin) == mDummyControlTarget) {
if (state == originalState) {
state = new InsetsState(state);
}
state.setSourceVisible(ITYPE_NAVIGATION_BAR, mFocusedWin.getRequestedInsetsState());
}
return state;
@@ -206,6 +221,13 @@ class InsetsPolicy {
}
}
private void abortTransient() {
mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
mShowingTransientTypes.toArray());
mShowingTransientTypes.clear();
updateBarControlTarget(mFocusedWin);
}
private @Nullable InsetsControlTarget getFakeStatusControlTarget(
@Nullable WindowState focused) {
return getStatusControlTarget(focused) == mDummyControlTarget ? focused : null;
@@ -286,19 +308,20 @@ class InsetsPolicy {
}
@VisibleForTesting
void startAnimation(boolean show, Runnable callback) {
void startAnimation(boolean show, Runnable callback, InsetsState state) {
int typesReady = 0;
final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
final IntArray showingTransientTypes = mShowingTransientTypes;
for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
InsetsSourceProvider provider =
mStateController.getSourceProvider(showingTransientTypes.get(i));
int type = showingTransientTypes.get(i);
InsetsSourceProvider provider = mStateController.getSourceProvider(type);
InsetsSourceControl control = provider.getControl(mDummyControlTarget);
if (control == null || control.getLeash() == null) {
continue;
}
typesReady |= InsetsState.toPublicType(showingTransientTypes.get(i));
typesReady |= InsetsState.toPublicType(type);
controls.put(control.getType(), new InsetsSourceControl(control));
state.setSourceVisible(type, show);
}
controlAnimationUnchecked(typesReady, controls, show, callback);
}

View File

@@ -259,10 +259,13 @@ class InsetsSourceProvider {
if (target == null) {
// Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
mWin.cancelAnimation();
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
return;
}
mAdapter = new ControlAdapter();
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
if (getSource().getType() == ITYPE_IME) {
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
}
final Transaction t = mDisplayContent.getPendingTransaction();
mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
ANIMATION_TYPE_INSETS_CONTROL, null /* animationFinishedCallback */);

View File

@@ -3974,7 +3974,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
pw.println(prefix + "isOnScreen=" + isOnScreen());
pw.println(prefix + "isVisible=" + isVisible());
pw.println(prefix + "mEmbeddedDisplayContents=" + mEmbeddedDisplayContents);
if (!mEmbeddedDisplayContents.isEmpty()) {
pw.println(prefix + "mEmbeddedDisplayContents=" + mEmbeddedDisplayContents);
}
if (dumpAll) {
pw.println(prefix + "mRequestedInsetsState: " + mRequestedInsetsState);
}
}
@Override

View File

@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -29,13 +30,18 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.platform.test.annotations.Presubmit;
import android.util.IntArray;
@@ -164,45 +170,51 @@ public class InsetsPolicyTest extends WindowTestsBase {
@Test
public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
addWindow(TYPE_STATUS_BAR, "statusBar")
addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
.getControllableInsetProvider().getSource().setVisible(false);
addWindow(TYPE_NAVIGATION_BAR, "navBar")
addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
.getControllableInsetProvider().getSource().setVisible(false);
final WindowState app = addWindow(TYPE_APPLICATION, "app");
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(app);
doAnswer(invocation -> {
((InsetsState) invocation.getArgument(2)).setSourceVisible(ITYPE_STATUS_BAR, true);
((InsetsState) invocation.getArgument(2)).setSourceVisible(ITYPE_NAVIGATION_BAR, true);
return null;
}).when(policy).startAnimation(anyBoolean(), any(), any());
policy.updateBarControlTarget(mAppWindow);
policy.showTransient(
IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
// The app must get both fake controls.
assertEquals(2, controls.length);
for (int i = controls.length - 1; i >= 0; i--) {
assertNull(controls[i].getLeash());
}
assertTrue(mDisplayContent.getInsetsStateController().getRawInsetsState()
.getSource(ITYPE_STATUS_BAR).isVisible());
assertTrue(mDisplayContent.getInsetsStateController().getRawInsetsState()
.getSource(ITYPE_NAVIGATION_BAR).isVisible());
}
@Test
public void testShowTransientBars_statusBarCanBeTransient_appGetsStatusBarFakeControl() {
// Adding app window before setting source visibility is to prevent the visibility from
// being cleared by InsetsSourceProvider.updateVisibility.
final WindowState app = addWindow(TYPE_APPLICATION, "app");
addWindow(TYPE_STATUS_BAR, "statusBar")
addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
.getControllableInsetProvider().getSource().setVisible(false);
addWindow(TYPE_NAVIGATION_BAR, "navBar")
addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
.getControllableInsetProvider().getSource().setVisible(true);
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(app);
doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
policy.updateBarControlTarget(mAppWindow);
policy.showTransient(
IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
// The app must get the fake control of the status bar, and must get the real control of the
// navigation bar.
@@ -219,19 +231,18 @@ public class InsetsPolicyTest extends WindowTestsBase {
@Test
public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
addWindow(TYPE_STATUS_BAR, "statusBar")
addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
.getControllableInsetProvider().getSource().setVisible(false);
addWindow(TYPE_NAVIGATION_BAR, "navBar")
addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
.getControllableInsetProvider().getSource().setVisible(false);
final WindowState app = addWindow(TYPE_APPLICATION, "app");
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(app);
doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
policy.updateBarControlTarget(mAppWindow);
policy.showTransient(
IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
// The app must get both fake controls.
assertEquals(2, controls.length);
@@ -239,12 +250,12 @@ public class InsetsPolicyTest extends WindowTestsBase {
assertNull(controls[i].getLeash());
}
final InsetsState state = policy.getInsetsForDispatch(app);
final InsetsState state = policy.getInsetsForDispatch(mAppWindow);
state.setSourceVisible(ITYPE_STATUS_BAR, true);
state.setSourceVisible(ITYPE_NAVIGATION_BAR, true);
policy.onInsetsModified(app, state);
policy.onInsetsModified(mAppWindow, state);
controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow);
// The app must get both real controls.
assertEquals(2, controls.length);
@@ -253,6 +264,33 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
}
@Test
public void testShowTransientBars_abortsWhenControlTargetChanges() {
addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
.getControllableInsetProvider().getSource().setVisible(false);
addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
.getControllableInsetProvider().getSource().setVisible(false);
final WindowState app = addWindow(TYPE_APPLICATION, "app");
final WindowState app2 = addWindow(TYPE_APPLICATION, "app");
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any(), any());
policy.updateBarControlTarget(app);
policy.showTransient(
IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
final InsetsSourceControl[] controls =
mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
policy.updateBarControlTarget(app2);
assertFalse(policy.isTransient(ITYPE_STATUS_BAR));
assertFalse(policy.isTransient(ITYPE_NAVIGATION_BAR));
}
private WindowState addNonFocusableWindow(int type, String name) {
WindowState win = addWindow(type, name);
win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
return win;
}
private WindowState addWindow(int type, String name) {
final WindowState win = createWindow(null, type, name);
mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs);

View File

@@ -41,6 +41,7 @@ import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -87,7 +88,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase {
0 /* systemUiVisibility */, false /* isTranslucent */);
mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
taskBounds, ORIENTATION_PORTRAIT, null /* insetsState */);
taskBounds, ORIENTATION_PORTRAIT, new InsetsState());
}
private static TaskDescription createTaskDescription(int background, int statusBar,