summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/IWindowSession.aidl6
-rw-r--r--core/java/android/view/InsetsController.java31
-rw-r--r--core/java/android/view/InsetsSource.java4
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java34
-rw-r--r--core/java/android/view/InsetsState.java27
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java14
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java40
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java16
-rw-r--r--services/core/java/com/android/server/wm/Session.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java43
10 files changed, 210 insertions, 17 deletions
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 97625869209d..d1115c7b4d1d 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -250,4 +250,10 @@ interface IWindowSession {
*/
void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width,
int height);
+
+ /**
+ * Called when the client has changed the local insets state, and now the server should reflect
+ * that new state.
+ */
+ void insetsModified(IWindow window, in InsetsState state);
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 4ab1f266cc70..01af37e75cb9 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -19,7 +19,9 @@ package android.view;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
+import android.os.RemoteException;
import android.util.ArraySet;
+import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetType;
@@ -36,7 +38,11 @@ import java.util.ArrayList;
*/
public class InsetsController implements WindowInsetsController {
+ private final String TAG = "InsetsControllerImpl";
+
private final InsetsState mState = new InsetsState();
+ private final InsetsState mTmpState = new InsetsState();
+
private final Rect mFrame = new Rect();
private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
private final ViewRootImpl mViewRoot;
@@ -61,8 +67,12 @@ public class InsetsController implements WindowInsetsController {
return false;
}
mState.set(state);
+ mTmpState.set(state, true /* copySources */);
applyLocalVisibilityOverride();
mViewRoot.notifyInsetsChanged();
+ if (!mState.equals(mTmpState)) {
+ sendStateToWindowManager();
+ }
return true;
}
@@ -163,6 +173,27 @@ public class InsetsController implements WindowInsetsController {
@VisibleForTesting
public void notifyVisibilityChanged() {
mViewRoot.notifyInsetsChanged();
+ sendStateToWindowManager();
+ }
+
+ /**
+ * Sends the local visibility state back to window manager.
+ */
+ private void sendStateToWindowManager() {
+ InsetsState tmpState = new InsetsState();
+ for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+ final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+ if (consumer.getControl() != null) {
+ tmpState.addSource(mState.getSource(consumer.getType()));
+ }
+ }
+
+ // TODO: Put this on a dispatcher thread.
+ try {
+ mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call insetsModified", e);
+ }
}
void dump(String prefix, PrintWriter pw) {
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index f8148a906bb3..fbc72a0f978e 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -65,6 +65,10 @@ public class InsetsSource implements Parcelable {
return mFrame;
}
+ public boolean isVisible() {
+ return mVisible;
+ }
+
/**
* Calculates the insets this source will cause to a client window.
*
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ec85c4c56cfc..145b09763676 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -35,7 +35,7 @@ public class InsetsSourceConsumer {
private final InsetsState mState;
private final InsetsController mController;
private @Nullable InsetsSourceControl mSourceControl;
- private boolean mHidden;
+ private boolean mVisible;
public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
Supplier<Transaction> transactionSupplier, InsetsController controller) {
@@ -43,6 +43,7 @@ public class InsetsSourceConsumer {
mState = state;
mTransactionSupplier = transactionSupplier;
mController = controller;
+ mVisible = InsetsState.getDefaultVisibly(type);
}
public void setControl(@Nullable InsetsSourceControl control) {
@@ -51,8 +52,9 @@ public class InsetsSourceConsumer {
}
mSourceControl = control;
applyHiddenToControl();
- applyLocalVisibilityOverride();
- mController.notifyVisibilityChanged();
+ if (applyLocalVisibilityOverride()) {
+ mController.notifyVisibilityChanged();
+ }
}
@VisibleForTesting
@@ -66,28 +68,32 @@ public class InsetsSourceConsumer {
@VisibleForTesting
public void show() {
- setHidden(false);
+ setVisible(true);
}
@VisibleForTesting
public void hide() {
- setHidden(true);
+ setVisible(false);
}
- void applyLocalVisibilityOverride() {
+ boolean applyLocalVisibilityOverride() {
// If we don't have control, we are not able to change the visibility.
if (mSourceControl == null) {
- return;
+ return false;
+ }
+ if (mState.getSource(mType).isVisible() == mVisible) {
+ return false;
}
- mState.getSource(mType).setVisible(!mHidden);
+ mState.getSource(mType).setVisible(mVisible);
+ return true;
}
- private void setHidden(boolean hidden) {
- if (mHidden == hidden) {
+ private void setVisible(boolean visible) {
+ if (mVisible == visible) {
return;
}
- mHidden = hidden;
+ mVisible = visible;
applyHiddenToControl();
applyLocalVisibilityOverride();
mController.notifyVisibilityChanged();
@@ -100,10 +106,10 @@ public class InsetsSourceConsumer {
// TODO: Animation
final Transaction t = mTransactionSupplier.get();
- if (mHidden) {
- t.hide(mSourceControl.getLeash());
- } else {
+ if (mVisible) {
t.show(mSourceControl.getLeash());
+ } else {
+ t.hide(mSourceControl.getLeash());
}
t.apply();
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 63025dc16f17..3f8f882a869f 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -32,6 +32,7 @@ import android.view.WindowInsets.Type.InsetType;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
/**
* Holder for state of system windows that cause window insets for all other windows in the system.
@@ -200,6 +201,18 @@ public class InsetsState implements Parcelable {
}
}
+ public void addSource(InsetsSource source) {
+ mSources.put(source.getType(), source);
+ }
+
+ public int getSourcesCount() {
+ return mSources.size();
+ }
+
+ public InsetsSource sourceAt(int index) {
+ return mSources.valueAt(index);
+ }
+
public static @InternalInsetType ArraySet<Integer> toInternalType(@InsetType int insetTypes) {
final ArraySet<Integer> result = new ArraySet<>();
if ((insetTypes & Type.TOP_BAR) != 0) {
@@ -216,6 +229,20 @@ public class InsetsState implements Parcelable {
return result;
}
+ public static boolean getDefaultVisibly(@InsetType int type) {
+ switch (type) {
+ case TYPE_TOP_BAR:
+ case TYPE_SIDE_BAR_1:
+ case TYPE_SIDE_BAR_2:
+ case TYPE_SIDE_BAR_3:
+ return true;
+ case TYPE_IME:
+ return false;
+ default:
+ return true;
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "InsetsState");
for (int i = mSources.size() - 1; i >= 0; i--) {
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index d41a718147f2..9807f26ad367 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -20,8 +20,13 @@ import static android.view.InsetsState.INSET_SIDE_BOTTOM;
import static android.view.InsetsState.INSET_SIDE_TOP;
import static android.view.InsetsState.TYPE_IME;
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_SIDE_BAR_1;
+import static android.view.InsetsState.TYPE_SIDE_BAR_2;
+import static android.view.InsetsState.TYPE_SIDE_BAR_3;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;
import android.graphics.Rect;
@@ -133,4 +138,13 @@ public class InsetsStateTest {
p.recycle();
assertEquals(mState, mState2);
}
+
+ @Test
+ public void testGetDefaultVisibility() {
+ assertTrue(InsetsState.getDefaultVisibly(TYPE_TOP_BAR));
+ assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_1));
+ assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_2));
+ assertTrue(InsetsState.getDefaultVisibly(TYPE_SIDE_BAR_3));
+ assertFalse(InsetsState.getDefaultVisibly(TYPE_IME));
+ }
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 282838f7d58b..49a3186be9b4 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.InsetsSource;
@@ -47,8 +48,18 @@ class InsetsSourceProvider {
private WindowState mWin;
private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
+ /** The visibility override from the current controlling window. */
+ private boolean mClientVisible;
+
+ /**
+ * Whether the window is available and considered visible as in {@link WindowState#isVisible}.
+ */
+ private boolean mServerVisible;
+
+
InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
DisplayContent displayContent) {
+ mClientVisible = InsetsState.getDefaultVisibly(source.getType());
mSource = source;
mDisplayContent = displayContent;
mStateController = stateController;
@@ -73,10 +84,9 @@ class InsetsSourceProvider {
mWin = win;
mFrameProvider = frameProvider;
if (win == null) {
- mSource.setVisible(false);
+ setServerVisible(false);
mSource.setFrame(new Rect());
} else {
- mSource.setVisible(true);
mWin.setInsetProvider(this);
}
}
@@ -96,7 +106,7 @@ class InsetsSourceProvider {
mTmpRect.inset(mWin.mGivenContentInsets);
}
mSource.setFrame(mTmpRect);
- mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
+ setServerVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
}
@@ -113,6 +123,29 @@ class InsetsSourceProvider {
false /* TODO hidden */);
mControllingWin = target;
mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash);
+ setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
+ }
+
+ boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
+ if (mControllingWin != caller || modifiedSource.isVisible() == mClientVisible) {
+ return false;
+ }
+ setClientVisible(modifiedSource.isVisible());
+ return true;
+ }
+
+ private void setClientVisible(boolean clientVisible) {
+ mClientVisible = clientVisible;
+ updateVisibility();
+ }
+
+ private void setServerVisible(boolean serverVisible) {
+ mServerVisible = serverVisible;
+ updateVisibility();
+ }
+
+ private void updateVisibility() {
+ mSource.setVisible(mServerVisible && mClientVisible);
}
InsetsSourceControl getControl() {
@@ -125,6 +158,7 @@ class InsetsSourceProvider {
// Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
mWin.cancelAnimation();
}
+ setClientVisible(InsetsState.getDefaultVisibly(mSource.getType()));
}
private class ControlAdapter implements AnimationAdapter {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 592b7fba4bfd..8e119bb195d6 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
+import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.ViewRootImpl;
@@ -117,6 +118,21 @@ class InsetsStateController {
}
}
+ void onInsetsModified(WindowState windowState, InsetsState state) {
+ boolean changed = false;
+ for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
+ final InsetsSource source = state.sourceAt(i);
+ final InsetsSourceProvider provider = mControllers.get(source.getType());
+ if (provider == null) {
+ continue;
+ }
+ changed |= provider.onInsetsModified(windowState, source);
+ }
+ if (changed) {
+ notifyInsetsChanged();
+ }
+ }
+
void onImeTargetChanged(@Nullable WindowState imeTarget) {
onControlChanged(TYPE_IME, imeTarget);
notifyPendingInsetsControlChanged();
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 37b5a7c30218..d85fdb03e4a6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -430,6 +430,18 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
}
+ @Override
+ public void insetsModified(IWindow window, InsetsState state) {
+ synchronized (mService.mWindowMap) {
+ final WindowState windowState = mService.windowForClientLocked(this, window,
+ false /* throwOnError */);
+ if (windowState != null) {
+ windowState.getDisplayContent().getInsetsStateController().onInsetsModified(
+ windowState, state);
+ }
+ }
+ }
+
void windowAddedLocked(String packageName) {
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 8821544903c1..374078625f4a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -19,13 +19,19 @@ package com.android.server.wm;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsSource;
+import android.view.InsetsState;
+import org.junit.Before;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
@@ -81,4 +87,41 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
mProvider.onPostLayout();
assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
}
+
+ @Test
+ public void testUpdateControlForTarget() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ mProvider.setWindow(topBar, null);
+ mProvider.updateControlForTarget(target);
+ assertNotNull(mProvider.getControl());
+ mProvider.updateControlForTarget(null);
+ assertNull(mProvider.getControl());
+ }
+
+ @Test
+ public void testInsetsModified() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ mProvider.setWindow(topBar, null);
+ mProvider.updateControlForTarget(target);
+ InsetsState state = new InsetsState();
+ state.getSource(TYPE_TOP_BAR).setVisible(false);
+ mProvider.onInsetsModified(target, state.getSource(TYPE_TOP_BAR));
+ assertFalse(mSource.isVisible());
+ }
+
+ @Test
+ public void testInsetsModified_noControl() {
+ final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+ final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+ topBar.getFrameLw().set(0, 0, 500, 100);
+ mProvider.setWindow(topBar, null);
+ InsetsState state = new InsetsState();
+ state.getSource(TYPE_TOP_BAR).setVisible(false);
+ mProvider.onInsetsModified(target, state.getSource(TYPE_TOP_BAR));
+ assertTrue(mSource.isVisible());
+ }
}