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