• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.view;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.CancellationSignal;
22 import android.view.WindowInsets.Type.InsetsType;
23 import android.view.animation.Interpolator;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 
27 import 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  */
34 public 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 boolean mAnimationsDisabled;
42     private final InsetsState mDummyState = new InsetsState();
43     private InsetsController mReplayedInsetsController;
44     private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
45             = new ArrayList<>();
46     private int mCaptionInsetsHeight = 0;
47 
48     @Override
show(int types)49     public void show(int types) {
50         if (mReplayedInsetsController != null) {
51             mReplayedInsetsController.show(types);
52         } else {
53             mRequests.add(new ShowRequest(types));
54         }
55     }
56 
57     @Override
hide(int types)58     public void hide(int types) {
59         if (mReplayedInsetsController != null) {
60             mReplayedInsetsController.hide(types);
61         } else {
62             mRequests.add(new HideRequest(types));
63         }
64     }
65 
66     @Override
setSystemBarsAppearance(int appearance, int mask)67     public void setSystemBarsAppearance(int appearance, int mask) {
68         if (mReplayedInsetsController != null) {
69             mReplayedInsetsController.setSystemBarsAppearance(appearance, mask);
70         } else {
71             mAppearance = (mAppearance & ~mask) | (appearance & mask);
72             mAppearanceMask |= mask;
73         }
74     }
75 
76     @Override
getSystemBarsAppearance()77     public int getSystemBarsAppearance() {
78         if (mReplayedInsetsController != null) {
79             return mReplayedInsetsController.getSystemBarsAppearance();
80         }
81         return mAppearance;
82     }
83 
84     @Override
setCaptionInsetsHeight(int height)85     public void setCaptionInsetsHeight(int height) {
86         mCaptionInsetsHeight = height;
87     }
88 
89     @Override
setSystemBarsBehavior(int behavior)90     public void setSystemBarsBehavior(int behavior) {
91         if (mReplayedInsetsController != null) {
92             mReplayedInsetsController.setSystemBarsBehavior(behavior);
93         } else {
94             mBehavior = behavior;
95         }
96     }
97 
98     @Override
getSystemBarsBehavior()99     public int getSystemBarsBehavior() {
100         if (mReplayedInsetsController != null) {
101             return mReplayedInsetsController.getSystemBarsBehavior();
102         }
103         return mBehavior;
104     }
105 
106     @Override
setAnimationsDisabled(boolean disable)107     public void setAnimationsDisabled(boolean disable) {
108         if (mReplayedInsetsController != null) {
109             mReplayedInsetsController.setAnimationsDisabled(disable);
110         } else {
111             mAnimationsDisabled = disable;
112         }
113     }
114 
115     @Override
getState()116     public InsetsState getState() {
117         return mDummyState;
118     }
119 
120     @Override
isRequestedVisible(int type)121     public boolean isRequestedVisible(int type) {
122 
123         // Method is only used once real insets controller is attached, so no need to traverse
124         // requests here.
125         return InsetsState.getDefaultVisibility(type);
126     }
127 
128     @Override
addOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)129     public void addOnControllableInsetsChangedListener(
130             OnControllableInsetsChangedListener listener) {
131         if (mReplayedInsetsController != null) {
132             mReplayedInsetsController.addOnControllableInsetsChangedListener(listener);
133         } else {
134             mControllableInsetsChangedListeners.add(listener);
135             listener.onControllableInsetsChanged(this, 0);
136         }
137     }
138 
139     @Override
removeOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)140     public void removeOnControllableInsetsChangedListener(
141             OnControllableInsetsChangedListener listener) {
142         if (mReplayedInsetsController != null) {
143             mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener);
144         } else {
145             mControllableInsetsChangedListeners.remove(listener);
146         }
147     }
148 
149     /**
150      * Replays the commands on {@code controller} and attaches it to this instance such that any
151      * calls will be forwarded to the real instance in the future.
152      */
153     @VisibleForTesting
replayAndAttach(InsetsController controller)154     public void replayAndAttach(InsetsController controller) {
155         if (mBehavior != KEEP_BEHAVIOR) {
156             controller.setSystemBarsBehavior(mBehavior);
157         }
158         if (mAppearanceMask != 0) {
159             controller.setSystemBarsAppearance(mAppearance, mAppearanceMask);
160         }
161         if (mCaptionInsetsHeight != 0) {
162             controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
163         }
164         if (mAnimationsDisabled) {
165             controller.setAnimationsDisabled(true);
166         }
167         int size = mRequests.size();
168         for (int i = 0; i < size; i++) {
169             mRequests.get(i).replay(controller);
170         }
171         size = mControllableInsetsChangedListeners.size();
172         for (int i = 0; i < size; i++) {
173             controller.addOnControllableInsetsChangedListener(
174                     mControllableInsetsChangedListeners.get(i));
175         }
176 
177         // Reset all state so it doesn't get applied twice just in case
178         mRequests.clear();
179         mControllableInsetsChangedListeners.clear();
180         mBehavior = KEEP_BEHAVIOR;
181         mAppearance = 0;
182         mAppearanceMask = 0;
183         mAnimationsDisabled = false;
184 
185         // After replaying, we forward everything directly to the replayed instance.
186         mReplayedInsetsController = controller;
187     }
188 
189     /**
190      * Detaches the controller to no longer forward calls to the real instance.
191      */
192     @VisibleForTesting
detach()193     public void detach() {
194         mReplayedInsetsController = null;
195     }
196 
197     @Override
controlWindowInsetsAnimation(@nsetsType int types, long durationMillis, @Nullable Interpolator interpolator, CancellationSignal cancellationSignal, @NonNull WindowInsetsAnimationControlListener listener)198     public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
199             @Nullable Interpolator interpolator,
200             CancellationSignal cancellationSignal,
201             @NonNull WindowInsetsAnimationControlListener listener) {
202         if (mReplayedInsetsController != null) {
203             mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
204                     interpolator, cancellationSignal, listener);
205         } else {
206             listener.onCancelled(null);
207         }
208     }
209 
210     private interface PendingRequest {
replay(InsetsController controller)211         void replay(InsetsController controller);
212     }
213 
214     private static class ShowRequest implements PendingRequest {
215 
216         private final @InsetsType int mTypes;
217 
ShowRequest(int types)218         public ShowRequest(int types) {
219             mTypes = types;
220         }
221 
222         @Override
replay(InsetsController controller)223         public void replay(InsetsController controller) {
224             controller.show(mTypes);
225         }
226     }
227 
228     private static class HideRequest implements PendingRequest {
229 
230         private final @InsetsType int mTypes;
231 
HideRequest(int types)232         public HideRequest(int types) {
233             mTypes = types;
234         }
235 
236         @Override
replay(InsetsController controller)237         public void replay(InsetsController controller) {
238             controller.hide(mTypes);
239         }
240     }
241 }
242