blob: dbf75705c073fd949c7fd706112c61e63dff7ba5 [file] [log] [blame]
Jorim Jaggif96c90a2018-09-26 16:55:15 +02001/*
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
17package android.view;
18
Yunfan Chenfae0aea2020-02-22 20:57:57 +090019import static android.view.InsetsState.ITYPE_CAPTION_BAR;
Jorim Jaggi23a02a92020-01-29 15:30:36 +010020import static android.view.InsetsState.ITYPE_IME;
21
Tiger Huanga24aab52020-03-13 17:06:35 +080022import android.annotation.NonNull;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010023import android.annotation.Nullable;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020024import android.graphics.Insets;
25import android.graphics.Rect;
26import android.os.Parcel;
27import android.os.Parcelable;
Tiger Huang332793b2019-10-29 23:21:27 +080028import android.view.InsetsState.InternalInsetsType;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020029
30import java.io.PrintWriter;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010031import java.util.Objects;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020032
33/**
34 * Represents the state of a single window generating insets for clients.
35 * @hide
36 */
37public class InsetsSource implements Parcelable {
38
Tiger Huang332793b2019-10-29 23:21:27 +080039 private final @InternalInsetsType int mType;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020040
41 /** Frame of the source in screen coordinate space */
42 private final Rect mFrame;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010043 private @Nullable Rect mVisibleFrame;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020044 private boolean mVisible;
45
46 private final Rect mTmpFrame = new Rect();
47
Tiger Huang332793b2019-10-29 23:21:27 +080048 public InsetsSource(@InternalInsetsType int type) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +020049 mType = type;
50 mFrame = new Rect();
Jorim Jaggi0dd0cf92019-12-27 15:17:44 +010051 mVisible = InsetsState.getDefaultVisibility(type);
Jorim Jaggif96c90a2018-09-26 16:55:15 +020052 }
53
54 public InsetsSource(InsetsSource other) {
55 mType = other.mType;
56 mFrame = new Rect(other.mFrame);
57 mVisible = other.mVisible;
Jorim Jaggi4d4fae42020-01-30 23:10:12 +010058 mVisibleFrame = other.mVisibleFrame != null
59 ? new Rect(other.mVisibleFrame)
60 : null;
Jorim Jaggif96c90a2018-09-26 16:55:15 +020061 }
62
Tiger Huange16645a2020-02-25 22:24:39 +080063 public void setFrame(int left, int top, int right, int bottom) {
64 mFrame.set(left, top, right, bottom);
65 }
66
Jorim Jaggif96c90a2018-09-26 16:55:15 +020067 public void setFrame(Rect frame) {
68 mFrame.set(frame);
69 }
70
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010071 public void setVisibleFrame(@Nullable Rect visibleFrame) {
72 mVisibleFrame = visibleFrame != null ? new Rect(visibleFrame) : visibleFrame;
73 }
74
Jorim Jaggif96c90a2018-09-26 16:55:15 +020075 public void setVisible(boolean visible) {
76 mVisible = visible;
77 }
78
Tiger Huang332793b2019-10-29 23:21:27 +080079 public @InternalInsetsType int getType() {
Jorim Jaggif96c90a2018-09-26 16:55:15 +020080 return mType;
81 }
82
83 public Rect getFrame() {
84 return mFrame;
85 }
86
Jorim Jaggi4e04eb22020-01-09 16:42:14 +010087 public @Nullable Rect getVisibleFrame() {
88 return mVisibleFrame;
89 }
90
Jorim Jaggie35c0592018-11-06 16:21:08 +010091 public boolean isVisible() {
92 return mVisible;
93 }
94
Tiger Huang618dbe022020-06-19 00:12:55 +080095 boolean isUserControllable() {
96 // If mVisibleFrame is null, it will be the same area as mFrame.
97 return mVisibleFrame == null || !mVisibleFrame.isEmpty();
98 }
99
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200100 /**
101 * Calculates the insets this source will cause to a client window.
102 *
103 * @param relativeFrame The frame to calculate the insets relative to.
104 * @param ignoreVisibility If true, always reports back insets even if source isn't visible.
Jorim Jaggi5bb571d2018-11-06 14:42:04 +0100105 * @return The resulting insets. The contract is that only one side will be occupied by a
106 * source.
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200107 */
108 public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100109 return calculateInsets(relativeFrame, mFrame, ignoreVisibility);
110 }
111
112 /**
113 * Like {@link #calculateInsets(Rect, boolean)}, but will return visible insets.
114 */
115 public Insets calculateVisibleInsets(Rect relativeFrame) {
116 return calculateInsets(relativeFrame, mVisibleFrame != null ? mVisibleFrame : mFrame,
117 false /* ignoreVisibility */);
118 }
119
120 private Insets calculateInsets(Rect relativeFrame, Rect frame, boolean ignoreVisibility) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200121 if (!ignoreVisibility && !mVisible) {
122 return Insets.NONE;
123 }
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900124 // During drag-move and drag-resizing, the caption insets position may not get updated
125 // before the app frame get updated. To layout the app content correctly during drag events,
126 // we always return the insets with the corresponding height covering the top.
127 if (getType() == ITYPE_CAPTION_BAR) {
128 return Insets.of(0, frame.height(), 0, 0);
129 }
Yunfan Chena07fb932020-04-27 18:35:14 +0900130 if (!getIntersection(frame, relativeFrame, mTmpFrame)) {
131 return Insets.NONE;
132 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200133
Jorim Jaggi23a02a92020-01-29 15:30:36 +0100134 // TODO: Currently, non-floating IME always intersects at bottom due to issues with cutout.
135 // However, we should let the policy decide from the server.
136 if (getType() == ITYPE_IME) {
137 return Insets.of(0, 0, 0, mTmpFrame.height());
138 }
139
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200140 // Intersecting at top/bottom
141 if (mTmpFrame.width() == relativeFrame.width()) {
142 if (mTmpFrame.top == relativeFrame.top) {
143 return Insets.of(0, mTmpFrame.height(), 0, 0);
Evan Roskye067ddf2020-04-24 18:28:25 -0700144 } else if (mTmpFrame.bottom == relativeFrame.bottom) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200145 return Insets.of(0, 0, 0, mTmpFrame.height());
146 }
Evan Roskye067ddf2020-04-24 18:28:25 -0700147 // TODO: remove when insets are shell-customizable.
148 // This is a hack that says "if this is a top-inset (eg statusbar), always apply it
149 // to the top". It is used when adjusting primary split for IME.
150 if (mTmpFrame.top == 0) {
151 return Insets.of(0, mTmpFrame.height(), 0, 0);
152 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200153 }
154 // Intersecting at left/right
155 else if (mTmpFrame.height() == relativeFrame.height()) {
156 if (mTmpFrame.left == relativeFrame.left) {
157 return Insets.of(mTmpFrame.width(), 0, 0, 0);
Evan Roskye067ddf2020-04-24 18:28:25 -0700158 } else if (mTmpFrame.right == relativeFrame.right) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200159 return Insets.of(0, 0, mTmpFrame.width(), 0);
160 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200161 }
Evan Roskye067ddf2020-04-24 18:28:25 -0700162 return Insets.NONE;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200163 }
164
Tiger Huanga24aab52020-03-13 17:06:35 +0800165 /**
166 * Outputs the intersection of two rectangles. The shared edges will also be counted in the
167 * intersection.
168 *
169 * @param a The first rectangle being intersected with.
170 * @param b The second rectangle being intersected with.
171 * @param out The rectangle which represents the intersection.
172 * @return {@code true} if there is any intersection.
173 */
174 private static boolean getIntersection(@NonNull Rect a, @NonNull Rect b, @NonNull Rect out) {
175 if (a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom) {
176 out.left = Math.max(a.left, b.left);
177 out.top = Math.max(a.top, b.top);
178 out.right = Math.min(a.right, b.right);
179 out.bottom = Math.min(a.bottom, b.bottom);
180 return true;
181 }
182 out.setEmpty();
183 return false;
184 }
185
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200186 public void dump(String prefix, PrintWriter pw) {
187 pw.print(prefix);
188 pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
189 pw.print(" frame="); pw.print(mFrame.toShortString());
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100190 if (mVisibleFrame != null) {
Tiger Huanga24aab52020-03-13 17:06:35 +0800191 pw.print(" visibleFrame="); pw.print(mVisibleFrame.toShortString());
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100192 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200193 pw.print(" visible="); pw.print(mVisible);
194 pw.println();
195 }
196
197 @Override
198 public boolean equals(Object o) {
Jorim Jaggied312592020-05-25 16:46:56 +0200199 return equals(o, false);
200 }
201
202 /**
203 * @param excludeInvisibleImeFrames If {@link InsetsState#ITYPE_IME} frames should be ignored
204 * when IME is not visible.
205 */
206 public boolean equals(Object o, boolean excludeInvisibleImeFrames) {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200207 if (this == o) return true;
208 if (o == null || getClass() != o.getClass()) return false;
209
210 InsetsSource that = (InsetsSource) o;
211
212 if (mType != that.mType) return false;
213 if (mVisible != that.mVisible) return false;
Jorim Jaggied312592020-05-25 16:46:56 +0200214 if (excludeInvisibleImeFrames && !mVisible && mType == ITYPE_IME) return true;
Jorim Jaggi4e04eb22020-01-09 16:42:14 +0100215 if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200216 return mFrame.equals(that.mFrame);
217 }
218
219 @Override
220 public int hashCode() {
221 int result = mType;
222 result = 31 * result + mFrame.hashCode();
Jorim Jaggi4d4fae42020-01-30 23:10:12 +0100223 result = 31 * result + (mVisibleFrame != null ? mVisibleFrame.hashCode() : 0);
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200224 result = 31 * result + (mVisible ? 1 : 0);
225 return result;
226 }
227
228 public InsetsSource(Parcel in) {
229 mType = in.readInt();
Jeff Sharkey1639e6b2020-04-22 17:36:53 -0600230 if (in.readInt() != 0) {
231 mFrame = Rect.CREATOR.createFromParcel(in);
232 } else {
233 mFrame = null;
234 }
235 if (in.readInt() != 0) {
236 mVisibleFrame = Rect.CREATOR.createFromParcel(in);
237 } else {
238 mVisibleFrame = null;
239 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200240 mVisible = in.readBoolean();
241 }
242
243 @Override
244 public int describeContents() {
245 return 0;
246 }
247
248 @Override
249 public void writeToParcel(Parcel dest, int flags) {
250 dest.writeInt(mType);
Jeff Sharkey1639e6b2020-04-22 17:36:53 -0600251 if (mFrame != null) {
252 dest.writeInt(1);
253 mFrame.writeToParcel(dest, 0);
254 } else {
255 dest.writeInt(0);
256 }
257 if (mVisibleFrame != null) {
258 dest.writeInt(1);
259 mVisibleFrame.writeToParcel(dest, 0);
260 } else {
261 dest.writeInt(0);
262 }
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200263 dest.writeBoolean(mVisible);
264 }
265
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900266 @Override
267 public String toString() {
268 return "InsetsSource: {"
269 + "mType=" + InsetsState.typeToString(mType)
270 + ", mFrame=" + mFrame.toShortString()
Tiger Huanga16634032020-02-05 17:10:03 +0800271 + ", mVisible=" + mVisible
Yunfan Chenb5d2db72019-12-06 15:43:43 +0900272 + "}";
273 }
274
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700275 public static final @android.annotation.NonNull Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
Jorim Jaggif96c90a2018-09-26 16:55:15 +0200276
277 public InsetsSource createFromParcel(Parcel in) {
278 return new InsetsSource(in);
279 }
280
281 public InsetsSource[] newArray(int size) {
282 return new InsetsSource[size];
283 }
284 };
285}