Loading core/java/android/view/ImeInsetsSourceConsumer.java +51 −38 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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); Loading @@ -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(); } /** Loading @@ -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) { Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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) { Loading core/java/android/view/InsetsController.java +79 −95 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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}; Loading @@ -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; Loading Loading @@ -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 " Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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. Loading @@ -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; Loading @@ -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( Loading @@ -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); Loading Loading @@ -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) { Loading @@ -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 Loading Loading @@ -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(); } } Loading Loading @@ -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 Loading Loading @@ -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}. */ Loading core/java/android/view/InsetsSourceConsumer.java +22 −60 File changed.Preview size limit exceeded, changes collapsed. Show changes core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -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()); Loading core/tests/coretests/src/android/view/InsetsControllerTest.java +6 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading @@ -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. Loading Loading @@ -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()); Loading Loading
core/java/android/view/ImeInsetsSourceConsumer.java +51 −38 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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); Loading @@ -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(); } /** Loading @@ -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) { Loading @@ -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 Loading @@ -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) { Loading Loading @@ -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) { Loading
core/java/android/view/InsetsController.java +79 −95 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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}; Loading @@ -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; Loading Loading @@ -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 " Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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. Loading @@ -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; Loading @@ -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( Loading @@ -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); Loading Loading @@ -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) { Loading @@ -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 Loading Loading @@ -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(); } } Loading Loading @@ -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 Loading Loading @@ -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}. */ Loading
core/java/android/view/InsetsSourceConsumer.java +22 −60 File changed.Preview size limit exceeded, changes collapsed. Show changes
core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -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()); Loading
core/tests/coretests/src/android/view/InsetsControllerTest.java +6 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading @@ -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. Loading Loading @@ -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()); Loading