Fix back presses dismissing bubbles when IME is up

If bubbles is expanded & shade is not use the virtual display id of the
expanded activity view (if it's valid, otherwise do whats normal).

Test: manual  1) have expanded bubble with IME up
              2) hit back button
              => note that the IME goes away but the bubble remains

              1) have expanded bubble up
              2) pull down shade
              3) hit back button
              => shade goes away

              Do above with gesture nav turned on & do back gesture

Test: atest KeyButtonViewTest
Fixes: 122535136
Change-Id: Iabaace66cfb4d3d80b1a3a3c7b0773deb48fbcaa
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 93effed..13bdb99 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.bubbles;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -530,6 +532,21 @@
         return mTempRect;
     }
 
+    /**
+     * The display id of the expanded view, if the stack is expanded and not occluded by the
+     * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
+     */
+    public int getExpandedDisplayId(Context context) {
+        boolean defaultDisplay = context.getDisplay() != null
+                && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
+        Bubble b = mStackView.getExpandedBubble();
+        if (defaultDisplay && b != null && isStackExpanded()
+                && !mStatusBarWindowController.getPanelExpanded()) {
+            return b.expandedView.getVirtualDisplayId();
+        }
+        return INVALID_DISPLAY;
+    }
+
     @VisibleForTesting
     BubbleStackView getStackView() {
         return mStackView;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 285d4aab..9903314 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -20,6 +20,7 @@
 import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
 import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
 import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
+import static android.view.Display.INVALID_DISPLAY;
 
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
@@ -598,6 +599,16 @@
         return mBubbleIntent != null && mActivityView != null;
     }
 
+    /**
+     * @return the display id of the virtual display.
+     */
+    public int getVirtualDisplayId() {
+        if (usingActivityView()) {
+            return mActivityView.getVirtualDisplayId();
+        }
+        return INVALID_DISPLAY;
+    }
+
     private void applyRowState(ExpandableNotificationRow view) {
         view.reset();
         view.setHeadsUp(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 79bf6b3..8aa50a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.statusbar.phone;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
@@ -46,7 +48,9 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -358,6 +362,13 @@
                 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                 InputDevice.SOURCE_KEYBOARD);
+
+        // Bubble controller will give us a valid display id if it should get the back event
+        BubbleController bubbleController = Dependency.get(BubbleController.class);
+        int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
+        if (code == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
+            ev.setDisplayId(bubbleDisplayId);
+        }
         InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 8d71ab8..984ab05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -523,12 +523,19 @@
     }
 
     /**
-     * The bubble is shown in expanded state for the status bar.
+     * Whether the bubble is shown in expanded state for the status bar.
      */
     public boolean getBubbleExpanded() {
         return mCurrentState.bubbleExpanded;
     }
 
+    /**
+     * Whether the status bar panel is expanded or not.
+     */
+    public boolean getPanelExpanded() {
+        return mCurrentState.panelExpanded;
+    }
+
     public void setStateListener(OtherwisedCollapsedListener listener) {
         mListener = listener;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 06fc745..9923ec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -48,10 +48,12 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ImageView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.system.NavigationBarCompat;
@@ -73,6 +75,7 @@
     private final KeyButtonRipple mRipple;
     private final OverviewProxyService mOverviewProxyService;
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final InputManager mInputManager;
 
     private final Runnable mCheckLongPress = new Runnable() {
         public void run() {
@@ -96,6 +99,11 @@
     }
 
     public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, InputManager.getInstance());
+    }
+
+    @VisibleForTesting
+    public KeyButtonView(Context context, AttributeSet attrs, int defStyle, InputManager manager) {
         super(context, attrs);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
@@ -117,6 +125,7 @@
 
         mRipple = new KeyButtonRipple(context, this);
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+        mInputManager = manager;
         setBackground(mRipple);
         forceHasOverlappingRendering(false);
     }
@@ -318,16 +327,23 @@
                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                 InputDevice.SOURCE_KEYBOARD);
-        //Make KeyEvent work on multi-display environment
-        if (getDisplay() != null) {
-            final int displayId = getDisplay().getDisplayId();
 
-            if (displayId != INVALID_DISPLAY) {
-                ev.setDisplayId(displayId);
-            }
+        int displayId = INVALID_DISPLAY;
+
+        // Make KeyEvent work on multi-display environment
+        if (getDisplay() != null) {
+            displayId = getDisplay().getDisplayId();
         }
-        InputManager.getInstance().injectInputEvent(ev,
-                InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+        // Bubble controller will give us a valid display id if it should get the back event
+        BubbleController bubbleController = Dependency.get(BubbleController.class);
+        int bubbleDisplayId = bubbleController.getExpandedDisplayId(mContext);
+        if (mCode == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
+            displayId = bubbleDisplayId;
+        }
+        if (displayId != INVALID_DISPLAY) {
+            ev.setDisplayId(displayId);
+        }
+        mInputManager.injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
index 39bdf20..d16dc16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
@@ -18,8 +18,16 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_FLAGS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_NAV_ACTION;
 
-import static org.mockito.ArgumentMatchers.argThat;
+import static junit.framework.Assert.assertEquals;
 
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.input.InputManager;
 import android.metrics.LogMaker;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -31,12 +39,15 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
+import org.mockito.Captor;
+import org.mockito.MockitoAnnotations;
 
 import java.util.Objects;
 
@@ -47,12 +58,19 @@
 
     private KeyButtonView mKeyButtonView;
     private MetricsLogger mMetricsLogger;
+    private BubbleController mBubbleController;
+    private InputManager mInputManager = mock(InputManager.class);
+    @Captor
+    private ArgumentCaptor<KeyEvent> mInputEventCaptor;
 
     @Before
     public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
         mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
-        TestableLooper.get(this).runWithLooper(() ->
-                mKeyButtonView = new KeyButtonView(mContext, null));
+        mBubbleController = mDependency.injectMockDependency(BubbleController.class);
+        TestableLooper.get(this).runWithLooper(() -> {
+            mKeyButtonView = new KeyButtonView(mContext, null, 0, mInputManager);
+        });
     }
 
     @Test
@@ -63,7 +81,7 @@
         mKeyButtonView.setCode(code);
         mKeyButtonView.sendEvent(action, flags);
 
-        Mockito.verify(mMetricsLogger).write(argThat(new ArgumentMatcher<LogMaker>() {
+        verify(mMetricsLogger).write(argThat(new ArgumentMatcher<LogMaker>() {
             public String mReason;
 
             @Override
@@ -91,4 +109,18 @@
         }));
     }
 
+    @Test
+    public void testBubbleEvents_bubbleExpanded() {
+        when(mBubbleController.getExpandedDisplayId(mContext)).thenReturn(3);
+
+        int action = KeyEvent.ACTION_DOWN;
+        int flags = 0;
+        int code = KeyEvent.KEYCODE_BACK;
+        mKeyButtonView.setCode(code);
+        mKeyButtonView.sendEvent(action, flags);
+
+        verify(mInputManager, times(1)).injectInputEvent(mInputEventCaptor.capture(),
+                anyInt());
+        assertEquals(3, mInputEventCaptor.getValue().getDisplayId());
+    }
 }
\ No newline at end of file