Fix the logic about notifyImeHidden and removeImeSurface
This CL fixes 2 issues.
For b/263472769, this CL restores the logic in collectSourceControls,
that when a type is requested to hide, we will let its consumer know, so
that it will have a chance to notify the remote control target. Also,
when IME is not ready, we only delayed the requested visibilities for
the types ready. In this way, the client can still send the requested
visibilities and notifyImeHidden to the server in split-screen mode,
when the client doesn't have the control.
For b/263345079, this CL refines the logic about handling pending
request in ImeInsetsSourceConsumer. If there is an opposite request
against to current animation, we won't remove the IME surface when
current animation ends.
Fix: 263472769
Fix: 263345079
Test: 1. Test if IME can be hidden by API in split-screen.
2. atest ImeOpenCloseStressTest
Change-Id: I59952623bd0914aa236bd8f421c958f167cca63d
This commit is contained in:
@@ -17,10 +17,9 @@
|
||||
package android.view;
|
||||
|
||||
import static android.os.Trace.TRACE_TAG_VIEW;
|
||||
import static android.view.ImeInsetsSourceConsumerProto.HAS_PENDING_REQUEST;
|
||||
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 android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
@@ -42,22 +41,18 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
|
||||
/**
|
||||
* Tracks whether are requested to show during the hide animation or requested to hide during
|
||||
* the show animation. If this is true, we should not remove the surface.
|
||||
*/
|
||||
private boolean mHasPendingRequest;
|
||||
|
||||
/**
|
||||
* Tracks whether we have an outstanding request from the IME to show, but weren't able to
|
||||
* execute it because we didn't have control yet.
|
||||
*/
|
||||
private boolean mIsRequestedVisibleAwaitingControl;
|
||||
|
||||
private boolean mIsHideAnimationRunning;
|
||||
|
||||
/**
|
||||
* Tracks whether {@link WindowInsetsController#show(int)} or
|
||||
* {@link InputMethodManager#showSoftInput(View, int)} is called during IME hide animation.
|
||||
* If it was called, we should not call {@link InputMethodManager#notifyImeHidden(IBinder,
|
||||
* ImeTracker.Token)}, because the IME is being shown.
|
||||
*/
|
||||
private boolean mIsShowRequestedDuringHideAnimation;
|
||||
|
||||
public ImeInsetsSourceConsumer(
|
||||
int id, InsetsState state, Supplier<Transaction> transactionSupplier,
|
||||
InsetsController controller) {
|
||||
@@ -72,27 +67,19 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
mController.getHost().getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
final boolean insetsChanged = super.onAnimationStateChanged(running);
|
||||
final boolean showRequested = (mController.getRequestedVisibleTypes() & getType()) != 0;
|
||||
if (showRequested) {
|
||||
onShowRequested();
|
||||
} else {
|
||||
if (!isShowRequested()) {
|
||||
mIsRequestedVisibleAwaitingControl = false;
|
||||
if (!running) {
|
||||
// Remove IME surface as IME has finished hide animation, if there is no pending
|
||||
// show request.
|
||||
if (!mIsShowRequestedDuringHideAnimation) {
|
||||
notifyHidden(null /* statsToken */);
|
||||
removeSurface();
|
||||
}
|
||||
if (!running && !mHasPendingRequest) {
|
||||
notifyHidden(null /* statsToken */);
|
||||
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;
|
||||
}
|
||||
// This method is called
|
||||
// (1) after the animation starts.
|
||||
// (2) after the animation ends (including the case of cancel).
|
||||
// (3) if the IME is not controllable (running == false in this case).
|
||||
// We should reset mHasPendingRequest in all cases.
|
||||
mHasPendingRequest = false;
|
||||
return insetsChanged;
|
||||
}
|
||||
|
||||
@@ -132,6 +119,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
"ImeInsetsSourceConsumer#requestShow",
|
||||
mController.getHost().getInputMethodManager(), null /* icProto */);
|
||||
}
|
||||
onShowRequested();
|
||||
|
||||
// TODO: ResultReceiver for IME.
|
||||
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
|
||||
@@ -153,6 +141,17 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
|
||||
}
|
||||
|
||||
void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
|
||||
if (!fromIme) {
|
||||
// The insets might be controlled by a remote target. Let the server know we are
|
||||
// requested to hide.
|
||||
notifyHidden(statsToken);
|
||||
}
|
||||
if (mAnimationState == ANIMATION_STATE_SHOW) {
|
||||
mHasPendingRequest = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
|
||||
* IME insets are hidden.
|
||||
@@ -222,18 +221,17 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
final long token = proto.start(fieldId);
|
||||
super.dumpDebug(proto, INSETS_SOURCE_CONSUMER);
|
||||
proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingControl);
|
||||
proto.write(IS_HIDE_ANIMATION_RUNNING, mIsHideAnimationRunning);
|
||||
proto.write(IS_SHOW_REQUESTED_DURING_HIDE_ANIMATION, mIsShowRequestedDuringHideAnimation);
|
||||
proto.write(HAS_PENDING_REQUEST, mHasPendingRequest);
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when {@link #onAnimationStateChanged(boolean)} or
|
||||
* Called when {@link #requestShow(boolean, ImeTracker.Token)} or
|
||||
* {@link InputMethodManager#showSoftInput(View, int)} is called.
|
||||
*/
|
||||
public void onShowRequested() {
|
||||
if (mIsHideAnimationRunning) {
|
||||
mIsShowRequestedDuringHideAnimation = true;
|
||||
if (mAnimationState == ANIMATION_STATE_HIDE) {
|
||||
mHasPendingRequest = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1203,8 +1203,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
return;
|
||||
}
|
||||
ImeTracker.get().onProgress(statsToken, ImeTracker.PHASE_CLIENT_DISABLED_USER_ANIMATION);
|
||||
|
||||
cancelExistingControllers(types);
|
||||
if (DEBUG) Log.d(TAG, "controlAnimation types: " + types);
|
||||
mLastStartedAnimTypes |= types;
|
||||
|
||||
@@ -1236,9 +1234,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
});
|
||||
}
|
||||
|
||||
// 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);
|
||||
// The requested visibilities should be delayed as well. Otherwise, we might override
|
||||
// the insets visibility before playing animation.
|
||||
setRequestedVisibleTypes(mReportedRequestedVisibleTypes, typesReady);
|
||||
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
|
||||
if (!fromIme) {
|
||||
@@ -1250,7 +1248,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
if (typesReady == 0) {
|
||||
if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
|
||||
listener.onCancelled(null);
|
||||
reportRequestedVisibleTypes();
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
|
||||
if (!fromIme) {
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0);
|
||||
@@ -1258,6 +1255,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
return;
|
||||
}
|
||||
|
||||
cancelExistingControllers(typesReady);
|
||||
|
||||
final InsetsAnimationControlRunner runner = useInsetsAnimationThread
|
||||
? new InsetsAnimationThreadControlRunner(controls,
|
||||
@@ -1344,6 +1342,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
setRequestedVisibleTypes(0 /* visibleTypes */, consumer.getType());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
consumer.requestHide(fromIme, statsToken);
|
||||
}
|
||||
if (!canRun) {
|
||||
if (WARN) Log.w(TAG, String.format(
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.view;
|
||||
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
|
||||
import static android.view.InsetsController.AnimationType;
|
||||
import static android.view.InsetsController.DEBUG;
|
||||
import static android.view.InsetsSourceConsumerProto.ANIMATION_STATE;
|
||||
import static android.view.InsetsSourceConsumerProto.HAS_WINDOW_FOCUS;
|
||||
import static android.view.InsetsSourceConsumerProto.INTERNAL_INSETS_TYPE;
|
||||
import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
|
||||
@@ -73,6 +74,12 @@ public class InsetsSourceConsumer {
|
||||
int IME_SHOW_FAILED = 2;
|
||||
}
|
||||
|
||||
protected static final int ANIMATION_STATE_NONE = 0;
|
||||
protected static final int ANIMATION_STATE_SHOW = 1;
|
||||
protected static final int ANIMATION_STATE_HIDE = 2;
|
||||
|
||||
protected int mAnimationState = ANIMATION_STATE_NONE;
|
||||
|
||||
protected final InsetsController mController;
|
||||
protected final InsetsState mState;
|
||||
private int mId;
|
||||
@@ -230,14 +237,31 @@ public class InsetsSourceConsumer {
|
||||
mPendingVisibleFrame = null;
|
||||
}
|
||||
|
||||
final boolean showRequested = isShowRequested();
|
||||
final boolean cancelledForNewAnimation = !running && showRequested
|
||||
? mAnimationState == ANIMATION_STATE_HIDE
|
||||
: mAnimationState == ANIMATION_STATE_SHOW;
|
||||
|
||||
mAnimationState = running
|
||||
? (showRequested ? ANIMATION_STATE_SHOW : ANIMATION_STATE_HIDE)
|
||||
: ANIMATION_STATE_NONE;
|
||||
|
||||
// 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();
|
||||
// If the animation is cancelled because we are going to play a new animation with an
|
||||
// opposite direction, don't apply it now but after the new animation is started.
|
||||
if (!cancelledForNewAnimation) {
|
||||
insetsChanged |= applyLocalVisibilityOverride();
|
||||
}
|
||||
return insetsChanged;
|
||||
}
|
||||
|
||||
protected boolean isShowRequested() {
|
||||
return (mController.getRequestedVisibleTypes() & getType()) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when current window gains focus
|
||||
*/
|
||||
@@ -300,6 +324,10 @@ public class InsetsSourceConsumer {
|
||||
return ShowResult.SHOW_IMMEDIATELY;
|
||||
}
|
||||
|
||||
void requestHide(boolean fromController, @Nullable ImeTracker.Token statsToken) {
|
||||
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that this source's perceptibility has changed
|
||||
*
|
||||
@@ -364,7 +392,7 @@ public class InsetsSourceConsumer {
|
||||
final long token = proto.start(fieldId);
|
||||
proto.write(INTERNAL_INSETS_TYPE, WindowInsets.Type.toString(mType));
|
||||
proto.write(HAS_WINDOW_FOCUS, mHasWindowFocus);
|
||||
proto.write(IS_REQUESTED_VISIBLE, (mController.getRequestedVisibleTypes() & mType) != 0);
|
||||
proto.write(IS_REQUESTED_VISIBLE, isShowRequested());
|
||||
if (mSourceControl != null) {
|
||||
mSourceControl.dumpDebug(proto, SOURCE_CONTROL);
|
||||
}
|
||||
@@ -374,6 +402,7 @@ public class InsetsSourceConsumer {
|
||||
if (mPendingVisibleFrame != null) {
|
||||
mPendingVisibleFrame.dumpDebug(proto, PENDING_VISIBLE_FRAME);
|
||||
}
|
||||
proto.write(ANIMATION_STATE, mAnimationState);
|
||||
proto.end(token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ message ImeInsetsSourceConsumerProto {
|
||||
optional InsetsSourceConsumerProto insets_source_consumer = 1;
|
||||
reserved 2; // focused_editor = 2
|
||||
optional bool is_requested_visible_awaiting_control = 3;
|
||||
optional bool is_hide_animation_running = 4;
|
||||
optional bool is_show_requested_during_hide_animation = 5;
|
||||
optional bool is_hide_animation_running = 4 [deprecated=true];
|
||||
optional bool is_show_requested_during_hide_animation = 5 [deprecated=true];
|
||||
optional bool has_pending_request = 6;
|
||||
}
|
||||
@@ -33,4 +33,5 @@ message InsetsSourceConsumerProto {
|
||||
optional InsetsSourceControlProto source_control = 4;
|
||||
optional .android.graphics.RectProto pending_frame = 5;
|
||||
optional .android.graphics.RectProto pending_visible_frame = 6;
|
||||
optional int32 animation_state = 7;
|
||||
}
|
||||
Reference in New Issue
Block a user