1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import static android.graphics.PointProto.X;
20 import static android.graphics.PointProto.Y;
21 import static android.view.InsetsSourceControlProto.LEASH;
22 import static android.view.InsetsSourceControlProto.POSITION;
23 import static android.view.InsetsSourceControlProto.TYPE_NUMBER;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.graphics.Insets;
28 import android.graphics.Point;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.util.proto.ProtoOutputStream;
32 import android.view.WindowInsets.Type.InsetsType;
33 
34 import java.io.PrintWriter;
35 import java.util.Arrays;
36 import java.util.Objects;
37 import java.util.function.Consumer;
38 
39 /**
40  * Represents a parcelable object to allow controlling a single {@link InsetsSource}.
41  * @hide
42  */
43 public class InsetsSourceControl implements Parcelable {
44 
45     private final int mId;
46     private final @InsetsType int mType;
47     private final @Nullable SurfaceControl mLeash;
48     private final boolean mInitiallyVisible;
49     private final Point mSurfacePosition;
50 
51     // This is used while playing an insets animation regardless of the relative frame. This would
52     // be the insets received by the bounds of its source window.
53     private Insets mInsetsHint;
54 
55     private boolean mSkipAnimationOnce;
56     private int mParcelableFlags;
57 
InsetsSourceControl(int id, @InsetsType int type, @Nullable SurfaceControl leash, boolean initiallyVisible, Point surfacePosition, Insets insetsHint)58     public InsetsSourceControl(int id, @InsetsType int type, @Nullable SurfaceControl leash,
59             boolean initiallyVisible, Point surfacePosition, Insets insetsHint) {
60         mId = id;
61         mType = type;
62         mLeash = leash;
63         mInitiallyVisible = initiallyVisible;
64         mSurfacePosition = surfacePosition;
65         mInsetsHint = insetsHint;
66     }
67 
InsetsSourceControl(InsetsSourceControl other)68     public InsetsSourceControl(InsetsSourceControl other) {
69         mId = other.mId;
70         mType = other.mType;
71         if (other.mLeash != null) {
72             mLeash = new SurfaceControl(other.mLeash, "InsetsSourceControl");
73         } else {
74             mLeash = null;
75         }
76         mInitiallyVisible = other.mInitiallyVisible;
77         mSurfacePosition = new Point(other.mSurfacePosition);
78         mInsetsHint = other.mInsetsHint;
79         mSkipAnimationOnce = other.getAndClearSkipAnimationOnce();
80     }
81 
InsetsSourceControl(Parcel in)82     public InsetsSourceControl(Parcel in) {
83         mId = in.readInt();
84         mType = in.readInt();
85         mLeash = in.readTypedObject(SurfaceControl.CREATOR);
86         mInitiallyVisible = in.readBoolean();
87         mSurfacePosition = in.readTypedObject(Point.CREATOR);
88         mInsetsHint = in.readTypedObject(Insets.CREATOR);
89         mSkipAnimationOnce = in.readBoolean();
90     }
91 
getId()92     public int getId() {
93         return mId;
94     }
95 
getType()96     public int getType() {
97         return mType;
98     }
99 
100     /**
101      * Gets the leash for controlling insets source. If the system is controlling the insets source,
102      * for example, transient bars, the client will receive fake controls without leash in it.
103      *
104      * @return the leash.
105      */
getLeash()106     public @Nullable SurfaceControl getLeash() {
107         return mLeash;
108     }
109 
isInitiallyVisible()110     public boolean isInitiallyVisible() {
111         return mInitiallyVisible;
112     }
113 
setSurfacePosition(int left, int top)114     public boolean setSurfacePosition(int left, int top) {
115         if (mSurfacePosition.equals(left, top)) {
116             return false;
117         }
118         mSurfacePosition.set(left, top);
119         return true;
120     }
121 
getSurfacePosition()122     public Point getSurfacePosition() {
123         return mSurfacePosition;
124     }
125 
setInsetsHint(Insets insets)126     public void setInsetsHint(Insets insets) {
127         mInsetsHint = insets;
128     }
129 
setInsetsHint(int left, int top, int right, int bottom)130     public void setInsetsHint(int left, int top, int right, int bottom) {
131         mInsetsHint = Insets.of(left, top, right, bottom);
132     }
133 
getInsetsHint()134     public Insets getInsetsHint() {
135         return mInsetsHint;
136     }
137 
setSkipAnimationOnce(boolean skipAnimation)138     public void setSkipAnimationOnce(boolean skipAnimation) {
139         mSkipAnimationOnce = skipAnimation;
140     }
141 
142     /**
143      * Get the state whether the current control needs to skip animation or not.
144      *
145      * Note that this is a one-time check that the state is only valid and can be called when
146      * {@link InsetsController#applyAnimation} to check if the current control can skip animation
147      * at this time, and then will clear the state value.
148      */
getAndClearSkipAnimationOnce()149     public boolean getAndClearSkipAnimationOnce() {
150         final boolean result = mSkipAnimationOnce;
151         mSkipAnimationOnce = false;
152         return result;
153     }
154 
setParcelableFlags(int parcelableFlags)155     public void setParcelableFlags(int parcelableFlags) {
156         mParcelableFlags = parcelableFlags;
157     }
158 
159     @Override
describeContents()160     public int describeContents() {
161         return 0;
162     }
163 
164     @Override
writeToParcel(Parcel dest, int flags)165     public void writeToParcel(Parcel dest, int flags) {
166         dest.writeInt(mId);
167         dest.writeInt(mType);
168         dest.writeTypedObject(mLeash, mParcelableFlags);
169         dest.writeBoolean(mInitiallyVisible);
170         dest.writeTypedObject(mSurfacePosition, mParcelableFlags);
171         dest.writeTypedObject(mInsetsHint, mParcelableFlags);
172         dest.writeBoolean(mSkipAnimationOnce);
173     }
174 
release(Consumer<SurfaceControl> surfaceReleaseConsumer)175     public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
176         if (mLeash != null) {
177             surfaceReleaseConsumer.accept(mLeash);
178         }
179     }
180 
181     @Override
equals(@ullable Object o)182     public boolean equals(@Nullable Object o) {
183         if (this == o) {
184             return true;
185         }
186         if (o == null || getClass() != o.getClass()) {
187             return false;
188         }
189         final InsetsSourceControl that = (InsetsSourceControl) o;
190         final SurfaceControl thatLeash = that.mLeash;
191         return mId == that.mId
192                 && mType == that.mType
193                 && ((mLeash == thatLeash)
194                         || (mLeash != null && thatLeash != null && mLeash.isSameSurface(thatLeash)))
195                 && mInitiallyVisible == that.mInitiallyVisible
196                 && mSurfacePosition.equals(that.mSurfacePosition)
197                 && mInsetsHint.equals(that.mInsetsHint)
198                 && mSkipAnimationOnce == that.mSkipAnimationOnce;
199     }
200 
201     @Override
hashCode()202     public int hashCode() {
203         return Objects.hash(mId, mType, mLeash, mInitiallyVisible, mSurfacePosition, mInsetsHint,
204                 mSkipAnimationOnce);
205     }
206 
207     @Override
toString()208     public String toString() {
209         return "InsetsSourceControl: {" + Integer.toHexString(mId)
210                 + " mType=" + WindowInsets.Type.toString(mType)
211                 + (mInitiallyVisible ? " initiallyVisible" : "")
212                 + " mSurfacePosition=" + mSurfacePosition
213                 + " mInsetsHint=" + mInsetsHint
214                 + (mSkipAnimationOnce ? " skipAnimationOnce" : "")
215                 + "}";
216     }
217 
dump(String prefix, PrintWriter pw)218     public void dump(String prefix, PrintWriter pw) {
219         pw.print(prefix);
220         pw.print("InsetsSourceControl mId="); pw.print(Integer.toHexString(mId));
221         pw.print(" mType="); pw.print(WindowInsets.Type.toString(mType));
222         pw.print(" mLeash="); pw.print(mLeash);
223         pw.print(" mInitiallyVisible="); pw.print(mInitiallyVisible);
224         pw.print(" mSurfacePosition="); pw.print(mSurfacePosition);
225         pw.print(" mInsetsHint="); pw.print(mInsetsHint);
226         pw.print(" mSkipAnimationOnce="); pw.print(mSkipAnimationOnce);
227         pw.println();
228     }
229 
230     public static final @NonNull Creator<InsetsSourceControl> CREATOR = new Creator<>() {
231         public InsetsSourceControl createFromParcel(Parcel in) {
232             return new InsetsSourceControl(in);
233         }
234 
235         public InsetsSourceControl[] newArray(int size) {
236             return new InsetsSourceControl[size];
237         }
238     };
239 
240     /**
241      * Export the state of {@link InsetsSourceControl} into a protocol buffer output stream.
242      *
243      * @param proto   Stream to write the state to
244      * @param fieldId FieldId of InsetsSource as defined in the parent message
245      */
dumpDebug(ProtoOutputStream proto, long fieldId)246     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
247         final long token = proto.start(fieldId);
248         final long surfaceToken = proto.start(POSITION);
249         proto.write(X, mSurfacePosition.x);
250         proto.write(Y, mSurfacePosition.y);
251         proto.end(surfaceToken);
252 
253         if (mLeash != null) {
254             mLeash.dumpDebug(proto, LEASH);
255         }
256 
257         proto.write(TYPE_NUMBER, mType);
258         proto.end(token);
259     }
260 
261     /**
262      * Used to obtain the array from the argument of a binder call. In this way, the length of the
263      * array can be dynamic.
264      */
265     public static class Array implements Parcelable {
266 
267         private @Nullable InsetsSourceControl[] mControls;
268 
Array()269         public Array() {
270         }
271 
272         /**
273          * @param copyControls whether or not to make a copy of the each {@link InsetsSourceControl}
274          */
Array(@onNull Array other, boolean copyControls)275         public Array(@NonNull Array other, boolean copyControls) {
276             setTo(other, copyControls);
277         }
278 
Array(@onNull Parcel in)279         public Array(@NonNull Parcel in) {
280             readFromParcel(in);
281         }
282 
283         /** Updates the current Array to the given Array. */
setTo(@onNull Array other, boolean copyControls)284         public void setTo(@NonNull Array other, boolean copyControls) {
285             set(other.mControls, copyControls);
286         }
287 
288         /** Updates the current controls to the given controls. */
set(@ullable InsetsSourceControl[] controls, boolean copyControls)289         public void set(@Nullable InsetsSourceControl[] controls, boolean copyControls) {
290             if (controls == null || !copyControls) {
291                 mControls = controls;
292                 return;
293             }
294             // Make a copy of the array.
295             mControls = new InsetsSourceControl[controls.length];
296             for (int i = mControls.length - 1; i >= 0; i--) {
297                 if (controls[i] != null) {
298                     mControls[i] = new InsetsSourceControl(controls[i]);
299                 }
300             }
301         }
302 
303         /** Gets the controls. */
get()304         public @Nullable InsetsSourceControl[] get() {
305             return mControls;
306         }
307 
308         /** Cleanup {@link SurfaceControl} stored in controls to prevent leak. */
release()309         public void release() {
310             if (mControls == null) {
311                 return;
312             }
313             for (InsetsSourceControl control : mControls) {
314                 if (control != null) {
315                     control.release(SurfaceControl::release);
316                 }
317             }
318         }
319 
320         /** Sets the given flags to all controls. */
setParcelableFlags(int parcelableFlags)321         public void setParcelableFlags(int parcelableFlags) {
322             if (mControls == null) {
323                 return;
324             }
325             for (InsetsSourceControl control : mControls) {
326                 if (control != null) {
327                     control.setParcelableFlags(parcelableFlags);
328                 }
329             }
330         }
331 
332         @Override
describeContents()333         public int describeContents() {
334             return 0;
335         }
336 
readFromParcel(Parcel in)337         public void readFromParcel(Parcel in) {
338             mControls = in.createTypedArray(InsetsSourceControl.CREATOR);
339         }
340 
341         @Override
writeToParcel(Parcel out, int flags)342         public void writeToParcel(Parcel out, int flags) {
343             out.writeTypedArray(mControls, flags);
344         }
345 
346         public static final @NonNull Creator<Array> CREATOR = new Creator<>() {
347             public Array createFromParcel(Parcel in) {
348                 return new Array(in);
349             }
350 
351             public Array[] newArray(int size) {
352                 return new Array[size];
353             }
354         };
355 
356         @Override
equals(@ullable Object o)357         public boolean equals(@Nullable Object o) {
358             if (this == o) {
359                 return true;
360             }
361             if (o == null || getClass() != o.getClass()) {
362                 return false;
363             }
364             final InsetsSourceControl.Array other = (InsetsSourceControl.Array) o;
365             return Arrays.equals(mControls, other.mControls);
366         }
367 
368         @Override
hashCode()369         public int hashCode() {
370             return Arrays.hashCode(mControls);
371         }
372     }
373 }
374