script-astra/Android/Sdk/sources/android-35/android/view/ImeInsetsSourceConsumer.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

261 lines
10 KiB
Java

/*
* Copyright (C) 2019 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.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_REQUESTED_VISIBLE_AWAITING_CONTROL;
import android.annotation.NonNull;
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.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import java.util.function.Supplier;
/**
* Controls the visibility and animations of IME window insets source.
* @hide
*/
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, or we didn't have a leash on the control yet.
*/
private boolean mIsRequestedVisibleAwaitingLeash;
public ImeInsetsSourceConsumer(
int id, InsetsState state, Supplier<Transaction> transactionSupplier,
InsetsController controller) {
super(id, WindowInsets.Type.ime(), state, transactionSupplier, controller);
}
@Override
public boolean onAnimationStateChanged(boolean running) {
if (!running) {
ImeTracing.getInstance().triggerClientDump(
"ImeInsetsSourceConsumer#onAnimationFinished",
mController.getHost().getInputMethodManager(), null /* icProto */);
}
boolean insetsChanged = super.onAnimationStateChanged(running);
if (running && !isShowRequested() && mController.isPredictiveBackImeHideAnimInProgress()) {
// IME predictive back animation switched from pre-commit to post-commit.
insetsChanged |= applyLocalVisibilityOverride();
}
if (!isShowRequested()) {
mIsRequestedVisibleAwaitingLeash = false;
if (!running && !mHasPendingRequest) {
final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
ImeTracker.ORIGIN_CLIENT,
SoftInputShowHideReason.HIDE_SOFT_INPUT_ON_ANIMATION_STATE_CHANGED,
mController.getHost().isHandlingPointerEvent() /* fromUser */);
notifyHidden(statsToken);
removeSurface();
}
}
// 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;
}
@Override
public void onWindowFocusGained(boolean hasViewFocus) {
super.onWindowFocusGained(hasViewFocus);
getImm().registerImeConsumer(this);
if ((mController.getRequestedVisibleTypes() & getType()) != 0 && !hasLeash()) {
mIsRequestedVisibleAwaitingLeash = true;
}
}
@Override
public void onWindowFocusLost() {
super.onWindowFocusLost();
getImm().unregisterImeConsumer(this);
mIsRequestedVisibleAwaitingLeash = false;
}
@Override
public boolean applyLocalVisibilityOverride() {
ImeTracing.getInstance().triggerClientDump(
"ImeInsetsSourceConsumer#applyLocalVisibilityOverride",
mController.getHost().getInputMethodManager(), null /* icProto */);
return super.applyLocalVisibilityOverride();
}
/**
* Request {@link InputMethodManager} to show the IME.
* @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
*/
@Override
@ShowResult
public int requestShow(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
if (fromIme) {
ImeTracing.getInstance().triggerClientDump(
"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.
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW);
if (!hasLeash()) {
// If control or leash is null, schedule to show IME when both available.
mIsRequestedVisibleAwaitingLeash = true;
}
// If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
// this code here means that we now got control, so we can start the animation immediately.
// If client window is trying to control IME and IME is already visible, it is immediate.
if (fromIme
|| (mState.isSourceOrDefaultVisible(getId(), getType()) && hasLeash())) {
return ShowResult.SHOW_IMMEDIATELY;
}
return getImm().requestImeShow(mController.getHost().getWindowToken(), statsToken)
? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
}
void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
if (!fromIme) {
// Create a new token to track the hide request when we have control and leash,
// as we use the passed in token for the insets animation already.
final var notifyStatsToken = hasLeash()
? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
ImeTracker.ORIGIN_CLIENT,
SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
mController.getHost().isHandlingPointerEvent() /* fromUser */)
: statsToken;
// The insets might be controlled by a remote target. Let the server know we are
// requested to hide.
notifyHidden(notifyStatsToken);
}
if (mAnimationState == ANIMATION_STATE_SHOW) {
mHasPendingRequest = true;
}
}
/**
* Notify {@link com.android.server.inputmethod.InputMethodManagerService} that
* IME insets are hidden.
*
* @param statsToken the token tracking the current IME request or {@code null} otherwise.
*/
private void notifyHidden(@NonNull ImeTracker.Token statsToken) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN);
getImm().notifyImeHidden(mController.getHost().getWindowToken(), statsToken);
mIsRequestedVisibleAwaitingLeash = false;
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
}
@Override
public void removeSurface() {
final IBinder window = mController.getHost().getWindowToken();
if (window != null) {
getImm().removeImeSurface(window);
}
}
@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 && !mIsRequestedVisibleAwaitingLeash) {
mController.setRequestedVisibleTypes(0 /* visibleTypes */, getType());
removeSurface();
}
final boolean hasLeash = control != null && control.getLeash() != null;
if (hasLeash) {
mIsRequestedVisibleAwaitingLeash = false;
}
return true;
}
@Override
protected boolean isRequestedVisibleAwaitingControl() {
return super.isRequestedVisibleAwaitingControl() || mIsRequestedVisibleAwaitingLeash;
}
/**
* Checks whether the consumer has an insets source control with a leash.
*/
private boolean hasLeash() {
final var control = getControl();
return control != null && control.getLeash() != null;
}
@Override
public void onPerceptible(boolean perceptible) {
super.onPerceptible(perceptible);
final IBinder window = mController.getHost().getWindowToken();
if (window != null) {
getImm().reportPerceptible(window, perceptible);
}
}
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
super.dumpDebug(proto, INSETS_SOURCE_CONSUMER);
proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingLeash);
proto.write(HAS_PENDING_REQUEST, mHasPendingRequest);
proto.end(token);
}
/**
* Called when {@link #requestShow(boolean, ImeTracker.Token)} or
* {@link InputMethodManager#showSoftInput(View, int)} is called.
*/
public void onShowRequested() {
if (mAnimationState == ANIMATION_STATE_HIDE
|| mController.isPredictiveBackImeHideAnimInProgress()) {
mHasPendingRequest = true;
}
}
private InputMethodManager getImm() {
return mController.getHost().getInputMethodManager();
}
}