blob: a106b2c4726dbb599096b061890dfd955484aac5 [file] [log] [blame]
Jorim Jaggi57157ac2019-01-22 19:01:48 +01001/*
2 * Copyright (C) 2019 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
Adrian Roos5ad8cd22020-03-12 18:30:54 +000019import android.annotation.NonNull;
20import android.annotation.Nullable;
Jorim Jaggi57157ac2019-01-22 19:01:48 +010021import android.os.CancellationSignal;
22import android.view.WindowInsets.Type.InsetsType;
23import android.view.animation.Interpolator;
24
25import com.android.internal.annotations.VisibleForTesting;
26
27import java.util.ArrayList;
28
29/**
30 * An insets controller that keeps track of pending requests. This is such that an app can freely
31 * use {@link WindowInsetsController} before the view root is attached during activity startup.
32 * @hide
33 */
34public class PendingInsetsController implements WindowInsetsController {
35
36 private static final int KEEP_BEHAVIOR = -1;
37 private final ArrayList<PendingRequest> mRequests = new ArrayList<>();
38 private @Appearance int mAppearance;
39 private @Appearance int mAppearanceMask;
40 private @Behavior int mBehavior = KEEP_BEHAVIOR;
41 private final InsetsState mDummyState = new InsetsState();
42 private InsetsController mReplayedInsetsController;
Jorim Jaggied35b172020-03-06 00:13:57 +010043 private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
44 = new ArrayList<>();
Yunfan Chenfae0aea2020-02-22 20:57:57 +090045 private int mCaptionInsetsHeight = 0;
Jorim Jaggi57157ac2019-01-22 19:01:48 +010046
47 @Override
48 public void show(int types) {
49 if (mReplayedInsetsController != null) {
50 mReplayedInsetsController.show(types);
51 } else {
52 mRequests.add(new ShowRequest(types));
53 }
54 }
55
56 @Override
57 public void hide(int types) {
58 if (mReplayedInsetsController != null) {
59 mReplayedInsetsController.hide(types);
60 } else {
61 mRequests.add(new HideRequest(types));
62 }
63 }
64
65 @Override
Jorim Jaggi57157ac2019-01-22 19:01:48 +010066 public void setSystemBarsAppearance(int appearance, int mask) {
67 if (mReplayedInsetsController != null) {
68 mReplayedInsetsController.setSystemBarsAppearance(appearance, mask);
69 } else {
70 mAppearance = (mAppearance & ~mask) | (appearance & mask);
71 mAppearanceMask |= mask;
72 }
73 }
74
75 @Override
76 public int getSystemBarsAppearance() {
77 if (mReplayedInsetsController != null) {
78 return mReplayedInsetsController.getSystemBarsAppearance();
79 }
80 return mAppearance;
81 }
82
83 @Override
Yunfan Chenfae0aea2020-02-22 20:57:57 +090084 public void setCaptionInsetsHeight(int height) {
85 mCaptionInsetsHeight = height;
86 }
87
88 @Override
Jorim Jaggi57157ac2019-01-22 19:01:48 +010089 public void setSystemBarsBehavior(int behavior) {
90 if (mReplayedInsetsController != null) {
91 mReplayedInsetsController.setSystemBarsBehavior(behavior);
92 } else {
93 mBehavior = behavior;
94 }
95 }
96
97 @Override
98 public int getSystemBarsBehavior() {
99 if (mReplayedInsetsController != null) {
100 return mReplayedInsetsController.getSystemBarsBehavior();
101 }
102 return mBehavior;
103 }
104
105 @Override
106 public InsetsState getState() {
107 return mDummyState;
108 }
109
Jorim Jaggied35b172020-03-06 00:13:57 +0100110 @Override
111 public void addOnControllableInsetsChangedListener(
112 OnControllableInsetsChangedListener listener) {
113 if (mReplayedInsetsController != null) {
114 mReplayedInsetsController.addOnControllableInsetsChangedListener(listener);
115 } else {
116 mControllableInsetsChangedListeners.add(listener);
117 listener.onControllableInsetsChanged(this, 0);
118 }
119 }
120
121 @Override
122 public void removeOnControllableInsetsChangedListener(
123 OnControllableInsetsChangedListener listener) {
124 if (mReplayedInsetsController != null) {
125 mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener);
126 } else {
127 mControllableInsetsChangedListeners.remove(listener);
128 }
129 }
130
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100131 /**
132 * Replays the commands on {@code controller} and attaches it to this instance such that any
133 * calls will be forwarded to the real instance in the future.
134 */
135 @VisibleForTesting
136 public void replayAndAttach(InsetsController controller) {
137 if (mBehavior != KEEP_BEHAVIOR) {
138 controller.setSystemBarsBehavior(mBehavior);
139 }
140 if (mAppearanceMask != 0) {
141 controller.setSystemBarsAppearance(mAppearance, mAppearanceMask);
142 }
Yunfan Chenfae0aea2020-02-22 20:57:57 +0900143 if (mCaptionInsetsHeight != 0) {
144 controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
145 }
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100146 int size = mRequests.size();
147 for (int i = 0; i < size; i++) {
148 mRequests.get(i).replay(controller);
149 }
Jorim Jaggied35b172020-03-06 00:13:57 +0100150 size = mControllableInsetsChangedListeners.size();
151 for (int i = 0; i < size; i++) {
152 controller.addOnControllableInsetsChangedListener(
153 mControllableInsetsChangedListeners.get(i));
154 }
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100155
156 // Reset all state so it doesn't get applied twice just in case
157 mRequests.clear();
Jorim Jaggied35b172020-03-06 00:13:57 +0100158 mControllableInsetsChangedListeners.clear();
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100159 mBehavior = KEEP_BEHAVIOR;
160 mAppearance = 0;
161 mAppearanceMask = 0;
162
163 // After replaying, we forward everything directly to the replayed instance.
164 mReplayedInsetsController = controller;
165 }
166
167 /**
168 * Detaches the controller to no longer forward calls to the real instance.
169 */
170 @VisibleForTesting
171 public void detach() {
172 mReplayedInsetsController = null;
173 }
174
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000175 @Override
176 public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
177 @Nullable Interpolator interpolator,
178 CancellationSignal cancellationSignal,
179 @NonNull WindowInsetsAnimationControlListener listener) {
180 if (mReplayedInsetsController != null) {
181 mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
182 interpolator, cancellationSignal, listener);
183 } else {
Adrian Roos5d557ed2020-03-17 20:04:35 +0100184 listener.onCancelled(null);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000185 }
186 }
187
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100188 private interface PendingRequest {
189 void replay(InsetsController controller);
190 }
191
192 private static class ShowRequest implements PendingRequest {
193
194 private final @InsetsType int mTypes;
195
196 public ShowRequest(int types) {
197 mTypes = types;
198 }
199
200 @Override
201 public void replay(InsetsController controller) {
202 controller.show(mTypes);
203 }
204 }
205
206 private static class HideRequest implements PendingRequest {
207
208 private final @InsetsType int mTypes;
209
210 public HideRequest(int types) {
211 mTypes = types;
212 }
213
214 @Override
215 public void replay(InsetsController controller) {
216 controller.hide(mTypes);
217 }
218 }
219}