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