1 /*
2  * Copyright (C) 2020 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 static android.view.InsetsController.DEBUG;
20 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED;
21 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
22 
23 import android.annotation.NonNull;
24 import android.content.Context;
25 import android.content.res.CompatibilityInfo;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.Log;
30 import android.view.inputmethod.InputMethodManager;
31 
32 import java.util.List;
33 
34 /**
35  * Implements {@link InsetsController.Host} for {@link ViewRootImpl}s.
36  * @hide
37  */
38 public class ViewRootInsetsControllerHost implements InsetsController.Host {
39 
40     private final String TAG = "VRInsetsControllerHost";
41 
42     private final ViewRootImpl mViewRoot;
43     private SyncRtSurfaceTransactionApplier mApplier;
44 
ViewRootInsetsControllerHost(ViewRootImpl viewRoot)45     public ViewRootInsetsControllerHost(ViewRootImpl viewRoot) {
46         mViewRoot = viewRoot;
47     }
48 
49     @Override
getHandler()50     public Handler getHandler() {
51         return mViewRoot.mHandler;
52     }
53 
54     @Override
notifyInsetsChanged()55     public void notifyInsetsChanged() {
56         mViewRoot.notifyInsetsChanged();
57     }
58 
59     @Override
addOnPreDrawRunnable(Runnable r)60     public void addOnPreDrawRunnable(Runnable r) {
61         if (mViewRoot.mView == null) {
62             return;
63         }
64         mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(
65                 new ViewTreeObserver.OnPreDrawListener() {
66                     @Override
67                     public boolean onPreDraw() {
68                         mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this);
69                         r.run();
70                         return true;
71                     }
72                 });
73         mViewRoot.mView.invalidate();
74     }
75 
76     @Override
dispatchWindowInsetsAnimationPrepare(@onNull WindowInsetsAnimation animation)77     public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) {
78         if (mViewRoot.mView == null) {
79             return;
80         }
81         mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation);
82     }
83 
84     @Override
dispatchWindowInsetsAnimationStart( @onNull WindowInsetsAnimation animation, @NonNull WindowInsetsAnimation.Bounds bounds)85     public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart(
86             @NonNull WindowInsetsAnimation animation,
87             @NonNull WindowInsetsAnimation.Bounds bounds) {
88         if (mViewRoot.mView == null) {
89             return null;
90         }
91         if (DEBUG) Log.d(TAG, "windowInsetsAnimation started");
92         return mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
93     }
94 
95     @Override
dispatchWindowInsetsAnimationProgress(@onNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations)96     public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
97             @NonNull List<WindowInsetsAnimation> runningAnimations) {
98         if (mViewRoot.mView == null) {
99             // The view has already detached from window.
100             return null;
101         }
102         if (DEBUG) {
103             for (WindowInsetsAnimation anim : runningAnimations) {
104                 Log.d(TAG, "windowInsetsAnimation progress: "
105                         + anim.getInterpolatedFraction());
106             }
107         }
108         return mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets, runningAnimations);
109     }
110 
111     @Override
dispatchWindowInsetsAnimationEnd(@onNull WindowInsetsAnimation animation)112     public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
113         if (DEBUG) Log.d(TAG, "windowInsetsAnimation ended");
114         if (mViewRoot.mView == null) {
115             // The view has already detached from window.
116             return;
117         }
118         mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation);
119     }
120 
121     @Override
applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params)122     public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
123         if (mViewRoot.mView == null) {
124             throw new IllegalStateException("View of the ViewRootImpl is not initiated.");
125         }
126         if (mApplier == null) {
127             mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
128         }
129         if (mViewRoot.mView.isHardwareAccelerated() && isVisibleToUser()) {
130             mApplier.scheduleApply(params);
131         } else {
132             // Synchronization requires hardware acceleration for now.
133             // If the window isn't visible, drawing is paused and the applier won't run.
134             // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every
135             //  frame instead.
136             final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
137             mApplier.applyParams(t, params);
138             t.apply();
139         }
140     }
141 
142     @Override
postInsetsAnimationCallback(Runnable r)143     public void postInsetsAnimationCallback(Runnable r) {
144         mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, r,
145                 null /* token */);
146     }
147 
148     @Override
updateCompatSysUiVisibility(int visibleTypes, int requestedVisibleTypes, int controllableTypes)149     public void updateCompatSysUiVisibility(int visibleTypes, int requestedVisibleTypes,
150             int controllableTypes) {
151         mViewRoot.updateCompatSysUiVisibility(visibleTypes, requestedVisibleTypes,
152                 controllableTypes);
153     }
154 
155     @Override
updateRequestedVisibleTypes(@indowInsets.Type.InsetsType int types)156     public void updateRequestedVisibleTypes(@WindowInsets.Type.InsetsType int types) {
157         try {
158             if (mViewRoot.mAdded) {
159                 mViewRoot.mWindowSession.updateRequestedVisibleTypes(mViewRoot.mWindow, types);
160             }
161         } catch (RemoteException e) {
162             Log.e(TAG, "Failed to call insetsModified", e);
163         }
164     }
165 
166     @Override
hasAnimationCallbacks()167     public boolean hasAnimationCallbacks() {
168         if (mViewRoot.mView == null) {
169             return false;
170         }
171         return mViewRoot.mView.hasWindowInsetsAnimationCallback();
172     }
173 
174     @Override
setSystemBarsAppearance(int appearance, int mask)175     public void setSystemBarsAppearance(int appearance, int mask) {
176         mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_APPEARANCE_CONTROLLED;
177         final InsetsFlags insetsFlags = mViewRoot.mWindowAttributes.insetsFlags;
178         final int newAppearance = (insetsFlags.appearance & ~mask) | (appearance & mask);
179         if (insetsFlags.appearance != newAppearance) {
180             insetsFlags.appearance = newAppearance;
181             mViewRoot.mWindowAttributesChanged = true;
182             mViewRoot.scheduleTraversals();
183         }
184     }
185 
186     @Override
getSystemBarsAppearance()187     public int getSystemBarsAppearance() {
188         return mViewRoot.mWindowAttributes.insetsFlags.appearance;
189     }
190 
191     @Override
isSystemBarsAppearanceControlled()192     public boolean isSystemBarsAppearanceControlled() {
193         return (mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) != 0;
194     }
195 
196     @Override
setSystemBarsBehavior(int behavior)197     public void setSystemBarsBehavior(int behavior) {
198         mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
199         if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) {
200             mViewRoot.mWindowAttributes.insetsFlags.behavior = behavior;
201             mViewRoot.mWindowAttributesChanged = true;
202             mViewRoot.scheduleTraversals();
203         }
204     }
205 
206     @Override
getSystemBarsBehavior()207     public int getSystemBarsBehavior() {
208         return mViewRoot.mWindowAttributes.insetsFlags.behavior;
209     }
210 
211     @Override
isSystemBarsBehaviorControlled()212     public boolean isSystemBarsBehaviorControlled() {
213         return (mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_BEHAVIOR_CONTROLLED) != 0;
214     }
215 
216     @Override
releaseSurfaceControlFromRt(SurfaceControl surfaceControl)217     public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
218 
219          // At the time we receive new leashes (e.g. InsetsSourceConsumer is processing
220          // setControl) we need to release the old leash. But we may have already scheduled
221          // a SyncRtSurfaceTransaction applier to use it from the RenderThread. To avoid
222          // synchronization issues we also release from the RenderThread so this release
223          // happens after any existing items on the work queue.
224 
225         if (mViewRoot.mView != null && mViewRoot.mView.isHardwareAccelerated()) {
226             mViewRoot.registerRtFrameCallback(frame -> {
227                 surfaceControl.release();
228             });
229             // Make sure a frame gets scheduled.
230             mViewRoot.mView.invalidate();
231         } else {
232             surfaceControl.release();
233         }
234     }
235 
236     @Override
getInputMethodManager()237     public InputMethodManager getInputMethodManager() {
238         return mViewRoot.mContext.getSystemService(InputMethodManager.class);
239     }
240 
241     @Override
getRootViewTitle()242     public String getRootViewTitle() {
243         if (mViewRoot == null) {
244             return null;
245         }
246         return mViewRoot.getTitle().toString();
247     }
248 
249     @Override
getRootViewContext()250     public Context getRootViewContext() {
251         return mViewRoot != null ? mViewRoot.mContext : null;
252     }
253 
254     @Override
dipToPx(int dips)255     public int dipToPx(int dips) {
256         if (mViewRoot != null) {
257             return mViewRoot.dipToPx(dips);
258         }
259         return 0;
260     }
261 
262     @Override
getWindowToken()263     public IBinder getWindowToken() {
264         if (mViewRoot == null) {
265             return null;
266         }
267         final View view = mViewRoot.getView();
268         if (view == null) {
269             return null;
270         }
271         return view.getWindowToken();
272     }
273 
274     @Override
getTranslator()275     public CompatibilityInfo.Translator getTranslator() {
276         if (mViewRoot != null) {
277             return mViewRoot.mTranslator;
278         }
279         return null;
280     }
281 
isVisibleToUser()282     private boolean isVisibleToUser() {
283         return mViewRoot.getHostVisibility() == View.VISIBLE;
284     }
285 }
286