Decouple InsetsController from ViewRootImpl
Such that all it's goodness can also be used in context when ViewRootImpl isn't available, like the SystemUI controller used for Car and Split Test: InsetsControllerTest Fixes: 154631128 Change-Id: I54a3f8a34810472d9273e4627a7811b7abd0863f
This commit is contained in:
@@ -217,6 +217,6 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
}
|
||||
|
||||
private InputMethodManager getImm() {
|
||||
return mController.getViewRoot().mContext.getSystemService(InputMethodManager.class);
|
||||
return mController.getHost().getInputMethodManager();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,7 @@ import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.view.InsetsSourceConsumer.ShowResult;
|
||||
@@ -53,6 +51,7 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
|
||||
@@ -72,6 +71,91 @@ import java.util.function.BiFunction;
|
||||
*/
|
||||
public class InsetsController implements WindowInsetsController, InsetsAnimationControlCallbacks {
|
||||
|
||||
public interface Host {
|
||||
|
||||
Handler getHandler();
|
||||
|
||||
/**
|
||||
* Notifies host that {@link InsetsController#getState()} has changed.
|
||||
*/
|
||||
void notifyInsetsChanged();
|
||||
|
||||
void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation);
|
||||
Bounds dispatchWindowInsetsAnimationStart(
|
||||
@NonNull WindowInsetsAnimation animation, @NonNull Bounds bounds);
|
||||
WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
|
||||
@NonNull List<WindowInsetsAnimation> runningAnimations);
|
||||
void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation);
|
||||
|
||||
/**
|
||||
* Requests host to apply surface params in synchronized manner.
|
||||
*/
|
||||
void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params);
|
||||
|
||||
/**
|
||||
* @see ViewRootImpl#updateCompatSysUiVisibility(int, boolean, boolean)
|
||||
*/
|
||||
void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible,
|
||||
boolean hasControl);
|
||||
|
||||
/**
|
||||
* Called when insets have been modified by the client and should be reported back to WM.
|
||||
*/
|
||||
void onInsetsModified(InsetsState insetsState);
|
||||
|
||||
/**
|
||||
* @return Whether the host has any callbacks it wants to synchronize the animations with.
|
||||
* If there are no callbacks, the animation will be off-loaded to another thread and
|
||||
* slightly different animation curves are picked.
|
||||
*/
|
||||
boolean hasAnimationCallbacks();
|
||||
|
||||
/**
|
||||
* @see WindowInsetsController#setSystemBarsAppearance
|
||||
*/
|
||||
void setSystemBarsAppearance(@Appearance int appearance, @Appearance int mask);
|
||||
|
||||
/**
|
||||
* @see WindowInsetsController#getSystemBarsAppearance()
|
||||
*/
|
||||
@Appearance int getSystemBarsAppearance();
|
||||
|
||||
/**
|
||||
* @see WindowInsetsController#setSystemBarsBehavior
|
||||
*/
|
||||
void setSystemBarsBehavior(@Behavior int behavior);
|
||||
|
||||
/**
|
||||
* @see WindowInsetsController#getSystemBarsBehavior
|
||||
*/
|
||||
@Behavior int getSystemBarsBehavior();
|
||||
|
||||
/**
|
||||
* Releases a surface and ensure that this is done after {@link #applySurfaceParams} has
|
||||
* finished applying params.
|
||||
*/
|
||||
void releaseSurfaceControlFromRt(SurfaceControl surfaceControl);
|
||||
|
||||
/**
|
||||
* If this host is a view hierarchy, adds a pre-draw runnable to ensure proper ordering as
|
||||
* described in {@link WindowInsetsAnimation.Callback#onPrepare}.
|
||||
*
|
||||
* If this host isn't a view hierarchy, the runnable can be executed immediately.
|
||||
*/
|
||||
void addOnPreDrawRunnable(Runnable r);
|
||||
|
||||
/**
|
||||
* Adds a runnbale to be executed during {@link Choreographer#CALLBACK_INSETS_ANIMATION}
|
||||
* phase.
|
||||
*/
|
||||
void postInsetsAnimationCallback(Runnable r);
|
||||
|
||||
/**
|
||||
* Obtains {@link InputMethodManager} instance from host.
|
||||
*/
|
||||
InputMethodManager getInputMethodManager();
|
||||
}
|
||||
|
||||
private static final int ANIMATION_DURATION_SHOW_MS = 275;
|
||||
private static final int ANIMATION_DURATION_HIDE_MS = 340;
|
||||
|
||||
@@ -346,7 +430,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
private final Rect mFrame = new Rect();
|
||||
private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
|
||||
private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
|
||||
private final ViewRootImpl mViewRoot;
|
||||
private final Host mHost;
|
||||
private final Handler mHandler;
|
||||
|
||||
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
|
||||
@@ -370,8 +454,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
private boolean mStartingAnimation;
|
||||
private int mCaptionInsetsHeight = 0;
|
||||
|
||||
private SyncRtSurfaceTransactionApplier mApplier;
|
||||
|
||||
private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
|
||||
private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
|
||||
= new ArrayList<>();
|
||||
@@ -379,22 +461,22 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
/** Set of inset types for which an animation was started since last resetting this field */
|
||||
private @InsetsType int mLastStartedAnimTypes;
|
||||
|
||||
public InsetsController(ViewRootImpl viewRoot) {
|
||||
this(viewRoot, (controller, type) -> {
|
||||
public InsetsController(Host host) {
|
||||
this(host, (controller, type) -> {
|
||||
if (type == ITYPE_IME) {
|
||||
return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller);
|
||||
} else {
|
||||
return new InsetsSourceConsumer(type, controller.mState, Transaction::new,
|
||||
controller);
|
||||
}
|
||||
}, viewRoot.mHandler);
|
||||
}, host.getHandler());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public InsetsController(ViewRootImpl viewRoot,
|
||||
public InsetsController(Host host,
|
||||
BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator,
|
||||
Handler handler) {
|
||||
mViewRoot = viewRoot;
|
||||
mHost = host;
|
||||
mConsumerCreator = consumerCreator;
|
||||
mHandler = handler;
|
||||
mAnimCallback = () -> {
|
||||
@@ -402,10 +484,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (mRunningAnimations.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (mViewRoot.mView == null) {
|
||||
// The view has already detached from window.
|
||||
return;
|
||||
}
|
||||
|
||||
mTmpFinishedControls.clear();
|
||||
mTmpRunningAnims.clear();
|
||||
@@ -433,8 +511,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
|
||||
mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags,
|
||||
null /* typeSideMap */);
|
||||
mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets,
|
||||
mUnmodifiableTmpRunningAnims);
|
||||
mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims);
|
||||
|
||||
for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) {
|
||||
dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation());
|
||||
@@ -447,7 +524,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (mFrame.equals(frame)) {
|
||||
return;
|
||||
}
|
||||
mViewRoot.notifyInsetsChanged();
|
||||
mHost.notifyInsetsChanged();
|
||||
mFrame.set(frame);
|
||||
}
|
||||
|
||||
@@ -476,7 +553,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
mLastDispachedState.set(state, true /* copySources */);
|
||||
applyLocalVisibilityOverride();
|
||||
if (localStateChanged) {
|
||||
mViewRoot.notifyInsetsChanged();
|
||||
mHost.notifyInsetsChanged();
|
||||
}
|
||||
if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) {
|
||||
sendStateToWindowManager();
|
||||
@@ -733,7 +810,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
final InsetsAnimationControlRunner runner = useInsetsAnimationThread
|
||||
? new InsetsAnimationThreadControlRunner(controls,
|
||||
frame, mState, listener, typesReady, this, durationMs, interpolator,
|
||||
animationType, mViewRoot.mHandler)
|
||||
animationType, mHost.getHandler())
|
||||
: new InsetsAnimationControlImpl(controls,
|
||||
frame, mState, listener, typesReady, this, durationMs, interpolator,
|
||||
animationType);
|
||||
@@ -860,21 +937,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
@Override
|
||||
public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
|
||||
if (mApplier == null) {
|
||||
if (mViewRoot.mView == null) {
|
||||
throw new IllegalStateException("View of the ViewRootImpl is not initiated.");
|
||||
}
|
||||
mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
|
||||
}
|
||||
if (mViewRoot.mView.isHardwareAccelerated()) {
|
||||
mApplier.scheduleApply(false /* earlyWakeup */, 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 Transaction(), -1 /* frame */, false /* earlyWakeup */,
|
||||
params);
|
||||
}
|
||||
mHost.applySurfaceParams(params);
|
||||
}
|
||||
|
||||
void notifyControlRevoked(InsetsSourceConsumer consumer) {
|
||||
@@ -900,7 +963,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
ArraySet<Integer> types = toInternalType(control.getTypes());
|
||||
for (int j = types.size() - 1; j >= 0; j--) {
|
||||
if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
|
||||
mViewRoot.notifyInsetsChanged();
|
||||
mHost.notifyInsetsChanged();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -928,7 +991,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
@VisibleForTesting
|
||||
public void notifyVisibilityChanged() {
|
||||
mViewRoot.notifyInsetsChanged();
|
||||
mHost.notifyInsetsChanged();
|
||||
sendStateToWindowManager();
|
||||
}
|
||||
|
||||
@@ -937,7 +1000,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
*/
|
||||
public void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible,
|
||||
boolean hasControl) {
|
||||
mViewRoot.updateCompatSysUiVisibility(type, visible, hasControl);
|
||||
mHost.updateCompatSysUiVisibility(type, visible, hasControl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -954,10 +1017,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
getSourceConsumer(ITYPE_IME).onWindowFocusLost();
|
||||
}
|
||||
|
||||
ViewRootImpl getViewRoot() {
|
||||
return mViewRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden.
|
||||
* @hide
|
||||
@@ -994,12 +1053,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
tmpState.addSource(mState.getSource(consumer.getType()));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, tmpState);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to call insetsModified", e);
|
||||
}
|
||||
mHost.onInsetsModified(tmpState);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -1009,7 +1063,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasAnimationCallbacks = hasAnimationCallbacks();
|
||||
boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks();
|
||||
final InternalAnimationControlListener listener =
|
||||
new InternalAnimationControlListener(show, hasAnimationCallbacks, types);
|
||||
|
||||
@@ -1024,13 +1078,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
}
|
||||
|
||||
private boolean hasAnimationCallbacks() {
|
||||
if (mViewRoot.mView == null) {
|
||||
return false;
|
||||
}
|
||||
return mViewRoot.mView.hasWindowInsetsAnimationCallback();
|
||||
}
|
||||
|
||||
private void hideDirectly(
|
||||
@InsetsType int types, boolean animationFinished, @AnimationType int animationType) {
|
||||
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
|
||||
@@ -1064,37 +1111,28 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
public void startAnimation(InsetsAnimationControlImpl controller,
|
||||
WindowInsetsAnimationControlListener listener, int types,
|
||||
WindowInsetsAnimation animation, Bounds bounds) {
|
||||
if (mViewRoot.mView == null) {
|
||||
return;
|
||||
}
|
||||
mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation);
|
||||
mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
if (controller.isCancelled()) {
|
||||
return true;
|
||||
}
|
||||
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
|
||||
RunningAnimation runningAnimation = mRunningAnimations.get(i);
|
||||
if (runningAnimation.runner == controller) {
|
||||
runningAnimation.startDispatched = true;
|
||||
}
|
||||
}
|
||||
mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
|
||||
mStartingAnimation = true;
|
||||
controller.mReadyDispatched = true;
|
||||
listener.onReady(controller, types);
|
||||
mStartingAnimation = false;
|
||||
return true;
|
||||
mHost.dispatchWindowInsetsAnimationPrepare(animation);
|
||||
mHost.addOnPreDrawRunnable(() -> {
|
||||
if (controller.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
|
||||
RunningAnimation runningAnimation = mRunningAnimations.get(i);
|
||||
if (runningAnimation.runner == controller) {
|
||||
runningAnimation.startDispatched = true;
|
||||
}
|
||||
}
|
||||
mHost.dispatchWindowInsetsAnimationStart(animation, bounds);
|
||||
mStartingAnimation = true;
|
||||
controller.mReadyDispatched = true;
|
||||
listener.onReady(controller, types);
|
||||
mStartingAnimation = false;
|
||||
});
|
||||
mViewRoot.mView.invalidate();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void dispatchAnimationEnd(WindowInsetsAnimation animation) {
|
||||
mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation);
|
||||
mHost.dispatchWindowInsetsAnimationEnd(animation);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -1106,30 +1144,19 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
return;
|
||||
}
|
||||
if (!mAnimCallbackScheduled) {
|
||||
mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
|
||||
mAnimCallback, null /* token*/);
|
||||
mHost.postInsetsAnimationCallback(mAnimCallback);
|
||||
mAnimCallbackScheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSystemBarsAppearance(@Appearance int appearance, @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();
|
||||
}
|
||||
mHost.setSystemBarsAppearance(appearance, mask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Appearance 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;
|
||||
return mHost.getSystemBarsAppearance();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1139,21 +1166,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
@Override
|
||||
public void setSystemBarsBehavior(@Behavior 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();
|
||||
}
|
||||
mHost.setSystemBarsBehavior(behavior);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Appearance 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;
|
||||
return mHost.getSystemBarsBehavior();
|
||||
}
|
||||
|
||||
private @InsetsType int calculateControllableTypes() {
|
||||
@@ -1198,22 +1216,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
mControllableInsetsChangedListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
public void releaseSurfaceControlFromRt(SurfaceControl sc) {
|
||||
if (mViewRoot.mView != null && mViewRoot.mView.isHardwareAccelerated()) {
|
||||
mViewRoot.registerRtFrameCallback(frame -> {
|
||||
sc.release();
|
||||
});
|
||||
// Make sure a frame gets scheduled.
|
||||
mViewRoot.mView.invalidate();
|
||||
} else {
|
||||
sc.release();
|
||||
}
|
||||
mHost.releaseSurfaceControlFromRt(sc);
|
||||
}
|
||||
|
||||
Host getHost() {
|
||||
return mHost;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,7 +757,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
mChoreographer = useSfChoreographer
|
||||
? Choreographer.getSfInstance() : Choreographer.getInstance();
|
||||
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
|
||||
mInsetsController = new InsetsController(this);
|
||||
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
|
||||
|
||||
String processorOverrideName = context.getResources().getString(
|
||||
R.string.config_inputEventCompatProcessorOverrideClassName);
|
||||
|
||||
215
core/java/android/view/ViewRootInsetsControllerHost.java
Normal file
215
core/java/android/view/ViewRootInsetsControllerHost.java
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.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.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;
|
||||
}
|
||||
return mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
|
||||
@NonNull List<WindowInsetsAnimation> runningAnimations) {
|
||||
if (mViewRoot.mView == null) {
|
||||
// The view has already detached from window.
|
||||
return null;
|
||||
}
|
||||
return mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets, runningAnimations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
|
||||
mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
|
||||
if (mApplier == null) {
|
||||
if (mViewRoot.mView == null) {
|
||||
throw new IllegalStateException("View of the ViewRootImpl is not initiated.");
|
||||
}
|
||||
mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
|
||||
}
|
||||
if (mViewRoot.mView.isHardwareAccelerated()) {
|
||||
mApplier.scheduleApply(false /* earlyWakeup */, 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 */,
|
||||
false /* earlyWakeup */, 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user