From 8ae977decd6c4b97e23014696b792357cc654ac1 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 25 Jun 2020 13:05:19 -0700 Subject: [PATCH] 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 --- .../systemui/screenshot/GlobalScreenshot.java | 57 +++++++++++++++++++ .../com/android/server/wm/WindowState.java | 6 ++ 2 files changed, 63 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 8e878ddc6da1a..d6e1a16bc69e1 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 @@ package com.android.systemui.screenshot; 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.View; 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.internal.logging.UiEventLogger; 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 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private MediaActionSound mCameraSound; + private int mNavMode; + private int mLeftInset; + private int mRightInset; + // standard material ease private final Interpolator mFastOutSlowIn; @@ -301,6 +308,15 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset 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 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset if (needsUpdate) { reloadAssets(); } + + mNavMode = mContext.getResources().getInteger( + com.android.internal.R.integer.config_navBarInteractionMode); } /** @@ -370,6 +389,25 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset // 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 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset } } + /** + * 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 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset 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 f1acee5031d87..d4d2f4d7a4920 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_POINTER; 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 @@ class WindowState extends WindowContainer implements WindowManagerP 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.