diff options
| author | 2019-02-01 17:58:15 +0100 | |
|---|---|---|
| committer | 2019-02-08 14:21:53 +0100 | |
| commit | a5dbf52d0a00d27089d03615f15c25079a56c70f (patch) | |
| tree | ccf025a5ab929da386ee0d3859a10127ba785488 | |
| parent | fcc4357533b9e7ecf316494213750a609746eb2c (diff) | |
Set insets on the virtual display to avoid IME covering the bubble.
Bug: 123544535
Test: Manual test using the test app, and atest DisplayPolicyLayoutTests
Change-Id: If2fceea97f4d702d000d887883c7f131337e9fd0
| -rw-r--r-- | api/current.txt | 5 | ||||
| -rw-r--r-- | core/java/android/app/ActivityView.java | 23 | ||||
| -rw-r--r-- | core/java/android/view/IWindowManager.aidl | 11 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Insets.aidl | 20 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Insets.java | 31 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java | 21 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/DisplayContent.java | 17 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/DisplayPolicy.java | 33 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/WindowManagerService.java | 18 | ||||
| -rw-r--r-- | services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java | 45 |
10 files changed, 220 insertions, 4 deletions
diff --git a/api/current.txt b/api/current.txt index f8a164da3143..7ead41ca4d39 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14191,13 +14191,16 @@ package android.graphics { field public static final int YV12 = 842094169; // 0x32315659 } - public final class Insets { + public final class Insets implements android.os.Parcelable { method @NonNull public static android.graphics.Insets add(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets); + method public int describeContents(); method @NonNull public static android.graphics.Insets max(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets); method @NonNull public static android.graphics.Insets min(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets); method @NonNull public static android.graphics.Insets of(int, int, int, int); method @NonNull public static android.graphics.Insets of(@Nullable android.graphics.Rect); method @NonNull public static android.graphics.Insets subtract(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.graphics.Insets> CREATOR; field public static final android.graphics.Insets NONE; field public final int bottom; field public final int left; diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 5814e69c36a3..ce7199816726 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -26,6 +26,7 @@ import android.app.ActivityManager.StackInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.graphics.Insets; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.os.RemoteException; @@ -84,6 +85,8 @@ public class ActivityView extends ViewGroup { /** The ActivityView is only allowed to contain one task. */ private final boolean mSingleTaskInstance; + private Insets mForwardedInsets; + @UnsupportedAppUsage public ActivityView(Context context) { this(context, null /* attrs */); @@ -369,11 +372,13 @@ public class ActivityView extends ViewGroup { .build(); try { + // TODO: Find a way to consolidate these calls to the server. wm.reparentDisplayContent(displayId, mRootSurfaceControl); wm.dontOverrideDisplayInfo(displayId); if (mSingleTaskInstance) { mActivityTaskManager.setDisplayToSingleTaskInstance(displayId); } + wm.setForwardedInsets(displayId, mForwardedInsets); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } @@ -454,6 +459,24 @@ public class ActivityView extends ViewGroup { } /** + * Set forwarded insets on the virtual display. + * + * @see IWindowManager#setForwardedInsets + */ + public void setForwardedInsets(Insets insets) { + mForwardedInsets = insets; + if (mVirtualDisplay == null) { + return; + } + try { + final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + wm.setForwardedInsets(mVirtualDisplay.getDisplay().getDisplayId(), mForwardedInsets); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** * A task change listener that detects background color change of the topmost stack on our * virtual display and updates the background of the surface view. This background will be shown * when surface view is resized, but the app hasn't drawn its content in new size yet. diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 8ae4757f5de6..2ef7c4b16d9d 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -25,6 +25,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.GraphicBuffer; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; @@ -380,6 +381,16 @@ interface IWindowManager void getStableInsets(int displayId, out Rect outInsets); /** + * Set the forwarded insets on the display. + * <p> + * This is only used in case a virtual display is displayed on another display that has insets, + * and the bounds of the virtual display is overlapping with the insets from the host display. + * In that case, the contents on the virtual display won't be placed over the forwarded insets. + * Only the owner of the display is permitted to set the forwarded insets on it. + */ + void setForwardedInsets(int displayId, in Insets insets); + + /** * Register shortcut key. Shortcut code is packed as: * (MetaState << Integer.SIZE) | KeyCode * @hide diff --git a/graphics/java/android/graphics/Insets.aidl b/graphics/java/android/graphics/Insets.aidl new file mode 100644 index 000000000000..e65e72d27f49 --- /dev/null +++ b/graphics/java/android/graphics/Insets.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/graphics/Insets.aidl +** +** Copyright 2019, 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 android.graphics; + +parcelable Insets; diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java index 8258b575d63f..c64c7893564c 100644 --- a/graphics/java/android/graphics/Insets.java +++ b/graphics/java/android/graphics/Insets.java @@ -18,6 +18,8 @@ package android.graphics; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; /** * An Insets instance holds four integer offsets which describe changes to the four @@ -27,7 +29,7 @@ import android.annotation.Nullable; * Insets are immutable so may be treated as values. * */ -public final class Insets { +public final class Insets implements Parcelable { public static final Insets NONE = new Insets(0, 0, 0, 0); public final int left; @@ -73,7 +75,7 @@ public final class Insets { } /** - * Returns a Rect intance with the appropriate values. + * Returns a Rect instance with the appropriate values. * * @hide */ @@ -168,4 +170,29 @@ public final class Insets { ", bottom=" + bottom + '}'; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(left); + out.writeInt(top); + out.writeInt(right); + out.writeInt(bottom); + } + + public static final Parcelable.Creator<Insets> CREATOR = new Parcelable.Creator<Insets>() { + @Override + public Insets createFromParcel(Parcel in) { + return new Insets(in.readInt(), in.readInt(), in.readInt(), in.readInt()); + } + + @Override + public Insets[] newArray(int size) { + return new Insets[size]; + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java index 2c23c0ccf614..74ddc8f6b8fc 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -22,6 +22,8 @@ import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -32,6 +34,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; @@ -266,6 +269,24 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati } } }); + mActivityView.setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { + ActivityView activityView = (ActivityView) view; + // Here we assume that the position of the ActivityView on the screen + // remains regardless of IME status. When we move ActivityView, the + // forwardedInsets should be computed not against the current location + // and size, but against the post-moved location and size. + Point displaySize = new Point(); + view.getContext().getDisplay().getSize(displaySize); + int[] windowLocation = view.getLocationOnScreen(); + final int windowBottom = windowLocation[1] + view.getHeight(); + final int keyboardHeight = insets.getSystemWindowInsetBottom() + - insets.getStableInsetBottom(); + final int insetsBottom = Math.max(0, + windowBottom + keyboardHeight - displaySize.y); + activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom)); + return view.onApplyWindowInsets(insets); + }); + } return mActivityView; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 429bce050142..5cfc20b6339f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -139,6 +138,7 @@ import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; @@ -4946,4 +4946,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo portalWindowHandle.portalToDisplayId = mDisplayId; return portalWindowHandle; } + + /** + * @see IWindowManager#setForwardedInsets + */ + public void setForwardedInsets(Insets insets) { + if (insets == null) { + insets = Insets.NONE; + } + if (mDisplayPolicy.getForwardedInsets().equals(insets)) { + return; + } + mDisplayPolicy.setForwardedInsets(insets); + setLayoutNeeded(); + mWmService.mWindowPlacerLocked.requestTraversal(); + } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index f98ca3d8889d..2ee30ac5c8ff 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -104,6 +104,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.localLOGV; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread; @@ -111,6 +112,7 @@ import android.app.StatusBarManager; import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.graphics.Insets; import android.graphics.Rect; import android.hardware.input.InputManager; import android.hardware.power.V1_0.PowerHint; @@ -341,6 +343,16 @@ public class DisplayPolicy { private InputConsumer mInputConsumer = null; + /** + * The area covered by system windows which belong to another display. Forwarded insets is set + * in case this is a virtual display, this is displayed on another display that has insets, and + * the bounds of this display is overlapping with the insets of the host display (e.g. IME is + * displayed on the host display, and it covers a part of this virtual display.) + * The forwarded insets is used to compute display frames of this virtual display, which will + * be then used to layout windows in the virtual display. + */ + @NonNull private Insets mForwardedInsets = Insets.NONE; + // -------- PolicyHandler -------- private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1; private static final int MSG_REQUEST_TRANSIENT_BARS = 2; @@ -1364,6 +1376,15 @@ public class DisplayPolicy { displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top, displayFrames.mStable.top); } + + // In case this is a virtual display, and the host display has insets that overlap this + // virtual display, apply the insets of the overlapped area onto the current and content + // frame of this virtual display. This let us layout windows in the virtual display as + // expected when the window needs to avoid overlap with the system windows. + // TODO: Generalize the forwarded insets, so that we can handle system windows other than + // IME. + displayFrames.mCurrent.inset(mForwardedInsets); + displayFrames.mContent.inset(mForwardedInsets); } private void layoutScreenDecorWindows(DisplayFrames displayFrames) { @@ -2727,6 +2748,18 @@ public class DisplayPolicy { } } + /** + * @see IWindowManager#setForwardedInsets + */ + public void setForwardedInsets(@NonNull Insets forwardedInsets) { + mForwardedInsets = forwardedInsets; + } + + @NonNull + public Insets getForwardedInsets() { + return mForwardedInsets; + } + @NavigationBarPosition int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { if (navigationBarCanMove() && displayWidth > displayHeight) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3bb660825843..6c3e1f4ce1c2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -134,6 +134,7 @@ import android.content.pm.PackageManagerInternal; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Bitmap; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; @@ -6438,6 +6439,23 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public void setForwardedInsets(int displayId, Insets insets) throws RemoteException { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc == null) { + return; + } + final int callingUid = Binder.getCallingUid(); + final int displayOwnerUid = dc.getDisplay().getOwnerUid(); + if (callingUid != displayOwnerUid) { + throw new SecurityException( + "Only owner of the display can set ForwardedInsets to it."); + } + dc.setForwardedInsets(insets); + } + } + void intersectDisplayInsetBounds(Rect display, Rect insets, Rect inOutBounds) { mTmpRect3.set(display); mTmpRect3.inset(insets); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 845a09f44b82..4279c4152836 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -28,6 +28,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -37,6 +39,7 @@ import static org.junit.Assert.assertThat; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; +import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -350,6 +353,48 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE; + addWindow(mWindow); + + final int forwardedInsetBottom = 50; + mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom)); + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), + STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), + STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + } + + @Test + public void layoutWindowLw_withForwardInset_SoftInputAdjustNothing() { + synchronized (mWm.mGlobalLock) { + mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING; + addWindow(mWindow); + + final int forwardedInsetBottom = 50; + mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom)); + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + } + + @Test public void layoutHint_appWindow() { synchronized (mWm.mGlobalLock) { // Initialize DisplayFrames |