Merge "IME animation: hide IME-related navbar icons until perceptible" into rvc-dev am: e658c76b5c am: ba68b71a5c
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11837615 Change-Id: I897a1afc63f971cb8061d02c44dad41ce5b84031
This commit is contained in:
@@ -21,6 +21,7 @@ import static android.view.InsetsState.ITYPE_IME;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.text.TextUtils;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
@@ -153,6 +154,15 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
|
||||
return mIsRequestedVisibleAwaitingControl || isRequestedVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPerceptible(boolean perceptible) {
|
||||
super.onPerceptible(perceptible);
|
||||
final IBinder window = mController.getHost().getWindowToken();
|
||||
if (window != null) {
|
||||
getImm().reportPerceptible(window, perceptible);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDummyOrEmptyEditor(EditorInfo info) {
|
||||
// TODO(b/123044812): Handle dummy input gracefully in IME Insets API
|
||||
return info == null || (info.fieldId <= 0 && info.inputType <= 0);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.view.WindowInsets.Type.InsetsType;
|
||||
import android.view.WindowInsetsAnimation.Bounds;
|
||||
|
||||
/**
|
||||
@@ -64,4 +65,15 @@ public interface InsetsAnimationControlCallbacks {
|
||||
* previous calls to applySurfaceParams.
|
||||
*/
|
||||
void releaseSurfaceControlFromRt(SurfaceControl sc);
|
||||
|
||||
/**
|
||||
* Reports that the perceptibility of the given types has changed to the given value.
|
||||
*
|
||||
* A type is perceptible if it is not (almost) entirely off-screen and not (almost) entirely
|
||||
* transparent.
|
||||
*
|
||||
* @param types the (public) types whose perceptibility has changed
|
||||
* @param perceptible true, if the types are now perceptible, false if they are not perceptible
|
||||
*/
|
||||
void reportPerceptible(@InsetsType int types, boolean perceptible);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
private float mPendingAlpha = 1.0f;
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public boolean mReadyDispatched;
|
||||
private Boolean mPerceptible;
|
||||
|
||||
@VisibleForTesting
|
||||
public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame,
|
||||
@@ -121,6 +122,14 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
new Bounds(mHiddenInsets, mShownInsets));
|
||||
}
|
||||
|
||||
private boolean calculatePerceptible(Insets currentInsets, float currentAlpha) {
|
||||
return 100 * currentInsets.left >= 5 * (mShownInsets.left - mHiddenInsets.left)
|
||||
&& 100 * currentInsets.top >= 5 * (mShownInsets.top - mHiddenInsets.top)
|
||||
&& 100 * currentInsets.right >= 5 * (mShownInsets.right - mHiddenInsets.right)
|
||||
&& 100 * currentInsets.bottom >= 5 * (mShownInsets.bottom - mHiddenInsets.bottom)
|
||||
&& currentAlpha >= 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasZeroInsetsIme() {
|
||||
return mHasZeroInsetsIme;
|
||||
@@ -175,6 +184,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
mPendingInsets = sanitize(insets);
|
||||
mPendingAlpha = sanitize(alpha);
|
||||
mController.scheduleApplyChangeInsets(this);
|
||||
boolean perceptible = calculatePerceptible(mPendingInsets, mPendingAlpha);
|
||||
if (mPerceptible == null || perceptible != mPerceptible) {
|
||||
mController.reportPerceptible(mTypes, perceptible);
|
||||
mPerceptible = perceptible;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -90,6 +90,11 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
|
||||
// Since we don't push the SurfaceParams to the RT we can release directly
|
||||
sc.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportPerceptible(int types, boolean perceptible) {
|
||||
mMainThreadHandler.post(() -> mOuterCallbacks.reportPerceptible(types, perceptible));
|
||||
}
|
||||
};
|
||||
|
||||
@UiThread
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Trace;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
@@ -165,6 +166,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
/** @see ViewRootImpl#dipToPx */
|
||||
int dipToPx(int dips);
|
||||
|
||||
/**
|
||||
* @return token associated with the host, if it has one.
|
||||
*/
|
||||
@Nullable
|
||||
IBinder getWindowToken();
|
||||
}
|
||||
|
||||
private static final String TAG = "InsetsController";
|
||||
@@ -1353,6 +1360,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
mHost.releaseSurfaceControlFromRt(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportPerceptible(int types, boolean perceptible) {
|
||||
final ArraySet<Integer> internalTypes = toInternalType(types);
|
||||
final int size = mSourceConsumers.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
|
||||
if (internalTypes.contains(consumer.getType())) {
|
||||
consumer.onPerceptible(perceptible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Host getHost() {
|
||||
return mHost;
|
||||
}
|
||||
|
||||
@@ -260,6 +260,15 @@ public class InsetsSourceConsumer {
|
||||
return ShowResult.SHOW_IMMEDIATELY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that this source's perceptibility has changed
|
||||
*
|
||||
* @param perceptible true if the source is perceptible, false otherwise.
|
||||
* @see InsetsAnimationControlCallbacks#reportPerceptible
|
||||
*/
|
||||
public void onPerceptible(boolean perceptible) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify listeners that window is now hidden.
|
||||
*/
|
||||
@@ -339,5 +348,6 @@ public class InsetsSourceConsumer {
|
||||
t.hide(mSourceControl.getLeash());
|
||||
}
|
||||
t.apply();
|
||||
onPerceptible(mRequestedVisible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONT
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
@@ -236,4 +237,16 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder getWindowToken() {
|
||||
if (mViewRoot == null) {
|
||||
return null;
|
||||
}
|
||||
final View view = mViewRoot.getView();
|
||||
if (view == null) {
|
||||
return null;
|
||||
}
|
||||
return view.getWindowToken();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,6 +541,19 @@ public final class InputMethodManager {
|
||||
return servedView.hasWindowFocus() || isAutofillUIShowing(servedView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports whether the IME is currently perceptible or not, according to the leash applied by
|
||||
* {@link android.view.WindowInsetsController}.
|
||||
* @hide
|
||||
*/
|
||||
public void reportPerceptible(IBinder windowToken, boolean perceptible) {
|
||||
try {
|
||||
mService.reportPerceptible(windowToken, perceptible);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private final class DelegateImpl implements
|
||||
ImeFocusController.InputMethodManagerDelegate {
|
||||
/**
|
||||
|
||||
@@ -72,5 +72,6 @@ interface IInputMethodManager {
|
||||
void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
|
||||
in float[] matrixValues);
|
||||
|
||||
oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
|
||||
void removeImeSurface();
|
||||
}
|
||||
|
||||
@@ -234,6 +234,24 @@ public class InsetsAnimationControlImplTest {
|
||||
verify(mMockListener).onFinished(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerceptible_insets() {
|
||||
mController.setInsetsAndAlpha(mController.getHiddenStateInsets(), 1f, 1f);
|
||||
verify(mMockController).reportPerceptible(systemBars(), false);
|
||||
|
||||
mController.setInsetsAndAlpha(mController.getShownStateInsets(), 1f, 1f);
|
||||
verify(mMockController).reportPerceptible(systemBars(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerceptible_alpha() {
|
||||
mController.setInsetsAndAlpha(mController.getShownStateInsets(), 0f, 1f);
|
||||
verify(mMockController).reportPerceptible(systemBars(), false);
|
||||
|
||||
mController.setInsetsAndAlpha(mController.getShownStateInsets(), 1f, 1f);
|
||||
verify(mMockController).reportPerceptible(systemBars(), true);
|
||||
}
|
||||
|
||||
private void assertPosition(Matrix m, Rect original, Rect transformed) {
|
||||
RectF rect = new RectF(original);
|
||||
rect.offsetTo(0, 0);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.inputmethod;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.os.IBinder;
|
||||
import android.view.inputmethod.InlineSuggestionsRequest;
|
||||
@@ -108,6 +109,16 @@ public abstract class InputMethodManagerInternal {
|
||||
public abstract boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
|
||||
int displayId);
|
||||
|
||||
/**
|
||||
* Reports that IME control has transferred to the given window token, or if null that
|
||||
* control has been taken away from client windows (and is instead controlled by the policy
|
||||
* or SystemUI).
|
||||
*
|
||||
* @param windowToken the window token that is now in control, or {@code null} if no client
|
||||
* window is in control of the IME.
|
||||
*/
|
||||
public abstract void reportImeControl(@Nullable IBinder windowToken);
|
||||
|
||||
/**
|
||||
* Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
|
||||
*/
|
||||
@@ -151,6 +162,10 @@ public abstract class InputMethodManagerInternal {
|
||||
int displayId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportImeControl(@Nullable IBinder windowToken) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -179,6 +179,7 @@ import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -592,6 +593,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
// Was the keyguard locked when this client became current?
|
||||
private boolean mCurClientInKeyguard;
|
||||
|
||||
/**
|
||||
* {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
|
||||
*/
|
||||
private boolean mCurPerceptible;
|
||||
|
||||
/**
|
||||
* Set to true if our ServiceConnection is currently actively bound to
|
||||
* a service (whether or not we have gotten its IBinder back yet).
|
||||
@@ -2936,6 +2942,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) {
|
||||
vis = 0;
|
||||
}
|
||||
if (!mCurPerceptible) {
|
||||
vis = 0;
|
||||
}
|
||||
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
|
||||
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
|
||||
if (mStatusBar != null) {
|
||||
@@ -3148,6 +3157,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@Override
|
||||
public void reportPerceptible(IBinder windowToken, boolean perceptible) {
|
||||
Objects.requireNonNull(windowToken, "windowToken must not be null");
|
||||
int uid = Binder.getCallingUid();
|
||||
synchronized (mMethodMap) {
|
||||
if (!calledFromValidUserLocked()) {
|
||||
return;
|
||||
}
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (mCurFocusedWindow == windowToken
|
||||
&& mCurPerceptible != perceptible) {
|
||||
mCurPerceptible = perceptible;
|
||||
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mMethodMap")
|
||||
boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
|
||||
@SoftInputShowHideReason int reason) {
|
||||
@@ -3451,6 +3482,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
mCurFocusedWindow = windowToken;
|
||||
mCurFocusedWindowSoftInputMode = softInputMode;
|
||||
mCurFocusedWindowClient = cs;
|
||||
mCurPerceptible = true;
|
||||
|
||||
// Should we auto-show the IME even if the caller has not
|
||||
// specified what should be done with it?
|
||||
@@ -5010,6 +5042,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
|
||||
}
|
||||
|
||||
private void reportImeControl(@Nullable IBinder windowToken) {
|
||||
synchronized (mMethodMap) {
|
||||
if (mCurFocusedWindow != windowToken) {
|
||||
// mCurPerceptible was set by the focused window, but it is no longer in control,
|
||||
// so we reset mCurPerceptible.
|
||||
mCurPerceptible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LocalServiceImpl extends InputMethodManagerInternal {
|
||||
@NonNull
|
||||
private final InputMethodManagerService mService;
|
||||
@@ -5062,6 +5104,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
int displayId) {
|
||||
return mService.transferTouchFocusToImeWindow(sourceInputToken, displayId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportImeControl(@Nullable IBinder windowToken) {
|
||||
mService.reportImeControl(windowToken);
|
||||
}
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@@ -5164,6 +5211,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
p.println(" mCurMethodId=" + mCurMethodId);
|
||||
client = mCurClient;
|
||||
p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
|
||||
p.println(" mCurPerceptible=" + mCurPerceptible);
|
||||
p.println(" mCurFocusedWindow=" + mCurFocusedWindow
|
||||
+ " softInputMode=" +
|
||||
InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
|
||||
|
||||
@@ -221,6 +221,10 @@ public final class MultiClientInputMethodManagerService {
|
||||
reportNotSupported();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportImeControl(@Nullable IBinder windowToken) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1751,6 +1755,12 @@ public final class MultiClientInputMethodManagerService {
|
||||
reportNotSupported();
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@Override
|
||||
public void reportPerceptible(IBinder windowClient, boolean perceptible) {
|
||||
reportNotSupported();
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@Override
|
||||
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
|
||||
|
||||
@@ -210,6 +210,7 @@ import com.android.internal.util.function.pooled.PooledConsumer;
|
||||
import com.android.internal.util.function.pooled.PooledFunction;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.internal.util.function.pooled.PooledPredicate;
|
||||
import com.android.server.inputmethod.InputMethodManagerInternal;
|
||||
import com.android.server.policy.WindowManagerPolicy;
|
||||
import com.android.server.protolog.common.ProtoLog;
|
||||
import com.android.server.wm.utils.DisplayRotationUtil;
|
||||
@@ -3566,6 +3567,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
private void updateImeControlTarget() {
|
||||
mInputMethodControlTarget = computeImeControlTarget();
|
||||
mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
|
||||
|
||||
final WindowState win = mInputMethodControlTarget != null
|
||||
? mInputMethodControlTarget.getWindow() : null;
|
||||
final IBinder token = win != null ? win.mClient.asBinder() : null;
|
||||
// Note: not allowed to call into IMMS with the WM lock held, hence the post.
|
||||
mWmService.mH.post(() ->
|
||||
InputMethodManagerInternal.get().reportImeControl(token)
|
||||
);
|
||||
}
|
||||
|
||||
private void updateImeParent() {
|
||||
|
||||
@@ -482,6 +482,12 @@ class InsetsPolicy {
|
||||
WindowInsetsAnimation animation,
|
||||
Bounds bounds) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportPerceptible(int types, boolean perceptible) {
|
||||
// No-op for now - only client windows report perceptibility for now, with policy
|
||||
// controllers assumed to always be perceptible.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user