blob: 229ee03521bce634328011bc9a2de48adeb9348e [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<>();
Jorim Jaggi57157ac2019-01-22 19:01:48 +010045
46 @Override
47 public void show(int types) {
48 if (mReplayedInsetsController != null) {
49 mReplayedInsetsController.show(types);
50 } else {
51 mRequests.add(new ShowRequest(types));
52 }
53 }
54
55 @Override
56 public void hide(int types) {
57 if (mReplayedInsetsController != null) {
58 mReplayedInsetsController.hide(types);
59 } else {
60 mRequests.add(new HideRequest(types));
61 }
62 }
63
64 @Override
Jorim Jaggi57157ac2019-01-22 19:01:48 +010065 public void setSystemBarsAppearance(int appearance, int mask) {
66 if (mReplayedInsetsController != null) {
67 mReplayedInsetsController.setSystemBarsAppearance(appearance, mask);
68 } else {
69 mAppearance = (mAppearance & ~mask) | (appearance & mask);
70 mAppearanceMask |= mask;
71 }
72 }
73
74 @Override
75 public int getSystemBarsAppearance() {
76 if (mReplayedInsetsController != null) {
77 return mReplayedInsetsController.getSystemBarsAppearance();
78 }
79 return mAppearance;
80 }
81
82 @Override
83 public void setSystemBarsBehavior(int behavior) {
84 if (mReplayedInsetsController != null) {
85 mReplayedInsetsController.setSystemBarsBehavior(behavior);
86 } else {
87 mBehavior = behavior;
88 }
89 }
90
91 @Override
92 public int getSystemBarsBehavior() {
93 if (mReplayedInsetsController != null) {
94 return mReplayedInsetsController.getSystemBarsBehavior();
95 }
96 return mBehavior;
97 }
98
99 @Override
100 public InsetsState getState() {
101 return mDummyState;
102 }
103
Jorim Jaggied35b172020-03-06 00:13:57 +0100104 @Override
105 public void addOnControllableInsetsChangedListener(
106 OnControllableInsetsChangedListener listener) {
107 if (mReplayedInsetsController != null) {
108 mReplayedInsetsController.addOnControllableInsetsChangedListener(listener);
109 } else {
110 mControllableInsetsChangedListeners.add(listener);
111 listener.onControllableInsetsChanged(this, 0);
112 }
113 }
114
115 @Override
116 public void removeOnControllableInsetsChangedListener(
117 OnControllableInsetsChangedListener listener) {
118 if (mReplayedInsetsController != null) {
119 mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener);
120 } else {
121 mControllableInsetsChangedListeners.remove(listener);
122 }
123 }
124
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100125 /**
126 * Replays the commands on {@code controller} and attaches it to this instance such that any
127 * calls will be forwarded to the real instance in the future.
128 */
129 @VisibleForTesting
130 public void replayAndAttach(InsetsController controller) {
131 if (mBehavior != KEEP_BEHAVIOR) {
132 controller.setSystemBarsBehavior(mBehavior);
133 }
134 if (mAppearanceMask != 0) {
135 controller.setSystemBarsAppearance(mAppearance, mAppearanceMask);
136 }
137 int size = mRequests.size();
138 for (int i = 0; i < size; i++) {
139 mRequests.get(i).replay(controller);
140 }
Jorim Jaggied35b172020-03-06 00:13:57 +0100141 size = mControllableInsetsChangedListeners.size();
142 for (int i = 0; i < size; i++) {
143 controller.addOnControllableInsetsChangedListener(
144 mControllableInsetsChangedListeners.get(i));
145 }
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100146
147 // Reset all state so it doesn't get applied twice just in case
148 mRequests.clear();
Jorim Jaggied35b172020-03-06 00:13:57 +0100149 mControllableInsetsChangedListeners.clear();
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100150 mBehavior = KEEP_BEHAVIOR;
151 mAppearance = 0;
152 mAppearanceMask = 0;
153
154 // After replaying, we forward everything directly to the replayed instance.
155 mReplayedInsetsController = controller;
156 }
157
158 /**
159 * Detaches the controller to no longer forward calls to the real instance.
160 */
161 @VisibleForTesting
162 public void detach() {
163 mReplayedInsetsController = null;
164 }
165
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000166 @Override
167 public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
168 @Nullable Interpolator interpolator,
169 CancellationSignal cancellationSignal,
170 @NonNull WindowInsetsAnimationControlListener listener) {
171 if (mReplayedInsetsController != null) {
172 mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
173 interpolator, cancellationSignal, listener);
174 } else {
Adrian Roos5d557ed2020-03-17 20:04:35 +0100175 listener.onCancelled(null);
Adrian Roos5ad8cd22020-03-12 18:30:54 +0000176 }
177 }
178
Jorim Jaggi57157ac2019-01-22 19:01:48 +0100179 private interface PendingRequest {
180 void replay(InsetsController controller);
181 }
182
183 private static class ShowRequest implements PendingRequest {
184
185 private final @InsetsType int mTypes;
186
187 public ShowRequest(int types) {
188 mTypes = types;
189 }
190
191 @Override
192 public void replay(InsetsController controller) {
193 controller.show(mTypes);
194 }
195 }
196
197 private static class HideRequest implements PendingRequest {
198
199 private final @InsetsType int mTypes;
200
201 public HideRequest(int types) {
202 mTypes = types;
203 }
204
205 @Override
206 public void replay(InsetsController controller) {
207 controller.hide(mTypes);
208 }
209 }
210}