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;
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.Objects;
36 import java.util.function.Consumer;
37 
38 /**
39  * Represents a parcelable object to allow controlling a single {@link InsetsSource}.
40  * @hide
41  */
42 public class InsetsSourceControl implements Parcelable {
43 
44     private final int mId;
45     private final @InsetsType int mType;
46     private final @Nullable SurfaceControl mLeash;
47     private final boolean mInitiallyVisible;
48     private final Point mSurfacePosition;
49 
50     // This is used while playing an insets animation regardless of the relative frame. This would
51     // be the insets received by the bounds of its source window.
52     private Insets mInsetsHint;
53 
54     private boolean mSkipAnimationOnce;
55     private int mParcelableFlags;
56 
InsetsSourceControl(int id, @InsetsType int type, @Nullable SurfaceControl leash, boolean initiallyVisible, Point surfacePosition, Insets insetsHint)57     public InsetsSourceControl(int id, @InsetsType int type, @Nullable SurfaceControl leash,
58             boolean initiallyVisible, Point surfacePosition, Insets insetsHint) {
59         mId = id;
60         mType = type;
61         mLeash = leash;
62         mInitiallyVisible = initiallyVisible;
63         mSurfacePosition = surfacePosition;
64         mInsetsHint = insetsHint;
65     }
66 
InsetsSourceControl(InsetsSourceControl other)67     public InsetsSourceControl(InsetsSourceControl other) {
68         mId = other.mId;
69         mType = other.mType;
70         if (other.mLeash != null) {
71             mLeash = new SurfaceControl(other.mLeash, "InsetsSourceControl");
72         } else {
73             mLeash = null;
74         }
75         mInitiallyVisible = other.mInitiallyVisible;
76         mSurfacePosition = new Point(other.mSurfacePosition);
77         mInsetsHint = other.mInsetsHint;
78         mSkipAnimationOnce = other.getAndClearSkipAnimationOnce();
79     }
80 
InsetsSourceControl(Parcel in)81     public InsetsSourceControl(Parcel in) {
82         mId = in.readInt();
83         mType = in.readInt();
84         mLeash = in.readTypedObject(SurfaceControl.CREATOR);
85         mInitiallyVisible = in.readBoolean();
86         mSurfacePosition = in.readTypedObject(Point.CREATOR);
87         mInsetsHint = in.readTypedObject(Insets.CREATOR);
88         mSkipAnimationOnce = in.readBoolean();
89     }
90 
getId()91     public int getId() {
92         return mId;
93     }
94 
getType()95     public int getType() {
96         return mType;
97     }
98 
99     /**
100      * Gets the leash for controlling insets source. If the system is controlling the insets source,
101      * for example, transient bars, the client will receive fake controls without leash in it.
102      *
103      * @return the leash.
104      */
getLeash()105     public @Nullable SurfaceControl getLeash() {
106         return mLeash;
107     }
108 
isInitiallyVisible()109     public boolean isInitiallyVisible() {
110         return mInitiallyVisible;
111     }
112 
setSurfacePosition(int left, int top)113     public boolean setSurfacePosition(int left, int top) {
114         if (mSurfacePosition.equals(left, top)) {
115             return false;
116         }
117         mSurfacePosition.set(left, top);
118         return true;
119     }
120 
getSurfacePosition()121     public Point getSurfacePosition() {
122         return mSurfacePosition;
123     }
124 
setInsetsHint(Insets insets)125     public void setInsetsHint(Insets insets) {
126         mInsetsHint = insets;
127     }
128 
setInsetsHint(int left, int top, int right, int bottom)129     public void setInsetsHint(int left, int top, int right, int bottom) {
130         mInsetsHint = Insets.of(left, top, right, bottom);
131     }
132 
getInsetsHint()133     public Insets getInsetsHint() {
134         return mInsetsHint;
135     }
136 
setSkipAnimationOnce(boolean skipAnimation)137     public void setSkipAnimationOnce(boolean skipAnimation) {
138         mSkipAnimationOnce = skipAnimation;
139     }
140 
141     /**
142      * Get the state whether the current control needs to skip animation or not.
143      *
144      * Note that this is a one-time check that the state is only valid and can be called when
145      * {@link InsetsController#applyAnimation} to check if the current control can skip animation
146      * at this time, and then will clear the state value.
147      */
getAndClearSkipAnimationOnce()148     public boolean getAndClearSkipAnimationOnce() {
149         final boolean result = mSkipAnimationOnce;
150         mSkipAnimationOnce = false;
151         return result;
152     }
153 
setParcelableFlags(int parcelableFlags)154     public void setParcelableFlags(int parcelableFlags) {
155         mParcelableFlags = parcelableFlags;
156     }
157 
158     @Override
describeContents()159     public int describeContents() {
160         return 0;
161     }
162 
163     @Override
writeToParcel(Parcel dest, int flags)164     public void writeToParcel(Parcel dest, int flags) {
165         dest.writeInt(mId);
166         dest.writeInt(mType);
167         dest.writeTypedObject(mLeash, mParcelableFlags);
168         dest.writeBoolean(mInitiallyVisible);
169         dest.writeTypedObject(mSurfacePosition, mParcelableFlags);
170         dest.writeTypedObject(mInsetsHint, mParcelableFlags);
171         dest.writeBoolean(mSkipAnimationOnce);
172     }
173 
release(Consumer<SurfaceControl> surfaceReleaseConsumer)174     public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
175         if (mLeash != null) {
176             surfaceReleaseConsumer.accept(mLeash);
177         }
178     }
179 
180     @Override
equals(@ullable Object o)181     public boolean equals(@Nullable Object o) {
182         if (this == o) {
183             return true;
184         }
185         if (o == null || getClass() != o.getClass()) {
186             return false;
187         }
188         final InsetsSourceControl that = (InsetsSourceControl) o;
189         final SurfaceControl thatLeash = that.mLeash;
190         return mId == that.mId
191                 && mType == that.mType
192                 && ((mLeash == thatLeash)
193                         || (mLeash != null && thatLeash != null && mLeash.isSameSurface(thatLeash)))
194                 && mInitiallyVisible == that.mInitiallyVisible
195                 && mSurfacePosition.equals(that.mSurfacePosition)
196                 && mInsetsHint.equals(that.mInsetsHint)
197                 && mSkipAnimationOnce == that.mSkipAnimationOnce;
198     }
199 
200     @Override
hashCode()201     public int hashCode() {
202         return Objects.hash(mId, mType, mLeash, mInitiallyVisible, mSurfacePosition, mInsetsHint,
203                 mSkipAnimationOnce);
204     }
205 
206     @Override
toString()207     public String toString() {
208         return "InsetsSourceControl: {" + Integer.toHexString(mId)
209                 + " mType=" + WindowInsets.Type.toString(mType)
210                 + (mInitiallyVisible ? " initiallyVisible" : "")
211                 + " mSurfacePosition=" + mSurfacePosition
212                 + " mInsetsHint=" + mInsetsHint
213                 + (mSkipAnimationOnce ? " skipAnimationOnce" : "")
214                 + "}";
215     }
216 
dump(String prefix, PrintWriter pw)217     public void dump(String prefix, PrintWriter pw) {
218         pw.print(prefix);
219         pw.print("InsetsSourceControl mId="); pw.print(Integer.toHexString(mId));
220         pw.print(" mType="); pw.print(WindowInsets.Type.toString(mType));
221         pw.print(" mLeash="); pw.print(mLeash);
222         pw.print(" mInitiallyVisible="); pw.print(mInitiallyVisible);
223         pw.print(" mSurfacePosition="); pw.print(mSurfacePosition);
224         pw.print(" mInsetsHint="); pw.print(mInsetsHint);
225         pw.print(" mSkipAnimationOnce="); pw.print(mSkipAnimationOnce);
226         pw.println();
227     }
228 
229     public static final @NonNull Creator<InsetsSourceControl> CREATOR = new Creator<>() {
230         public InsetsSourceControl createFromParcel(Parcel in) {
231             return new InsetsSourceControl(in);
232         }
233 
234         public InsetsSourceControl[] newArray(int size) {
235             return new InsetsSourceControl[size];
236         }
237     };
238 
239     /**
240      * Export the state of {@link InsetsSourceControl} into a protocol buffer output stream.
241      *
242      * @param proto   Stream to write the state to
243      * @param fieldId FieldId of InsetsSource as defined in the parent message
244      */
dumpDebug(ProtoOutputStream proto, long fieldId)245     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
246         final long token = proto.start(fieldId);
247         proto.write(TYPE, WindowInsets.Type.toString(mType));
248 
249         final long surfaceToken = proto.start(POSITION);
250         proto.write(X, mSurfacePosition.x);
251         proto.write(Y, mSurfacePosition.y);
252         proto.end(surfaceToken);
253 
254         if (mLeash != null) {
255             mLeash.dumpDebug(proto, LEASH);
256         }
257         proto.end(token);
258     }
259 
260     /**
261      * Used to obtain the array from the argument of a binder call. In this way, the length of the
262      * array can be dynamic.
263      */
264     public static class Array implements Parcelable {
265 
266         private @Nullable InsetsSourceControl[] mControls;
267 
Array()268         public Array() {
269         }
270 
Array(Parcel in)271         public Array(Parcel in) {
272             readFromParcel(in);
273         }
274 
set(@ullable InsetsSourceControl[] controls)275         public void set(@Nullable InsetsSourceControl[] controls) {
276             mControls = controls;
277         }
278 
get()279         public @Nullable InsetsSourceControl[] get() {
280             return mControls;
281         }
282 
283         @Override
describeContents()284         public int describeContents() {
285             return 0;
286         }
287 
readFromParcel(Parcel in)288         public void readFromParcel(Parcel in) {
289             mControls = in.createTypedArray(InsetsSourceControl.CREATOR);
290         }
291 
292         @Override
writeToParcel(Parcel out, int flags)293         public void writeToParcel(Parcel out, int flags) {
294             out.writeTypedArray(mControls, flags);
295         }
296 
297         public static final @NonNull Creator<Array> CREATOR = new Creator<>() {
298             public Array createFromParcel(Parcel in) {
299                 return new Array(in);
300             }
301 
302             public Array[] newArray(int size) {
303                 return new Array[size];
304             }
305         };
306     }
307 }
308