Merge "Set insets on the virtual display to avoid IME covering the bubble."

This commit is contained in:
Issei Suzuki
2019-02-08 14:06:41 +00:00
committed by Android (Google) Code Review
10 changed files with 220 additions and 4 deletions

View File

@@ -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;

View File

@@ -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();
}
@@ -453,6 +458,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

View File

@@ -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;
@@ -379,6 +380,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

View File

@@ -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;

View File

@@ -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];
}
};
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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;
@@ -349,6 +352,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) {