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