diff options
Diffstat (limited to 'core/java/android/window/WindowContainerTransaction.java')
| -rw-r--r-- | core/java/android/window/WindowContainerTransaction.java | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java new file mode 100644 index 000000000000..483dec66cfd3 --- /dev/null +++ b/core/java/android/window/WindowContainerTransaction.java @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2020 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.window; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.WindowConfiguration; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.window.IWindowContainer; +import android.view.SurfaceControl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Represents a collection of operations on some WindowContainers that should be applied all at + * once. + * + * @hide + */ +public class WindowContainerTransaction implements Parcelable { + private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>(); + + // Flat list because re-order operations are order-dependent + private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>(); + + public WindowContainerTransaction() {} + + protected WindowContainerTransaction(Parcel in) { + in.readMap(mChanges, null /* loader */); + in.readList(mHierarchyOps, null /* loader */); + } + + private Change getOrCreateChange(IBinder token) { + Change out = mChanges.get(token); + if (out == null) { + out = new Change(); + mChanges.put(token, out); + } + return out; + } + + /** + * Resize a container. + */ + public WindowContainerTransaction setBounds(IWindowContainer container, Rect bounds) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mConfiguration.windowConfiguration.setBounds(bounds); + chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; + chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS; + return this; + } + + /** + * Resize a container's app bounds. This is the bounds used to report appWidth/Height to an + * app's DisplayInfo. It is derived by subtracting the overlapping portion of the navbar from + * the full bounds. + */ + public WindowContainerTransaction setAppBounds(IWindowContainer container, Rect appBounds) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mConfiguration.windowConfiguration.setAppBounds(appBounds); + chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION; + chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; + return this; + } + + /** + * Resize a container's configuration size. The configuration size is what gets reported to the + * app via screenWidth/HeightDp and influences which resources get loaded. This size is + * derived by subtracting the overlapping portions of both the statusbar and the navbar from + * the full bounds. + */ + public WindowContainerTransaction setScreenSizeDp(IWindowContainer container, int w, int h) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mConfiguration.screenWidthDp = w; + chg.mConfiguration.screenHeightDp = h; + chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE; + return this; + } + + /** + * Notify activies within the hiearchy of a container that they have entered picture-in-picture + * mode with the given bounds. + */ + public WindowContainerTransaction scheduleFinishEnterPip(IWindowContainer container, + Rect bounds) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mPinnedBounds = new Rect(bounds); + chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK; + + return this; + } + + /** + * Send a SurfaceControl transaction to the server, which the server will apply in sync with + * the next bounds change. As this uses deferred transaction and not BLAST it is only + * able to sync with a single window, and the first visible window in this hierarchy of type + * BASE_APPLICATION to resize will be used. If there are bound changes included in this + * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl + * transaction will be synced with those bounds. If there are no changes, then + * the SurfaceControl transaction will be synced with the next bounds change. This means + * that you can call this, apply the WindowContainer transaction, and then later call + * dismissPip() to achieve synchronization. + */ + public WindowContainerTransaction setBoundsChangeTransaction(IWindowContainer container, + SurfaceControl.Transaction t) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mBoundsChangeTransaction = t; + chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION; + return this; + } + + /** + * Set the windowing mode of children of a given root task, without changing + * the windowing mode of the Task itself. This can be used during transitions + * for example to make the activity render it's fullscreen configuration + * while the Task is still in PIP, so you can complete the animation. + * + * TODO(b/134365562): Can be removed once TaskOrg drives full-screen + */ + public WindowContainerTransaction setActivityWindowingMode(IWindowContainer container, + int windowingMode) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mActivityWindowingMode = windowingMode; + return this; + } + + /** + * Sets the windowing mode of the given container. + */ + public WindowContainerTransaction setWindowingMode(IWindowContainer container, + int windowingMode) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mWindowingMode = windowingMode; + return this; + } + + /** + * Sets whether a container or any of its children can be focusable. When {@code false}, no + * child can be focused; however, when {@code true}, it is still possible for children to be + * non-focusable due to WM policy. + */ + public WindowContainerTransaction setFocusable(IWindowContainer container, boolean focusable) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mFocusable = focusable; + chg.mChangeMask |= Change.CHANGE_FOCUSABLE; + return this; + } + + /** + * Sets whether a container or its children should be hidden. When {@code false}, the existing + * visibility of the container applies, but when {@code true} the container will be forced + * to be hidden. + */ + public WindowContainerTransaction setHidden(IWindowContainer container, boolean hidden) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mHidden = hidden; + chg.mChangeMask |= Change.CHANGE_HIDDEN; + return this; + } + + /** + * Set the smallestScreenWidth of a container. + */ + public WindowContainerTransaction setSmallestScreenWidthDp(IWindowContainer container, + int widthDp) { + Change cfg = getOrCreateChange(container.asBinder()); + cfg.mConfiguration.smallestScreenWidthDp = widthDp; + cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; + return this; + } + + /** + * Reparents a container into another one. The effect of a {@code null} parent can vary. For + * example, reparenting a stack to {@code null} will reparent it to its display. + * + * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to + * the bottom. + */ + public WindowContainerTransaction reparent(@NonNull IWindowContainer child, + @Nullable IWindowContainer parent, boolean onTop) { + mHierarchyOps.add(new HierarchyOp(child.asBinder(), + parent == null ? null : parent.asBinder(), onTop)); + return this; + } + + /** + * Reorders a container within its parent. + * + * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to + * the bottom. + */ + public WindowContainerTransaction reorder(@NonNull IWindowContainer child, boolean onTop) { + mHierarchyOps.add(new HierarchyOp(child.asBinder(), onTop)); + return this; + } + + public Map<IBinder, Change> getChanges() { + return mChanges; + } + + public List<HierarchyOp> getHierarchyOps() { + return mHierarchyOps; + } + + @Override + public String toString() { + return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps + + " }"; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeMap(mChanges); + dest.writeList(mHierarchyOps); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<WindowContainerTransaction> CREATOR = + new Creator<WindowContainerTransaction>() { + @Override + public WindowContainerTransaction createFromParcel(Parcel in) { + return new WindowContainerTransaction(in); + } + + @Override + public WindowContainerTransaction[] newArray(int size) { + return new WindowContainerTransaction[size]; + } + }; + + /** + * Holds changes on a single WindowContainer including Configuration changes. + * + * @hide + */ + public static class Change implements Parcelable { + public static final int CHANGE_FOCUSABLE = 1; + public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1; + public static final int CHANGE_PIP_CALLBACK = 1 << 2; + public static final int CHANGE_HIDDEN = 1 << 3; + + private final Configuration mConfiguration = new Configuration(); + private boolean mFocusable = true; + private boolean mHidden = false; + private int mChangeMask = 0; + private @ActivityInfo.Config int mConfigSetMask = 0; + private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; + + private Rect mPinnedBounds = null; + private SurfaceControl.Transaction mBoundsChangeTransaction = null; + + private int mActivityWindowingMode = -1; + private int mWindowingMode = -1; + + public Change() {} + + protected Change(Parcel in) { + mConfiguration.readFromParcel(in); + mFocusable = in.readBoolean(); + mHidden = in.readBoolean(); + mChangeMask = in.readInt(); + mConfigSetMask = in.readInt(); + mWindowSetMask = in.readInt(); + if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) { + mPinnedBounds = new Rect(); + mPinnedBounds.readFromParcel(in); + } + if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) { + mBoundsChangeTransaction = + SurfaceControl.Transaction.CREATOR.createFromParcel(in); + } + + mWindowingMode = in.readInt(); + mActivityWindowingMode = in.readInt(); + } + + public int getWindowingMode() { + return mWindowingMode; + } + + public int getActivityWindowingMode() { + return mActivityWindowingMode; + } + + public Configuration getConfiguration() { + return mConfiguration; + } + + /** Gets the requested focusable state */ + public boolean getFocusable() { + if ((mChangeMask & CHANGE_FOCUSABLE) == 0) { + throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first"); + } + return mFocusable; + } + + /** Gets the requested hidden state */ + public boolean getHidden() { + if ((mChangeMask & CHANGE_HIDDEN) == 0) { + throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first"); + } + return mHidden; + } + + public int getChangeMask() { + return mChangeMask; + } + + @ActivityInfo.Config + public int getConfigSetMask() { + return mConfigSetMask; + } + + @WindowConfiguration.WindowConfig + public int getWindowSetMask() { + return mWindowSetMask; + } + + /** + * Returns the bounds to be used for scheduling the enter pip callback + * or null if no callback is to be scheduled. + */ + public Rect getEnterPipBounds() { + return mPinnedBounds; + } + + public SurfaceControl.Transaction getBoundsChangeTransaction() { + return mBoundsChangeTransaction; + } + + @Override + public String toString() { + final boolean changesBounds = + (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 + && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) + != 0); + final boolean changesAppBounds = + (mConfigSetMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 + && ((mWindowSetMask & WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS) + != 0); + final boolean changesSs = (mConfigSetMask & ActivityInfo.CONFIG_SCREEN_SIZE) != 0; + final boolean changesSss = + (mConfigSetMask & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0; + StringBuilder sb = new StringBuilder(); + sb.append('{'); + if (changesBounds) { + sb.append("bounds:" + mConfiguration.windowConfiguration.getBounds() + ","); + } + if (changesAppBounds) { + sb.append("appbounds:" + mConfiguration.windowConfiguration.getAppBounds() + ","); + } + if (changesSss) { + sb.append("ssw:" + mConfiguration.smallestScreenWidthDp + ","); + } + if (changesSs) { + sb.append("sw/h:" + mConfiguration.screenWidthDp + "x" + + mConfiguration.screenHeightDp + ","); + } + if ((mChangeMask & CHANGE_FOCUSABLE) != 0) { + sb.append("focusable:" + mFocusable + ","); + } + sb.append("}"); + return sb.toString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + mConfiguration.writeToParcel(dest, flags); + dest.writeBoolean(mFocusable); + dest.writeBoolean(mHidden); + dest.writeInt(mChangeMask); + dest.writeInt(mConfigSetMask); + dest.writeInt(mWindowSetMask); + + if (mPinnedBounds != null) { + mPinnedBounds.writeToParcel(dest, flags); + } + if (mBoundsChangeTransaction != null) { + mBoundsChangeTransaction.writeToParcel(dest, flags); + } + + dest.writeInt(mWindowingMode); + dest.writeInt(mActivityWindowingMode); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<Change> CREATOR = new Creator<Change>() { + @Override + public Change createFromParcel(Parcel in) { + return new Change(in); + } + + @Override + public Change[] newArray(int size) { + return new Change[size]; + } + }; + } + + /** + * Holds information about a reparent/reorder operation in the hierarchy. This is separate from + * Changes because they must be executed in the same order that they are added. + */ + public static class HierarchyOp implements Parcelable { + private final IBinder mContainer; + + // If this is same as mContainer, then only change position, don't reparent. + private final IBinder mReparent; + + // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. + private final boolean mToTop; + + public HierarchyOp(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { + mContainer = container; + mReparent = reparent; + mToTop = toTop; + } + + public HierarchyOp(@NonNull IBinder container, boolean toTop) { + mContainer = container; + mReparent = container; + mToTop = toTop; + } + + protected HierarchyOp(Parcel in) { + mContainer = in.readStrongBinder(); + mReparent = in.readStrongBinder(); + mToTop = in.readBoolean(); + } + + public boolean isReparent() { + return mContainer != mReparent; + } + + @Nullable + public IBinder getNewParent() { + return mReparent; + } + + @NonNull + public IBinder getContainer() { + return mContainer; + } + + public boolean getToTop() { + return mToTop; + } + + @Override + public String toString() { + if (isReparent()) { + return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") + + mReparent + "}"; + } else { + return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mContainer); + dest.writeStrongBinder(mReparent); + dest.writeBoolean(mToTop); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() { + @Override + public HierarchyOp createFromParcel(Parcel in) { + return new HierarchyOp(in); + } + + @Override + public HierarchyOp[] newArray(int size) { + return new HierarchyOp[size]; + } + }; + } +} |
