/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import static android.view.InsetsController.DEBUG; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; import android.annotation.NonNull; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.inputmethod.InputMethodManager; import java.util.List; /** * Implements {@link InsetsController.Host} for {@link ViewRootImpl}s. * @hide */ public class ViewRootInsetsControllerHost implements InsetsController.Host { private final String TAG = "VRInsetsControllerHost"; private final ViewRootImpl mViewRoot; private SyncRtSurfaceTransactionApplier mApplier; public ViewRootInsetsControllerHost(ViewRootImpl viewRoot) { mViewRoot = viewRoot; } @Override public Handler getHandler() { return mViewRoot.mHandler; } @Override public void notifyInsetsChanged() { mViewRoot.notifyInsetsChanged(); } @Override public void addOnPreDrawRunnable(Runnable r) { if (mViewRoot.mView == null) { return; } mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this); r.run(); return true; } }); mViewRoot.mView.invalidate(); } @Override public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) { if (mViewRoot.mView == null) { return; } mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation); } @Override public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart( @NonNull WindowInsetsAnimation animation, @NonNull WindowInsetsAnimation.Bounds bounds) { if (mViewRoot.mView == null) { return null; } if (DEBUG) Log.d(TAG, "windowInsetsAnimation started"); return mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds); } @Override public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets, @NonNull List runningAnimations) { if (mViewRoot.mView == null) { // The view has already detached from window. return null; } if (DEBUG) { for (WindowInsetsAnimation anim : runningAnimations) { Log.d(TAG, "windowInsetsAnimation progress: " + anim.getInterpolatedFraction()); } } return mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets, runningAnimations); } @Override public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) { if (DEBUG) Log.d(TAG, "windowInsetsAnimation ended"); mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation); } @Override public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params) { if (mViewRoot.mView == null) { throw new IllegalStateException("View of the ViewRootImpl is not initiated."); } if (mApplier == null) { mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView); } if (mViewRoot.mView.isHardwareAccelerated()) { mApplier.scheduleApply(params); } else { // Window doesn't support hardware acceleration, no synchronization for now. // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every // frame instead. mApplier.applyParams(new SurfaceControl.Transaction(), -1 /* frame */, params); } } @Override public void postInsetsAnimationCallback(Runnable r) { mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION, r, null /* token */); } @Override public void updateCompatSysUiVisibility(int type, boolean visible, boolean hasControl) { mViewRoot.updateCompatSysUiVisibility(type, visible, hasControl); } @Override public void onInsetsModified(InsetsState insetsState) { try { mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState); } catch (RemoteException e) { Log.e(TAG, "Failed to call insetsModified", e); } } @Override public boolean hasAnimationCallbacks() { if (mViewRoot.mView == null) { return false; } return mViewRoot.mView.hasWindowInsetsAnimationCallback(); } @Override public void setSystemBarsAppearance(int appearance, int mask) { mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_APPEARANCE_CONTROLLED; final InsetsFlags insetsFlags = mViewRoot.mWindowAttributes.insetsFlags; if (insetsFlags.appearance != appearance) { insetsFlags.appearance = (insetsFlags.appearance & ~mask) | (appearance & mask); mViewRoot.mWindowAttributesChanged = true; mViewRoot.scheduleTraversals(); } } @Override public int getSystemBarsAppearance() { if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_APPEARANCE_CONTROLLED) == 0) { // We only return the requested appearance, not the implied one. return 0; } return mViewRoot.mWindowAttributes.insetsFlags.appearance; } @Override public void setSystemBarsBehavior(int behavior) { mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED; if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) { mViewRoot.mWindowAttributes.insetsFlags.behavior = behavior; mViewRoot.mWindowAttributesChanged = true; mViewRoot.scheduleTraversals(); } } @Override public int getSystemBarsBehavior() { if ((mViewRoot.mWindowAttributes.privateFlags & PRIVATE_FLAG_BEHAVIOR_CONTROLLED) == 0) { // We only return the requested behavior, not the implied one. return 0; } return mViewRoot.mWindowAttributes.insetsFlags.behavior; } @Override public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) { // At the time we receive new leashes (e.g. InsetsSourceConsumer is processing // setControl) we need to release the old leash. But we may have already scheduled // a SyncRtSurfaceTransaction applier to use it from the RenderThread. To avoid // synchronization issues we also release from the RenderThread so this release // happens after any existing items on the work queue. if (mViewRoot.mView != null && mViewRoot.mView.isHardwareAccelerated()) { mViewRoot.registerRtFrameCallback(frame -> { surfaceControl.release(); }); // Make sure a frame gets scheduled. mViewRoot.mView.invalidate(); } else { surfaceControl.release(); } } @Override public InputMethodManager getInputMethodManager() { return mViewRoot.mContext.getSystemService(InputMethodManager.class); } @Override public String getRootViewTitle() { if (mViewRoot == null) { return null; } return mViewRoot.getTitle().toString(); } @Override public int dipToPx(int dips) { if (mViewRoot != null) { return mViewRoot.dipToPx(dips); } return 0; } @Override public IBinder getWindowToken() { if (mViewRoot == null) { return null; } final View view = mViewRoot.getView(); if (view == null) { return null; } return view.getWindowToken(); } }