Merge "Let InsetsController control requested visible types"
This commit is contained in:
@@ -16,21 +16,20 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import static android.os.Trace.TRACE_TAG_VIEW;
|
||||
import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
|
||||
import static android.view.ImeInsetsSourceConsumerProto.IS_HIDE_ANIMATION_RUNNING;
|
||||
import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
|
||||
import static android.view.ImeInsetsSourceConsumerProto.IS_SHOW_REQUESTED_DURING_HIDE_ANIMATION;
|
||||
import static android.view.InsetsController.AnimationType;
|
||||
import static android.view.InsetsState.ITYPE_IME;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
import android.os.Trace;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import com.android.internal.inputmethod.ImeTracing;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -61,6 +60,38 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
super(ITYPE_IME, state, transactionSupplier, controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onAnimationStateChanged(boolean running) {
|
||||
if (!running) {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"ImeInsetsSourceConsumer#onAnimationFinished",
|
||||
mController.getHost().getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
final boolean insetsChanged = super.onAnimationStateChanged(running);
|
||||
final boolean showRequested = (mController.getRequestedVisibleTypes() & getType()) != 0;
|
||||
if (showRequested) {
|
||||
onShowRequested();
|
||||
} else {
|
||||
mIsRequestedVisibleAwaitingControl = false;
|
||||
if (!running) {
|
||||
// Remove IME surface as IME has finished hide animation, if there is no pending
|
||||
// show request.
|
||||
if (!mIsShowRequestedDuringHideAnimation) {
|
||||
notifyHidden();
|
||||
removeSurface();
|
||||
}
|
||||
}
|
||||
// Here is reached
|
||||
// (1) before the hide animation starts.
|
||||
// (2) after the hide animation ends.
|
||||
// (3) if the IME is not controllable (animationFinished == true in this case).
|
||||
// We should reset mIsShowRequestedDuringHideAnimation in all cases.
|
||||
mIsHideAnimationRunning = running;
|
||||
mIsShowRequestedDuringHideAnimation = false;
|
||||
}
|
||||
return insetsChanged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusGained(boolean hasViewFocus) {
|
||||
super.onWindowFocusGained(hasViewFocus);
|
||||
@@ -78,36 +109,11 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(boolean fromIme) {
|
||||
super.show(fromIme);
|
||||
onShowRequested();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
super.hide();
|
||||
mIsRequestedVisibleAwaitingControl = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
void hide(boolean animationFinished, @AnimationType int animationType) {
|
||||
hide();
|
||||
|
||||
if (animationFinished) {
|
||||
// Remove IME surface as IME has finished hide animation, if there is no pending
|
||||
// show request.
|
||||
if (!mIsShowRequestedDuringHideAnimation) {
|
||||
notifyHidden();
|
||||
removeSurface();
|
||||
}
|
||||
}
|
||||
// This method is called
|
||||
// (1) before the hide animation starts.
|
||||
// (2) after the hide animation ends.
|
||||
// (3) if the IME is not controllable (animationFinished == true in this case).
|
||||
// We should reset mIsShowRequestedDuringHideAnimation in all cases.
|
||||
mIsHideAnimationRunning = !animationFinished;
|
||||
mIsShowRequestedDuringHideAnimation = false;
|
||||
public boolean applyLocalVisibilityOverride() {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"ImeInsetsSourceConsumer#applyLocalVisibilityOverride",
|
||||
mController.getHost().getInputMethodManager(), null /* icProto */);
|
||||
return super.applyLocalVisibilityOverride();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,6 +122,12 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
*/
|
||||
@Override
|
||||
public @ShowResult int requestShow(boolean fromIme) {
|
||||
if (fromIme) {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"ImeInsetsSourceConsumer#requestShow",
|
||||
mController.getHost().getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
|
||||
// TODO: ResultReceiver for IME.
|
||||
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
|
||||
if (getControl() == null) {
|
||||
@@ -137,10 +149,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
* Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
|
||||
* IME insets are hidden.
|
||||
*/
|
||||
@Override
|
||||
void notifyHidden() {
|
||||
private void notifyHidden() {
|
||||
getImm().notifyImeHidden(mController.getHost().getWindowToken());
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,11 +164,13 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
@Override
|
||||
public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes,
|
||||
int[] hideTypes) {
|
||||
ImeTracing.getInstance().triggerClientDump("ImeInsetsSourceConsumer#setControl",
|
||||
mController.getHost().getInputMethodManager(), null /* icProto */);
|
||||
if (!super.setControl(control, showTypes, hideTypes)) {
|
||||
return false;
|
||||
}
|
||||
if (control == null && !mIsRequestedVisibleAwaitingControl) {
|
||||
hide();
|
||||
mController.setRequestedVisibleTypes(0 /* visibleTypes */, getType());
|
||||
removeSurface();
|
||||
}
|
||||
if (control != null) {
|
||||
@@ -192,7 +204,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when {@link #show} or {@link InputMethodManager#showSoftInput(View, int)} is called.
|
||||
* Called when {@link #onAnimationStateChanged(boolean)} or
|
||||
* {@link InputMethodManager#showSoftInput(View, int)} is called.
|
||||
*/
|
||||
public void onShowRequested() {
|
||||
if (mIsHideAnimationRunning) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import static android.view.InsetsControllerProto.CONTROL;
|
||||
import static android.view.InsetsControllerProto.STATE;
|
||||
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
|
||||
import static android.view.InsetsState.ITYPE_IME;
|
||||
import static android.view.InsetsState.toInternalType;
|
||||
import static android.view.InsetsState.toPublicType;
|
||||
import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
|
||||
import static android.view.WindowInsets.Type.FIRST;
|
||||
@@ -742,6 +741,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
private void updateState(InsetsState newState) {
|
||||
mState.set(newState, 0 /* types */);
|
||||
@InsetsType int existingTypes = 0;
|
||||
@InsetsType int visibleTypes = 0;
|
||||
@InsetsType int disabledUserAnimationTypes = 0;
|
||||
@InsetsType int[] cancelledUserAnimationTypes = {0};
|
||||
@@ -760,10 +760,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
}
|
||||
}
|
||||
getSourceConsumer(type).updateSource(source, animationType);
|
||||
existingTypes |= insetsType;
|
||||
if (source.isVisible()) {
|
||||
visibleTypes |= insetsType;
|
||||
}
|
||||
}
|
||||
|
||||
// If a type doesn't have a source, treat it as visible if it is visible by default.
|
||||
visibleTypes |= WindowInsets.Type.defaultVisible() & ~existingTypes;
|
||||
|
||||
if (mVisibleTypes != visibleTypes) {
|
||||
if (WindowInsets.Type.hasCompatSystemBars(mVisibleTypes ^ visibleTypes)) {
|
||||
mCompatSysUiVisibilityStaled = true;
|
||||
@@ -1103,6 +1108,29 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
@AnimationType int animationType,
|
||||
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
|
||||
boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
|
||||
final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
|
||||
|
||||
// Basically, we accept the requested visibilities from the upstream callers...
|
||||
setRequestedVisibleTypes(visible ? types : 0, types);
|
||||
|
||||
// However, we might reject the request in some cases, such as delaying showing IME or
|
||||
// rejecting showing IME.
|
||||
controlAnimationUncheckedInner(types, cancellationSignal, listener, frame, fromIme,
|
||||
durationMs, interpolator, animationType, layoutInsetsDuringAnimation,
|
||||
useInsetsAnimationThread, statsToken);
|
||||
|
||||
// We are finishing setting the requested visible types. Report them to the server and/or
|
||||
// the app.
|
||||
reportRequestedVisibleTypes();
|
||||
}
|
||||
|
||||
private void controlAnimationUncheckedInner(@InsetsType int types,
|
||||
@Nullable CancellationSignal cancellationSignal,
|
||||
WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
|
||||
long durationMs, Interpolator interpolator,
|
||||
@AnimationType int animationType,
|
||||
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
|
||||
boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
|
||||
ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_CONTROL_ANIMATION);
|
||||
if ((types & mTypesBeingCancelled) != 0) {
|
||||
throw new IllegalStateException("Cannot start a new insets animation of "
|
||||
@@ -1119,13 +1147,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
&& !mState.getSource(ITYPE_IME).isVisible()) {
|
||||
// We've requested IMM to show IME, but the IME is not controllable. We need to
|
||||
// cancel the request.
|
||||
getSourceConsumer(ITYPE_IME).hide(true, animationType);
|
||||
setRequestedVisibleTypes(0 /* visibleTypes */, ime());
|
||||
if (getSourceConsumer(ITYPE_IME).onAnimationStateChanged(false /* running */)) {
|
||||
notifyVisibilityChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (types == 0) {
|
||||
// nothing to animate.
|
||||
listener.onCancelled(null);
|
||||
reportRequestedVisibleTypes();
|
||||
if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
|
||||
return;
|
||||
}
|
||||
@@ -1161,7 +1191,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
}
|
||||
});
|
||||
}
|
||||
reportRequestedVisibleTypes();
|
||||
|
||||
// The requested visibilities should be delayed as well. Otherwise, the server will
|
||||
// create already visible leashes for us before we play the show animation.
|
||||
setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types);
|
||||
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
|
||||
return;
|
||||
}
|
||||
@@ -1169,7 +1203,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (typesReady == 0) {
|
||||
if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
|
||||
listener.onCancelled(null);
|
||||
reportRequestedVisibleTypes();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1198,12 +1231,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
} else {
|
||||
Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
|
||||
}
|
||||
if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
|
||||
showDirectly(types, fromIme);
|
||||
} else {
|
||||
hideDirectly(types, false /* animationFinished */, animationType, fromIme);
|
||||
onAnimationStateChanged(types, true /* running */);
|
||||
|
||||
if (fromIme) {
|
||||
switch (animationType) {
|
||||
case ANIMATION_TYPE_SHOW:
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0);
|
||||
break;
|
||||
case ANIMATION_TYPE_HIDE:
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0);
|
||||
break;
|
||||
}
|
||||
} else if (animationType == ANIMATION_TYPE_HIDE) {
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
|
||||
}
|
||||
reportRequestedVisibleTypes();
|
||||
}
|
||||
|
||||
// TODO(b/242962223): Make this setter restrictive.
|
||||
@@ -1228,11 +1269,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
boolean canRun = true;
|
||||
if (show) {
|
||||
// Show request
|
||||
if (fromIme) {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"ImeInsetsSourceConsumer#requestShow", mHost.getInputMethodManager(),
|
||||
null /* icProto */);
|
||||
}
|
||||
switch(consumer.requestShow(fromIme)) {
|
||||
case ShowResult.SHOW_IMMEDIATELY:
|
||||
break;
|
||||
@@ -1246,15 +1282,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
// IME cannot be shown (since it didn't have focus), proceed
|
||||
// with animation of other types.
|
||||
canRun = false;
|
||||
|
||||
// Reject the show request.
|
||||
setRequestedVisibleTypes(0 /* visibleTypes */, consumer.getType());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Hide request
|
||||
// TODO: Move notifyHidden() to beginning of the hide animation
|
||||
// (when visibility actually changes using hideDirectly()).
|
||||
if (!fromIme) {
|
||||
consumer.notifyHidden();
|
||||
}
|
||||
}
|
||||
if (!canRun) {
|
||||
if (WARN) Log.w(TAG, String.format(
|
||||
@@ -1266,24 +1298,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (control != null && control.getLeash() != null) {
|
||||
controls.put(control.getId(), new InsetsSourceControl(control));
|
||||
typesReady |= consumer.getType();
|
||||
} else if (animationType == ANIMATION_TYPE_SHOW) {
|
||||
if (DEBUG) Log.d(TAG, "collectSourceControls no control for show(). fromIme: "
|
||||
+ fromIme);
|
||||
// We don't have a control at the moment. However, we still want to update requested
|
||||
// visibility state such that in case we get control, we can apply show animation.
|
||||
if (fromIme) {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"InsetsSourceConsumer#show", mHost.getInputMethodManager(),
|
||||
null /* icProto */);
|
||||
}
|
||||
consumer.show(fromIme);
|
||||
} else if (animationType == ANIMATION_TYPE_HIDE) {
|
||||
if (fromIme) {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"InsetsSourceConsumer#hide", mHost.getInputMethodManager(),
|
||||
null /* icProto */);
|
||||
}
|
||||
consumer.hide();
|
||||
}
|
||||
}
|
||||
return new Pair<>(typesReady, imeReady);
|
||||
@@ -1332,6 +1346,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
|
||||
setRequestedVisibleTypes(shown ? runner.getTypes() : 0, runner.getTypes());
|
||||
cancelAnimation(runner, false /* invokeCallback */);
|
||||
if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
|
||||
if (runner.getAnimationType() == ANIMATION_TYPE_RESIZE) {
|
||||
@@ -1343,15 +1358,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (shown) {
|
||||
ImeTracker.get().onProgress(statsToken,
|
||||
ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_SHOW);
|
||||
showDirectly(runner.getTypes(), true /* fromIme */);
|
||||
ImeTracker.get().onShown(statsToken);
|
||||
} else {
|
||||
ImeTracker.get().onProgress(statsToken,
|
||||
ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_HIDE);
|
||||
hideDirectly(runner.getTypes(), true /* animationFinished */,
|
||||
runner.getAnimationType(), true /* fromIme */);
|
||||
ImeTracker.get().onHidden(statsToken);
|
||||
}
|
||||
reportRequestedVisibleTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1388,28 +1401,31 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
"cancelAnimation of types: %d, animType: %d, host: %s",
|
||||
control.getTypes(), control.getAnimationType(), mHost.getRootViewTitle()));
|
||||
}
|
||||
boolean stateChanged = false;
|
||||
@InsetsType int removedTypes = 0;
|
||||
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
|
||||
RunningAnimation runningAnimation = mRunningAnimations.get(i);
|
||||
if (runningAnimation.runner == control) {
|
||||
mRunningAnimations.remove(i);
|
||||
ArraySet<Integer> types = toInternalType(control.getTypes());
|
||||
for (int j = types.size() - 1; j >= 0; j--) {
|
||||
if (types.valueAt(j) == ITYPE_IME) {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"InsetsSourceConsumer#notifyAnimationFinished",
|
||||
mHost.getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished();
|
||||
}
|
||||
removedTypes = control.getTypes();
|
||||
if (invokeCallback) {
|
||||
dispatchAnimationEnd(runningAnimation.runner.getAnimation());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stateChanged) {
|
||||
mHost.notifyInsetsChanged();
|
||||
onAnimationStateChanged(removedTypes, false /* running */);
|
||||
}
|
||||
|
||||
private void onAnimationStateChanged(@InsetsType int types, boolean running) {
|
||||
boolean insetsChanged = false;
|
||||
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
|
||||
final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
|
||||
if ((consumer.getType() & types) != 0) {
|
||||
insetsChanged |= consumer.onAnimationStateChanged(running);
|
||||
}
|
||||
}
|
||||
if (insetsChanged) {
|
||||
notifyVisibilityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1472,27 +1488,28 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
return ANIMATION_TYPE_NONE;
|
||||
}
|
||||
|
||||
void setRequestedVisibleTypes(@InsetsType int visibleTypes, @InsetsType int mask) {
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
public void setRequestedVisibleTypes(@InsetsType int visibleTypes, @InsetsType int mask) {
|
||||
final @InsetsType int requestedVisibleTypes =
|
||||
(mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
|
||||
if (mRequestedVisibleTypes != requestedVisibleTypes) {
|
||||
if (WindowInsets.Type.hasCompatSystemBars(
|
||||
mRequestedVisibleTypes ^ requestedVisibleTypes)) {
|
||||
mCompatSysUiVisibilityStaled = true;
|
||||
}
|
||||
mRequestedVisibleTypes = requestedVisibleTypes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the requested visible types to window manager if any of them is changed.
|
||||
* Called when finishing setting requested visible types or finishing setting controls.
|
||||
*/
|
||||
private void reportRequestedVisibleTypes() {
|
||||
updateCompatSysUiVisibility();
|
||||
if (mReportedRequestedVisibleTypes != mRequestedVisibleTypes) {
|
||||
final @InsetsType int diff = mRequestedVisibleTypes ^ mReportedRequestedVisibleTypes;
|
||||
if (WindowInsets.Type.hasCompatSystemBars(diff)) {
|
||||
mCompatSysUiVisibilityStaled = true;
|
||||
}
|
||||
mReportedRequestedVisibleTypes = mRequestedVisibleTypes;
|
||||
mHost.updateRequestedVisibleTypes(mReportedRequestedVisibleTypes);
|
||||
}
|
||||
updateCompatSysUiVisibility();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -1538,39 +1555,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
!hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken);
|
||||
}
|
||||
|
||||
private void hideDirectly(@InsetsType int types, boolean animationFinished,
|
||||
@AnimationType int animationType, boolean fromIme) {
|
||||
if ((types & ime()) != 0) {
|
||||
ImeTracing.getInstance().triggerClientDump("InsetsController#hideDirectly",
|
||||
mHost.getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
|
||||
for (int i = internalTypes.size() - 1; i >= 0; i--) {
|
||||
getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
|
||||
}
|
||||
reportRequestedVisibleTypes();
|
||||
|
||||
if (fromIme) {
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void showDirectly(@InsetsType int types, boolean fromIme) {
|
||||
if ((types & ime()) != 0) {
|
||||
ImeTracing.getInstance().triggerClientDump("InsetsController#showDirectly",
|
||||
mHost.getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
|
||||
for (int i = internalTypes.size() - 1; i >= 0; i--) {
|
||||
getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
|
||||
}
|
||||
reportRequestedVisibleTypes();
|
||||
|
||||
if (fromIme) {
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel on-going animation to show/hide {@link InsetsType}.
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,6 @@ import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
|
||||
import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
|
||||
import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
|
||||
import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
|
||||
import static android.view.InsetsState.ITYPE_IME;
|
||||
import static android.view.InsetsState.getDefaultVisibility;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
||||
@@ -40,7 +39,6 @@ import android.view.SurfaceControl.Transaction;
|
||||
import android.view.WindowInsets.Type.InsetsType;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.inputmethod.ImeTracing;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -116,10 +114,6 @@ public class InsetsSourceConsumer {
|
||||
*/
|
||||
public boolean setControl(@Nullable InsetsSourceControl control,
|
||||
@InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
|
||||
if (mInternalType == ITYPE_IME) {
|
||||
ImeTracing.getInstance().triggerClientDump("InsetsSourceConsumer#setControl",
|
||||
mController.getHost().getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
if (Objects.equals(mSourceControl, control)) {
|
||||
if (mSourceControl != null && mSourceControl != control) {
|
||||
mSourceControl.release(SurfaceControl::release);
|
||||
@@ -210,22 +204,27 @@ public class InsetsSourceConsumer {
|
||||
return mInternalType;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void show(boolean fromIme) {
|
||||
if (DEBUG) Log.d(TAG, String.format("Call show() for type: %s fromIme: %b ",
|
||||
InsetsState.typeToString(mInternalType), fromIme));
|
||||
setRequestedVisible(true);
|
||||
}
|
||||
/**
|
||||
* Called right after the animation is started or finished.
|
||||
*/
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public boolean onAnimationStateChanged(boolean running) {
|
||||
boolean insetsChanged = false;
|
||||
if (!running && mPendingFrame != null) {
|
||||
InsetsSource source = mState.getSource(mInternalType);
|
||||
source.setFrame(mPendingFrame);
|
||||
source.setVisibleFrame(mPendingVisibleFrame);
|
||||
mPendingFrame = null;
|
||||
mPendingVisibleFrame = null;
|
||||
insetsChanged = true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void hide() {
|
||||
if (DEBUG) Log.d(TAG, String.format("Call hide for %s on %s",
|
||||
InsetsState.typeToString(mInternalType), mController.getHost().getRootViewTitle()));
|
||||
setRequestedVisible(false);
|
||||
}
|
||||
|
||||
void hide(boolean animationFinished, @AnimationType int animationType) {
|
||||
hide();
|
||||
// We apply the visibility override after the animation is started. We don't do this before
|
||||
// that because we need to know the initial insets state while creating the animation.
|
||||
// We also need to apply the override after the animation is finished because the requested
|
||||
// visibility can be set when finishing the user animation.
|
||||
insetsChanged |= applyLocalVisibilityOverride();
|
||||
return insetsChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,19 +246,14 @@ public class InsetsSourceConsumer {
|
||||
return mHasViewFocusWhenWindowFocusGain;
|
||||
}
|
||||
|
||||
boolean applyLocalVisibilityOverride() {
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public boolean applyLocalVisibilityOverride() {
|
||||
final InsetsSource source = mState.peekSource(mInternalType);
|
||||
final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(
|
||||
mInternalType);
|
||||
final boolean hasControl = mSourceControl != null;
|
||||
final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
|
||||
|
||||
if (mInternalType == ITYPE_IME) {
|
||||
ImeTracing.getInstance().triggerClientDump(
|
||||
"InsetsSourceConsumer#applyLocalVisibilityOverride",
|
||||
mController.getHost().getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
|
||||
// If we don't have control, we are not able to change the visibility.
|
||||
if (!hasControl) {
|
||||
if (DEBUG) Log.d(TAG, "applyLocalVisibilityOverride: No control in "
|
||||
@@ -298,13 +292,6 @@ public class InsetsSourceConsumer {
|
||||
public void onPerceptible(boolean perceptible) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify listeners that window is now hidden.
|
||||
*/
|
||||
void notifyHidden() {
|
||||
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove surface on which this consumer type is drawn.
|
||||
*/
|
||||
@@ -336,31 +323,6 @@ public class InsetsSourceConsumer {
|
||||
if (DEBUG) Log.d(TAG, "updateSource: " + newSource);
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public boolean notifyAnimationFinished() {
|
||||
if (mPendingFrame != null) {
|
||||
InsetsSource source = mState.getSource(mInternalType);
|
||||
source.setFrame(mPendingFrame);
|
||||
source.setVisibleFrame(mPendingVisibleFrame);
|
||||
mPendingFrame = null;
|
||||
mPendingVisibleFrame = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets requested visibility from the client, regardless of whether we are able to control it at
|
||||
* the moment.
|
||||
*/
|
||||
protected void setRequestedVisible(boolean requestedVisible) {
|
||||
mController.setRequestedVisibleTypes(requestedVisible ? mType : 0, mType);
|
||||
if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
|
||||
if (applyLocalVisibilityOverride()) {
|
||||
mController.notifyVisibilityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyRequestedVisibilityToControl() {
|
||||
if (mSourceControl == null || mSourceControl.getLeash() == null) {
|
||||
return;
|
||||
|
||||
@@ -102,7 +102,8 @@ public class InsetsAnimationControlImplTest {
|
||||
new InsetsSourceControl(ITYPE_NAVIGATION_BAR, WindowInsets.Type.navigationBars(),
|
||||
mNavLeash, true, new Point(400, 0), Insets.of(0, 0, 100, 0)),
|
||||
new int[1], new int[1]);
|
||||
navConsumer.hide();
|
||||
mMockController.setRequestedVisibleTypes(0, WindowInsets.Type.navigationBars());
|
||||
navConsumer.applyLocalVisibilityOverride();
|
||||
|
||||
SparseArray<InsetsSourceControl> controls = new SparseArray<>();
|
||||
controls.put(ITYPE_STATUS_BAR, topConsumer.getControl());
|
||||
|
||||
@@ -132,17 +132,10 @@ public class InsetsControllerTest {
|
||||
|
||||
private boolean mImeRequestedShow;
|
||||
|
||||
@Override
|
||||
public void show(boolean fromIme) {
|
||||
super.show(fromIme);
|
||||
if (fromIme) {
|
||||
mImeRequestedShow = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requestShow(boolean fromController) {
|
||||
if (fromController || mImeRequestedShow) {
|
||||
mImeRequestedShow = true;
|
||||
return SHOW_IMMEDIATELY;
|
||||
} else {
|
||||
return IME_SHOW_DELAYED;
|
||||
@@ -815,10 +808,10 @@ public class InsetsControllerTest {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
prepareControls();
|
||||
|
||||
// Hiding visible system bars should only causes insets change once for each bar.
|
||||
// Calling to hide system bars once should only cause insets change once.
|
||||
clearInvocations(mTestHost);
|
||||
mController.hide(statusBars() | navigationBars());
|
||||
verify(mTestHost, times(2)).notifyInsetsChanged();
|
||||
verify(mTestHost, times(1)).notifyInsetsChanged();
|
||||
|
||||
// Sending the same insets state should not cause insets change.
|
||||
// This simulates the callback from server after hiding system bars.
|
||||
@@ -826,10 +819,10 @@ public class InsetsControllerTest {
|
||||
mController.onStateChanged(mController.getState());
|
||||
verify(mTestHost, never()).notifyInsetsChanged();
|
||||
|
||||
// Showing invisible system bars should only causes insets change once for each bar.
|
||||
// Calling to show system bars once should only cause insets change once.
|
||||
clearInvocations(mTestHost);
|
||||
mController.show(statusBars() | navigationBars());
|
||||
verify(mTestHost, times(2)).notifyInsetsChanged();
|
||||
verify(mTestHost, times(1)).notifyInsetsChanged();
|
||||
|
||||
// Sending the same insets state should not cause insets change.
|
||||
// This simulates the callback from server after showing system bars.
|
||||
@@ -912,7 +905,7 @@ public class InsetsControllerTest {
|
||||
assertNull(imeInsetsConsumer.getControl());
|
||||
|
||||
// Verify IME requested visibility should be updated to IME consumer from controller.
|
||||
mController.show(ime());
|
||||
mController.show(ime(), true /* fromIme */, null /* statsToken */);
|
||||
assertTrue(isRequestedVisible(mController, ime()));
|
||||
|
||||
mController.hide(ime());
|
||||
|
||||
@@ -29,7 +29,6 @@ import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -119,30 +118,33 @@ public class InsetsSourceConsumerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHide() {
|
||||
public void testOnAnimationStateChanged_requestedInvisible() {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
mConsumer.hide();
|
||||
mController.setRequestedVisibleTypes(0 /* visibleTypes */, mSpyInsetsSource.getType());
|
||||
mConsumer.onAnimationStateChanged(false /* running */);
|
||||
verify(mSpyInsetsSource).setVisible(eq(false));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShow() {
|
||||
public void testOnAnimationStateChanged_requestedVisible() {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
// Insets source starts out visible
|
||||
mConsumer.hide();
|
||||
mConsumer.show(false /* fromIme */);
|
||||
final int type = mSpyInsetsSource.getType();
|
||||
mController.setRequestedVisibleTypes(0 /* visibleTypes */, type);
|
||||
mConsumer.onAnimationStateChanged(false /* running */);
|
||||
mController.setRequestedVisibleTypes(type, type);
|
||||
mConsumer.onAnimationStateChanged(false /* running */);
|
||||
verify(mSpyInsetsSource).setVisible(eq(false));
|
||||
verify(mSpyInsetsSource).setVisible(eq(true));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPendingStates() {
|
||||
InsetsState state = new InsetsState();
|
||||
InsetsController controller = mock(InsetsController.class);
|
||||
InsetsController controller = new InsetsController(new ViewRootInsetsControllerHost(
|
||||
mViewRoot));
|
||||
InsetsSourceConsumer consumer = new InsetsSourceConsumer(
|
||||
ITYPE_IME, state, null, controller);
|
||||
|
||||
@@ -156,7 +158,7 @@ public class InsetsSourceConsumerTest {
|
||||
assertEquals(new Rect(0, 1, 2, 3), state.peekSource(ITYPE_IME).getFrame());
|
||||
|
||||
// Finish the animation, now the pending frame should be applied
|
||||
assertTrue(consumer.notifyAnimationFinished());
|
||||
assertTrue(consumer.onAnimationStateChanged(false /* running */));
|
||||
assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
|
||||
|
||||
// Animating again, updates are delayed
|
||||
@@ -169,7 +171,7 @@ public class InsetsSourceConsumerTest {
|
||||
source.setFrame(4, 5, 6, 7);
|
||||
consumer.updateSource(new InsetsSource(source), ANIMATION_TYPE_USER);
|
||||
|
||||
assertFalse(consumer.notifyAnimationFinished());
|
||||
assertFalse(consumer.onAnimationStateChanged(false /* running */));
|
||||
assertEquals(new Rect(4, 5, 6, 7), state.peekSource(ITYPE_IME).getFrame());
|
||||
}
|
||||
|
||||
@@ -178,7 +180,7 @@ public class InsetsSourceConsumerTest {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
mConsumer.setControl(null, new int[1], new int[1]);
|
||||
reset(mMockTransaction);
|
||||
mConsumer.hide();
|
||||
mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
|
||||
verifyZeroInteractions(mMockTransaction);
|
||||
int[] hideTypes = new int[1];
|
||||
mConsumer.setControl(
|
||||
@@ -193,7 +195,7 @@ public class InsetsSourceConsumerTest {
|
||||
@Test
|
||||
public void testRestore_noAnimation() {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
mConsumer.hide();
|
||||
mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
|
||||
mConsumer.setControl(null, new int[1], new int[1]);
|
||||
reset(mMockTransaction);
|
||||
verifyZeroInteractions(mMockTransaction);
|
||||
|
||||
Reference in New Issue
Block a user