From f1e0887eb83483eff1ef9fee8de72266387d45b8 Mon Sep 17 00:00:00 2001 From: Taran Singh Date: Thu, 10 Oct 2019 14:38:52 +0200 Subject: Ime target window should control when to hide IME (1/2) Input method frameworks uses focused window and some tricks to best guestimate IME target. Though it doesn't always know the actual IME target. e.g. when window has both NOT_FOCUSABLE, ALT_FOCUSABLE_IM; IMF thinks focued window is IME target but it isn't the case. The right thing to do is to call both show(IME), hide(IME) on IME target. Bug: 142461756 Bug: 111084606 Test: Manually tested using steps below: 1. Make sure new insets flag is enabled 2. Launch any activity which has child window with NOT_FOCUSABLE, ALT_FOCUSABLE_IM (e.g. Instagram login screen) 3. Verify IME can be shown and hidden by this window. Change-Id: I13f3e04f6f9e1574db9cbb56bdb7817152499d03 --- core/java/android/view/IWindow.aidl | 8 ++++++++ core/java/android/view/ViewRootImpl.java | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) (limited to 'core/java/android') diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 4b872d3ad758..8bf99ec4f251 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -81,6 +81,14 @@ oneway interface IWindow { */ void showInsets(int types, boolean fromIme); + /** + * Called when a set of insets source window should be hidden by policy. + * + * @param types internal inset types (WindowInsets.Type.InsetType) to hide + * @param fromIme true if this request originated from IME (InputMethodService). + */ + void hideInsets(int types, boolean fromIme); + void moved(int newX, int newY); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index cf0698575851..c7acf83f10b3 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4530,6 +4530,7 @@ public final class ViewRootImpl implements ViewParent, private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32; private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33; private static final int MSG_SHOW_INSETS = 34; + private static final int MSG_HIDE_INSETS = 35; final class ViewRootHandler extends Handler { @@ -4596,6 +4597,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED"; case MSG_SHOW_INSETS: return "MSG_SHOW_INSETS"; + case MSG_HIDE_INSETS: + return "MSG_HIDE_INSETS"; } return super.getMessageName(message); } @@ -4714,6 +4717,10 @@ public final class ViewRootImpl implements ViewParent, mInsetsController.show(msg.arg1, msg.arg2 == 1); break; } + case MSG_HIDE_INSETS: { + mInsetsController.hide(msg.arg1, msg.arg2 == 1); + break; + } case MSG_WINDOW_MOVED: if (mAdded) { final int w = mWinFrame.width(); @@ -7513,6 +7520,10 @@ public final class ViewRootImpl implements ViewParent, mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0).sendToTarget(); } + private void hideInsets(@InsetType int types, boolean fromIme) { + mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0).sendToTarget(); + } + public void dispatchMoved(int newX, int newY) { if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY); if (mTranslator != null) { @@ -8635,6 +8646,14 @@ public final class ViewRootImpl implements ViewParent, } } + @Override + public void hideInsets(@InsetType int types, boolean fromIme) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.hideInsets(types, fromIme); + } + } + @Override public void moved(int newX, int newY) { final ViewRootImpl viewAncestor = mViewAncestor.get(); -- cgit v1.2.3 From d7fc5864f44a7cff1e4b9851d5675a2c2b9541f6 Mon Sep 17 00:00:00 2001 From: Taran Singh Date: Thu, 10 Oct 2019 14:45:17 +0200 Subject: Ime target window should control when to hide IME (2/2) This followup CL implements hideInsets() introduced in 1/2. Bug: 142461756 Bug: 111084606 Test: Manually tested using steps below: 1. Make sure new insets flag is enabled 2. Launch any activity which has child window with NOT_FOCUSABLE, ALT_FOCUSABLE_IM (e.g. Instagram login screen) 3. Verify IME can be shown and hidden by this window. Change-Id: I307594014eca8a06397c739ffbc9c12eac160fdc --- .../android/view/InsetsAnimationControlImpl.java | 4 ++ core/java/android/view/InsetsController.java | 65 +++++++++++----------- 2 files changed, 35 insertions(+), 34 deletions(-) (limited to 'core/java/android') diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 341c2147c64a..e4deffadc966 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -229,6 +229,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll final InsetsSourceConsumer consumer = items.valueAt(i); final InsetsSource source = mInitialInsetsState.getSource(consumer.getType()); final InsetsSourceControl control = consumer.getControl(); + if (control == null) { + // Control may not be available for consumer yet or revoked. + continue; + } final SurfaceControl leash = consumer.getControl().getLeash(); mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 5a8636d85a08..5bb4f63d62c8 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -251,6 +251,10 @@ public class InsetsController implements WindowInsetsController { @Override public void hide(@InsetType int types) { + hide(types, false /* fromIme */); + } + + void hide(@InsetType int types, boolean fromIme) { int typesReady = 0; final ArraySet internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { @@ -265,7 +269,7 @@ public class InsetsController implements WindowInsetsController { } typesReady |= InsetsState.toPublicType(consumer.getType()); } - applyAnimation(typesReady, false /* show */, false /* fromIme */); + applyAnimation(typesReady, false /* show */, fromIme /* fromIme */); } @Override @@ -331,42 +335,35 @@ public class InsetsController implements WindowInsetsController { boolean isReady = true; for (int i = internalTypes.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); - // Double check for IME that IME target window has focus. - if (consumer.getType() != TYPE_IME || consumer.hasWindowFocus()) { - boolean setVisible = !consumer.isVisible(); - if (setVisible) { - // Show request - switch(consumer.requestShow(fromIme)) { - case ShowResult.SHOW_IMMEDIATELY: - typesReady |= InsetsState.toPublicType(consumer.getType()); - break; - case ShowResult.SHOW_DELAYED: - isReady = false; - break; - case ShowResult.SHOW_FAILED: - // IME cannot be shown (since it didn't have focus), proceed - // with animation of other types. - if (mPendingTypesToShow != 0) { - // remove IME from pending because view no longer has focus. - mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME); - } - break; - } - } else { - // Hide request - // TODO: Move notifyHidden() to beginning of the hide animation - // (when visibility actually changes using hideDirectly()). - consumer.notifyHidden(); - typesReady |= InsetsState.toPublicType(consumer.getType()); + boolean setVisible = !consumer.isVisible(); + if (setVisible) { + // Show request + switch(consumer.requestShow(fromIme)) { + case ShowResult.SHOW_IMMEDIATELY: + typesReady |= InsetsState.toPublicType(consumer.getType()); + break; + case ShowResult.SHOW_DELAYED: + isReady = false; + break; + case ShowResult.SHOW_FAILED: + // IME cannot be shown (since it didn't have focus), proceed + // with animation of other types. + if (mPendingTypesToShow != 0) { + // remove IME from pending because view no longer has focus. + mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME); + } + break; } - consumers.put(consumer.getType(), consumer); } else { - // window doesnt have focus, no-op. - isReady = false; - // TODO: Let the calling app know that window has lost focus and - // show()/hide()/controlWindowInsetsAnimation requests will be ignored. - typesReady &= ~InsetsState.toPublicType(consumer.getType()); + // Hide request + // TODO: Move notifyHidden() to beginning of the hide animation + // (when visibility actually changes using hideDirectly()). + if (!fromIme) { + consumer.notifyHidden(); + } + typesReady |= InsetsState.toPublicType(consumer.getType()); } + consumers.put(consumer.getType(), consumer); } return new Pair<>(typesReady, isReady); } -- cgit v1.2.3