From 847b1d425e7db15a3b4c786add18f809b23c3701 Mon Sep 17 00:00:00 2001 From: Joshua Tsuji Date: Mon, 1 Jun 2020 13:42:19 -0400 Subject: [PATCH] Make the bubbles window NOT_FOCUSABLE when the IME is up, but FOCUSABLE otherwise. This works around an issue where the IME doesn't appear due to bugs with virtual displays + focus. By making Bubbles FOCUSABLE during the initial tap to bring up the IME, we ensure that the Bubbles window has focus and no other windows do. Once the IME is up, we make Bubbles NOT_FOCUSABLE so that swipes on the Bubbles UI doesn't steal focus from the ActivityView. Also, adds a touch listener to the expanded view that prevents the stack from collapsing if the side padding is touched during a back gesture. Test: manual, lots of IME mashing Bug: 156785479 Change-Id: Ia9de170a9ce4c9b99017d0ce6901621f70b414fc --- .../systemui/bubbles/BubbleController.java | 26 ++++++++++---- .../systemui/bubbles/BubbleExpandedView.java | 36 +++++++++++++++++++ .../systemui/bubbles/BubbleStackView.java | 22 ++++++++++-- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index f05d547edd0c7..0690907e8433e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -211,6 +211,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline private final List mCallbacks = new ArrayList<>(); + /** + * Whether the IME is visible, as reported by the BubbleStackView. If it is, we'll make the + * Bubbles window NOT_FOCUSABLE so that touches on the Bubbles UI doesn't steal focus from the + * ActivityView and hide the IME. + */ + private boolean mImeVisible = false; + /** * Listener to find out about stack expansion / collapse events. */ @@ -598,7 +605,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (mStackView == null) { mStackView = new BubbleStackView( mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, - mSysUiState, mNotificationShadeWindowController, this::onAllBubblesAnimatedOut); + mSysUiState, mNotificationShadeWindowController, this::onAllBubblesAnimatedOut, + this::onImeVisibilityChanged); mStackView.addView(mBubbleScrim); if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); @@ -649,6 +657,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } } + private void onImeVisibilityChanged(boolean imeVisible) { + mImeVisible = imeVisible; + updateWmFlags(); + } + /** Removes the BubbleStackView from the WindowManager if it's there. */ private void removeFromWindowManagerMaybe() { if (!mAddedToWindowManager) { @@ -676,13 +689,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * the new params if the stack has been added. */ private void updateWmFlags() { - if (isStackExpanded()) { - // If we're expanded, we want to be focusable so that the ActivityView can receive focus - // and show the IME. + if (isStackExpanded() && !mImeVisible) { + // If we're expanded, and the IME isn't visible, we want to be focusable. This ensures + // that any taps within Bubbles (including on the ActivityView) results in Bubbles + // receiving focus and clearing it from any other windows that might have it. mWmLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } else { - // If we're collapsed, we don't want to be able to receive focus. Doing so would - // preclude applications from using the IME since we are always above them. + // If we're collapsed, we don't want to be focusable since tapping on the stack would + // steal focus from apps. We also don't want to be focusable if the IME is visible, mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 64dc2ccc8b520..fee7847ee2206 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -34,6 +34,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.ActivityView; @@ -239,6 +240,7 @@ public class BubbleExpandedView extends LinearLayout { mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); } + @SuppressLint("ClickableViewAccessibility") @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -290,6 +292,30 @@ public class BubbleExpandedView extends LinearLayout { } return view.onApplyWindowInsets(insets); }); + + final int expandedViewPadding = + res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); + + setPadding( + expandedViewPadding, expandedViewPadding, expandedViewPadding, expandedViewPadding); + setOnTouchListener((view, motionEvent) -> { + if (!usingActivityView()) { + return false; + } + + final Rect avBounds = new Rect(); + mActivityView.getBoundsOnScreen(avBounds); + + // Consume and ignore events on the expanded view padding that are within the + // ActivityView's vertical bounds. These events are part of a back gesture, and so they + // should not collapse the stack (which all other touches on areas around the AV would + // do). + if (motionEvent.getRawY() >= avBounds.top && motionEvent.getRawY() <= avBounds.bottom) { + return true; + } + + return false; + }); } private String getBubbleKey() { @@ -323,9 +349,19 @@ public class BubbleExpandedView extends LinearLayout { } } + /** + * Hides the IME if it's showing. This is currently done by dispatching a back press to the AV. + */ + void hideImeIfVisible() { + if (mKeyboardVisible) { + performBackPressIfNeeded(); + } + } + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + hideImeIfVisible(); mKeyboardVisible = false; mNeedsNewHeight = false; if (mActivityView != null) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 239132ea292ba..fb081e2bf904e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -341,6 +341,12 @@ public class BubbleStackView extends FrameLayout private final NotificationShadeWindowController mNotificationShadeWindowController; + /** + * Callback to run when the IME visibility changes - BubbleController uses this to update the + * Bubbles window focusability flags with the WindowManager. + */ + public final Consumer mOnImeVisibilityChanged; + /** * The currently magnetized object, which is being dragged and will be attracted to the magnetic * dismiss target. @@ -667,7 +673,8 @@ public class BubbleStackView extends FrameLayout FloatingContentCoordinator floatingContentCoordinator, SysUiState sysUiState, NotificationShadeWindowController notificationShadeWindowController, - Runnable allBubblesAnimatedOutAction) { + Runnable allBubblesAnimatedOutAction, + Consumer onImeVisibilityChanged) { super(context); mBubbleData = data; @@ -724,8 +731,6 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer = new FrameLayout(context); mExpandedViewContainer.setElevation(elevation); - mExpandedViewContainer.setPadding(mExpandedViewPadding, mExpandedViewPadding, - mExpandedViewPadding, mExpandedViewPadding); mExpandedViewContainer.setClipChildren(false); addView(mExpandedViewContainer); @@ -793,7 +798,11 @@ public class BubbleStackView extends FrameLayout setUpOverflow(); + mOnImeVisibilityChanged = onImeVisibilityChanged; + setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { + onImeVisibilityChanged.accept(insets.getInsets(WindowInsets.Type.ime()).bottom > 0); + if (!mIsExpanded || mIsExpansionAnimating) { return view.onApplyWindowInsets(insets); } @@ -2126,6 +2135,13 @@ public class BubbleStackView extends FrameLayout if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "updateExpandedBubble()"); } + + if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { + // Hide the currently expanded bubble's IME if it's visible before switching to a new + // bubble. + mExpandedBubble.getExpandedView().hideImeIfVisible(); + } + mExpandedViewContainer.removeAllViews(); if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {