• 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         if (mBehavior == KEEP_BEHAVIOR) {
104             return BEHAVIOR_DEFAULT;
105         }
106         return mBehavior;
107     }
108 
109     @Override
setAnimationsDisabled(boolean disable)110     public void setAnimationsDisabled(boolean disable) {
111         if (mReplayedInsetsController != null) {
112             mReplayedInsetsController.setAnimationsDisabled(disable);
113         } else {
114             mAnimationsDisabled = disable;
115         }
116     }
117 
118     @Override
getState()119     public InsetsState getState() {
120         return mDummyState;
121     }
122 
123     @Override
isRequestedVisible(int type)124     public boolean isRequestedVisible(int type) {
125 
126         // Method is only used once real insets controller is attached, so no need to traverse
127         // requests here.
128         return InsetsState.getDefaultVisibility(type);
129     }
130 
131     @Override
addOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)132     public void addOnControllableInsetsChangedListener(
133             OnControllableInsetsChangedListener listener) {
134         if (mReplayedInsetsController != null) {
135             mReplayedInsetsController.addOnControllableInsetsChangedListener(listener);
136         } else {
137             mControllableInsetsChangedListeners.add(listener);
138             listener.onControllableInsetsChanged(this, 0);
139         }
140     }
141 
142     @Override
removeOnControllableInsetsChangedListener( OnControllableInsetsChangedListener listener)143     public void removeOnControllableInsetsChangedListener(
144             OnControllableInsetsChangedListener listener) {
145         if (mReplayedInsetsController != null) {
146             mReplayedInsetsController.removeOnControllableInsetsChangedListener(listener);
147         } else {
148             mControllableInsetsChangedListeners.remove(listener);
149         }
150     }
151 
152     /**
153      * Replays the commands on {@code controller} and attaches it to this instance such that any
154      * calls will be forwarded to the real instance in the future.
155      */
156     @VisibleForTesting
replayAndAttach(InsetsController controller)157     public void replayAndAttach(InsetsController controller) {
158         if (mBehavior != KEEP_BEHAVIOR) {
159             controller.setSystemBarsBehavior(mBehavior);
160         }
161         if (mAppearanceMask != 0) {
162             controller.setSystemBarsAppearance(mAppearance, mAppearanceMask);
163         }
164         if (mCaptionInsetsHeight != 0) {
165             controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
166         }
167         if (mAnimationsDisabled) {
168             controller.setAnimationsDisabled(true);
169         }
170         int size = mRequests.size();
171         for (int i = 0; i < size; i++) {
172             mRequests.get(i).replay(controller);
173         }
174         size = mControllableInsetsChangedListeners.size();
175         for (int i = 0; i < size; i++) {
176             controller.addOnControllableInsetsChangedListener(
177                     mControllableInsetsChangedListeners.get(i));
178         }
179 
180         // Reset all state so it doesn't get applied twice just in case
181         mRequests.clear();
182         mControllableInsetsChangedListeners.clear();
183         mBehavior = KEEP_BEHAVIOR;
184         mAppearance = 0;
185         mAppearanceMask = 0;
186         mAnimationsDisabled = false;
187 
188         // After replaying, we forward everything directly to the replayed instance.
189         mReplayedInsetsController = controller;
190     }
191 
192     /**
193      * Detaches the controller to no longer forward calls to the real instance.
194      */
195     @VisibleForTesting
detach()196     public void detach() {
197         mReplayedInsetsController = null;
198     }
199 
200     @Override
controlWindowInsetsAnimation(@nsetsType int types, long durationMillis, @Nullable Interpolator interpolator, CancellationSignal cancellationSignal, @NonNull WindowInsetsAnimationControlListener listener)201     public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
202             @Nullable Interpolator interpolator,
203             CancellationSignal cancellationSignal,
204             @NonNull WindowInsetsAnimationControlListener listener) {
205         if (mReplayedInsetsController != null) {
206             mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
207                     interpolator, cancellationSignal, listener);
208         } else {
209             listener.onCancelled(null);
210         }
211     }
212 
213     private interface PendingRequest {
replay(InsetsController controller)214         void replay(InsetsController controller);
215     }
216 
217     private static class ShowRequest implements PendingRequest {
218 
219         private final @InsetsType int mTypes;
220 
ShowRequest(int types)221         public ShowRequest(int types) {
222             mTypes = types;
223         }
224 
225         @Override
replay(InsetsController controller)226         public void replay(InsetsController controller) {
227             controller.show(mTypes);
228         }
229     }
230 
231     private static class HideRequest implements PendingRequest {
232 
233         private final @InsetsType int mTypes;
234 
HideRequest(int types)235         public HideRequest(int types) {
236             mTypes = types;
237         }
238 
239         @Override
replay(InsetsController controller)240         public void replay(InsetsController controller) {
241             controller.hide(mTypes);
242         }
243     }
244 }
245