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.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         mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation);
113     }
114 
115     @Override
applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params)116     public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
117         if (mViewRoot.mView == null) {
118             throw new IllegalStateException("View of the ViewRootImpl is not initiated.");
119         }
120         if (mApplier == null) {
121             mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
122         }
123         if (mViewRoot.mView.isHardwareAccelerated()) {
124             mApplier.scheduleApply(params);
125         } else {
126             // Window doesn't support hardware acceleration, no synchronization for now.
127             // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every
128             //  frame instead.
129             mApplier.applyParams(new SurfaceControl.Transaction(), -1 /* frame */, params);
130         }
131     }
132 
133     @Override
postInsetsAnimationCallback(Runnable r)134     public void postInsetsAnimationCallback(Runnable r) {
135         mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, r,
136                 null /* token */);
137     }
138 
139     @Override
updateCompatSysUiVisibility(int type, boolean visible, boolean hasControl)140     public void updateCompatSysUiVisibility(int type, boolean visible, boolean hasControl) {
141         mViewRoot.updateCompatSysUiVisibility(type, visible, hasControl);
142     }
143 
144     @Override
onInsetsModified(InsetsState insetsState)145     public void onInsetsModified(InsetsState insetsState) {
146         try {
147             mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState);
148         } catch (RemoteException e) {
149             Log.e(TAG, "Failed to call insetsModified", e);
150         }
151     }
152 
153     @Override
hasAnimationCallbacks()154     public boolean hasAnimationCallbacks() {
155         if (mViewRoot.mView == null) {
156             return false;
157         }
158         return mViewRoot.mView.hasWindowInsetsAnimationCallback();
159     }
160 
161     @Override
setSystemBarsAppearance(int appearance, int mask)162     public void setSystemBarsAppearance(int appearance, int mask) {
163         mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_APPEARANCE_CONTROLLED;
164         final InsetsFlags insetsFlags = mViewRoot.mWindowAttributes.insetsFlags;
165         if (insetsFlags.appearance != appearance) {
166             insetsFlags.appearance = (insetsFlags.appearance & ~mask) | (appearance & mask);
167             mViewRoot.mWindowAttributesChanged = true;
168             mViewRoot.scheduleTraversals();
169         }
170     }
171 
172     @Override
getSystemBarsAppearance()173     public int getSystemBarsAppearance() {
174         if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) {
175             // We only return the requested appearance, not the implied one.
176             return 0;
177         }
178         return mViewRoot.mWindowAttributes.insetsFlags.appearance;
179     }
180 
181     @Override
setSystemBarsBehavior(int behavior)182     public void setSystemBarsBehavior(int behavior) {
183         mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
184         if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) {
185             mViewRoot.mWindowAttributes.insetsFlags.behavior = behavior;
186             mViewRoot.mWindowAttributesChanged = true;
187             mViewRoot.scheduleTraversals();
188         }
189     }
190 
191     @Override
getSystemBarsBehavior()192     public int getSystemBarsBehavior() {
193         if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_BEHAVIOR_CONTROLLED) == 0) {
194             // We only return the requested behavior, not the implied one.
195             return 0;
196         }
197         return mViewRoot.mWindowAttributes.insetsFlags.behavior;
198     }
199 
200     @Override
releaseSurfaceControlFromRt(SurfaceControl surfaceControl)201     public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
202 
203          // At the time we receive new leashes (e.g. InsetsSourceConsumer is processing
204          // setControl) we need to release the old leash. But we may have already scheduled
205          // a SyncRtSurfaceTransaction applier to use it from the RenderThread. To avoid
206          // synchronization issues we also release from the RenderThread so this release
207          // happens after any existing items on the work queue.
208 
209         if (mViewRoot.mView != null && mViewRoot.mView.isHardwareAccelerated()) {
210             mViewRoot.registerRtFrameCallback(frame -> {
211                 surfaceControl.release();
212             });
213             // Make sure a frame gets scheduled.
214             mViewRoot.mView.invalidate();
215         } else {
216             surfaceControl.release();
217         }
218     }
219 
220     @Override
getInputMethodManager()221     public InputMethodManager getInputMethodManager() {
222         return mViewRoot.mContext.getSystemService(InputMethodManager.class);
223     }
224 
225     @Override
getRootViewTitle()226     public String getRootViewTitle() {
227         if (mViewRoot == null) {
228             return null;
229         }
230         return mViewRoot.getTitle().toString();
231     }
232 
233     @Override
dipToPx(int dips)234     public int dipToPx(int dips) {
235         if (mViewRoot != null) {
236             return mViewRoot.dipToPx(dips);
237         }
238         return 0;
239     }
240 
241     @Override
getWindowToken()242     public IBinder getWindowToken() {
243         if (mViewRoot == null) {
244             return null;
245         }
246         final View view = mViewRoot.getView();
247         if (view == null) {
248             return null;
249         }
250         return view.getWindowToken();
251     }
252 }
253