Workaround for screenshot IME focusability

- Include back gestural insets into the touchable region for the
  screenshot window, and disable focusability when we receive an
  outside touch.  Also need to disallow screenshot windows from
  IME target calculations to prevent the IME from being made
  relative to the screenshot window.  Disabling focusability also
  means that apps will receive back once the user starts interacting
  outside the screenshot window.

Bug: 159460485
Bug: 151179149
Test: In 3button/gestural, take screenshot and check that back still
      dismisses screenshot.  Take another screenshot and tap on app
      that opens an IME and check that it opens the IME.  Once the
      user taps outside, ensure back goes to the app and not the
      screenshot window.

Change-Id: I1d2100d0640fec2efc36ac8afbf8ffaf900f3bd6
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 8e878dd..d6e1a16 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -18,6 +18,7 @@
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
@@ -72,6 +73,7 @@
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
@@ -87,6 +89,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.ArrayList;
@@ -220,6 +223,10 @@
 
     private MediaActionSound mCameraSound;
 
+    private int mNavMode;
+    private int mLeftInset;
+    private int mRightInset;
+
     // standard material ease
     private final Interpolator mFastOutSlowIn;
 
@@ -301,6 +308,15 @@
         mDismissButton.getBoundsOnScreen(dismissRect);
         touchRegion.op(dismissRect, Region.Op.UNION);
 
+        if (QuickStepContract.isGesturalMode(mNavMode)) {
+            // Receive touches in gesture insets such that they don't cause TOUCH_OUTSIDE
+            Rect inset = new Rect(0, 0, mLeftInset, mDisplayMetrics.heightPixels);
+            touchRegion.op(inset, Region.Op.UNION);
+            inset.set(mDisplayMetrics.widthPixels - mRightInset, 0, mDisplayMetrics.widthPixels,
+                    mDisplayMetrics.heightPixels);
+            touchRegion.op(inset, Region.Op.UNION);
+        }
+
         inoutInfo.touchableRegion.set(touchRegion);
     }
 
@@ -356,6 +372,9 @@
         if (needsUpdate) {
             reloadAssets();
         }
+
+        mNavMode = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_navBarInteractionMode);
     }
 
     /**
@@ -370,6 +389,25 @@
 
         // Inflate the screenshot layout
         mScreenshotLayout = LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
+        // TODO(159460485): Remove this when focus is handled properly in the system
+        mScreenshotLayout.setOnTouchListener((v, event) -> {
+            if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
+                // Once the user touches outside, stop listening for input
+                setWindowFocusable(false);
+            }
+            return false;
+        });
+        mScreenshotLayout.setOnApplyWindowInsetsListener((v, insets) -> {
+            if (QuickStepContract.isGesturalMode(mNavMode)) {
+                Insets gestureInsets = insets.getInsets(
+                        WindowInsets.Type.systemGestures());
+                mLeftInset = gestureInsets.left;
+                mRightInset = gestureInsets.right;
+            } else {
+                mLeftInset = mRightInset = 0;
+            }
+            return mScreenshotLayout.onApplyWindowInsets(insets);
+        });
         mScreenshotLayout.setOnKeyListener(new View.OnKeyListener() {
             @Override
             public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -432,6 +470,21 @@
         }
     }
 
+    /**
+     * Updates the window focusability.  If the window is already showing, then it updates the
+     * window immediately, otherwise the layout params will be applied when the window is next
+     * shown.
+     */
+    private void setWindowFocusable(boolean focusable) {
+        if (focusable) {
+            mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
+        } else {
+            mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
+        }
+        if (mScreenshotLayout.isAttachedToWindow()) {
+            mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
+        }
+    }
 
     /**
      * Creates a new worker thread and saves the screenshot to the media store.
@@ -500,6 +553,10 @@
         if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
             mDismissAnimation.cancel();
         }
+
+        // The window is focusable by default
+        setWindowFocusable(true);
+
         // Start the post-screenshot animation
         startAnimation(finisher, screenRect, screenInsets, showFlash);
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f1acee5..d4d2f4d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -81,6 +81,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
@@ -2372,6 +2373,11 @@
             return false;
         }
 
+        if (mAttrs.type == TYPE_SCREENSHOT) {
+            // Disallow screenshot windows from being IME targets
+            return false;
+        }
+
         final boolean windowsAreFocusable = mActivityRecord == null || mActivityRecord.windowsAreFocusable();
         if (!windowsAreFocusable) {
             // This window can't be an IME target if the app's windows should not be focusable.