Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d656c9f authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

Shift screenshot shortcuts to KeyGestureController

This also allows the displayId to be passed into the Screenshot request

Test: atest WmTests
Test: InputTests
Flag: EXEMPT refactor
Bug: 358569822
Change-Id: I6d94adb1d5847d98baa553424526eb4dc2648ea0
parent e3c98685
Loading
Loading
Loading
Loading
+60 −1
Original line number Diff line number Diff line
@@ -20,9 +20,12 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.os.UserManager.isVisibleBackgroundUsersEnabled;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE;

import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;

import android.annotation.BinderThread;
import android.annotation.MainThread;
@@ -52,6 +55,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -63,12 +67,15 @@ import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.ViewConfiguration;
import android.view.WindowManager;

import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IShortcutService;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.ScreenshotRequest;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;

@@ -106,6 +113,7 @@ final class KeyGestureController {
    private static final int MSG_PERSIST_CUSTOM_GESTURES = 2;
    private static final int MSG_LOAD_CUSTOM_GESTURES = 3;
    private static final int MSG_ACCESSIBILITY_SHORTCUT = 4;
    private static final int MSG_SCREENSHOT_SHORTCUT = 5;

    // must match: config_settingsKeyBehavior in config.xml
    private static final int SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0;
@@ -123,12 +131,17 @@ final class KeyGestureController {
    static final int POWER_VOLUME_UP_BEHAVIOR_MUTE = 1;
    static final int POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS = 2;

    // Screenshot trigger states
    // Increase the chord delay when taking a screenshot from the keyguard
    private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f;

    private final Context mContext;
    private InputManagerService.WindowManagerCallbacks mWindowManagerCallbacks;
    private final Handler mHandler;
    private final Handler mIoHandler;
    private final int mSystemPid;
    private final KeyCombinationManager mKeyCombinationManager;
    private final ScreenshotHelper mScreenshotHelper;
    private final SettingsObserver mSettingsObserver;
    private final AppLaunchShortcutManager mAppLaunchShortcutManager;
    @VisibleForTesting
@@ -196,6 +209,7 @@ final class KeyGestureController {
        mIoHandler = new Handler(ioLooper, this::handleIoMessage);
        mSystemPid = Process.myPid();
        mKeyCombinationManager = new KeyCombinationManager(mHandler);
        mScreenshotHelper = injector.getScreenshotHelper(mContext);
        mSettingsObserver = new SettingsObserver(mHandler);
        mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext);
        mInputGestureManager = new InputGestureManager(mContext);
@@ -454,7 +468,9 @@ final class KeyGestureController {
        InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
        im.registerKeyGestureEventHandler(
                List.of(KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT),
                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
                        KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
                        KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD),
                new LocalKeyGestureEventHandler());
    }

@@ -1083,6 +1099,10 @@ final class KeyGestureController {
            case MSG_ACCESSIBILITY_SHORTCUT:
                mAccessibilityShortcutController.performAccessibilityShortcut();
                break;
            case MSG_SCREENSHOT_SHORTCUT:
                takeScreenshot(msg.arg1, msg.arg2);
                break;

        }
        return true;
    }
@@ -1454,6 +1474,25 @@ final class KeyGestureController {
        }
    }

    private void takeScreenshot(int source, int displayId) {
        ScreenshotRequest request =
                new ScreenshotRequest.Builder(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, source)
                        .setDisplayId(displayId)
                        .build();
        mScreenshotHelper.takeScreenshot(request, mHandler, null /* completionConsumer */);
    }

    private long getScreenshotChordLongPressDelay() {
        long delayMs = DeviceConfig.getLong(
                DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY,
                ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
        if (mWindowManagerCallbacks.isKeyguardLocked(DEFAULT_DISPLAY)) {
            // Double the time it takes to take a screenshot from the keyguard
            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs);
        }
        return delayMs;
    }

    public void dump(IndentingPrintWriter ipw) {
        ipw.println("KeyGestureController:");
        ipw.increaseIndent();
@@ -1510,6 +1549,10 @@ final class KeyGestureController {
                Handler handler) {
            return new AccessibilityShortcutController(context, handler, UserHandle.USER_SYSTEM);
        }

        ScreenshotHelper getScreenshotHelper(Context context) {
            return new ScreenshotHelper(context);
        }
    }

    private class LocalKeyGestureEventHandler implements InputManager.KeyGestureEventHandler {
@@ -1534,6 +1577,22 @@ final class KeyGestureController {
                        mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
                    }
                    break;
                case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
                    if (complete) {
                        mHandler.sendMessage(mHandler.obtainMessage(MSG_SCREENSHOT_SHORTCUT,
                                SCREENSHOT_KEY_OTHER, event.getDisplayId()));
                    }
                    break;
                case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
                    mHandler.removeMessages(MSG_SCREENSHOT_SHORTCUT);
                    if (start) {
                        mHandler.sendMessageDelayed(
                                mHandler.obtainMessage(MSG_SCREENSHOT_SHORTCUT,
                                        SCREENSHOT_KEY_CHORD,
                                        event.getDisplayId()),
                                getScreenshotChordLongPressDelay());
                    }
                    break;
                default:
                    Log.w(TAG, "Received a key gesture " + event
                            + " that was not registered by this handler");
+0 −58
Original line number Diff line number Diff line
@@ -74,16 +74,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;

import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -175,7 +171,6 @@ import android.os.Trace;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.DeviceConfig;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.Secure;
@@ -376,7 +371,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
    static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
    static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
    static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
    static public final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";

    public static final String TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD = "waitForAllWindowsDrawn";
@@ -667,10 +661,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    // Whether to go to sleep entering theater mode from power button
    private boolean mGoToSleepOnButtonPressTheaterMode;

    // Screenshot trigger states
    // Increase the chord delay when taking a screenshot from the keyguard
    private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f;

    // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
    int mRingerToggleChord = VOLUME_HUSH_OFF;

@@ -722,7 +712,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    private static final int MSG_HIDE_BOOT_MESSAGE = 11;
    private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12;
    private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15;
    private static final int MSG_SCREENSHOT_CHORD = 16;
    private static final int MSG_BUGREPORT_TV = 18;
    private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 20;
    private static final int MSG_SYSTEM_KEY_PRESS = 21;
@@ -804,9 +793,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                case MSG_RINGER_TOGGLE_CHORD:
                    handleRingerChordGesture();
                    break;
                case MSG_SCREENSHOT_CHORD:
                    handleScreenShot(msg.arg1);
                    break;
                case MSG_SWITCH_KEYBOARD_LAYOUT:
                    SwitchKeyboardLayoutMessageObject object =
                            (SwitchKeyboardLayoutMessageObject) msg.obj;
@@ -1774,40 +1760,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                || defaultShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
    }

    private void interceptScreenshotChord(int source, long pressDelay) {
        mHandler.removeMessages(MSG_SCREENSHOT_CHORD);
        // arg2 is unused, but necessary to insure we call the correct method signature
        // since the screenshot source is read from message.arg1
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCREENSHOT_CHORD, source, 0),
                pressDelay);
    }

    private void interceptRingerToggleChord() {
        mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD),
                getRingerToggleChordDelay());
    }

    private long getScreenshotChordLongPressDelay() {
        long delayMs = DeviceConfig.getLong(
                DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY,
                ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
        if (mKeyguardDelegate.isShowing()) {
            // Double the time it takes to take a screenshot from the keyguard
            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs);
        }
        return delayMs;
    }

    private long getRingerToggleChordDelay() {
        // Always timeout like a tap
        return ViewConfiguration.getTapTimeout();
    }

    private void cancelPendingScreenshotChordAction() {
        mHandler.removeMessages(MSG_SCREENSHOT_CHORD);
    }

    private void cancelPendingRingerToggleChordAction() {
        mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
    }
@@ -1822,10 +1785,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    };

    private void handleScreenShot(@WindowManager.ScreenshotSource int source) {
        mDefaultDisplayPolicy.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, source);
    }

    @Override
    public void showGlobalActions() {
        mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
@@ -3381,7 +3340,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
                KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
                KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
                KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
                KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
                KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
@@ -3395,7 +3353,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
                KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
                KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD,
                KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS,
                KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT
@@ -3479,11 +3436,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    toggleNotificationPanel();
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
                if (complete) {
                    interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
                if (complete && mEnableBugReportKeyboardShortcut) {
                    try {
@@ -3568,16 +3520,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    sendSwitchKeyboardLayout(displayId, focusedToken, direction);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
                if (start) {
                    // Screenshot chord is pressed: Wait for long press delay before taking
                    // screenshot
                    interceptScreenshotChord(SCREENSHOT_KEY_CHORD,
                            getScreenshotChordLongPressDelay());
                } else {
                    cancelPendingScreenshotChordAction();
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
                if (start) {
                    interceptRingerToggleChord();
+0 −23
Original line number Diff line number Diff line
@@ -133,8 +133,6 @@ import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.ScreenshotRequest;
import com.android.internal.util.function.TriFunction;
import com.android.internal.view.AppearanceRegion;
import com.android.internal.widget.PointerLocationView;
@@ -198,7 +196,6 @@ public class DisplayPolicy {
    private final boolean mCarDockEnablesAccelerometer;
    private final boolean mDeskDockEnablesAccelerometer;
    private final AccessibilityManager mAccessibilityManager;
    private final ScreenshotHelper mScreenshotHelper;

    private final Object mServiceAcquireLock = new Object();
    private long mPanicTime;
@@ -658,10 +655,6 @@ public class DisplayPolicy {
        };
        displayContent.mTransitionController.registerLegacyListener(mAppTransitionListener);

        // TODO: Make it can take screenshot on external display
        mScreenshotHelper = displayContent.isDefaultDisplay
                ? new ScreenshotHelper(mContext) : null;

        if (mDisplayContent.isDefaultDisplay) {
            mHasStatusBar = true;
            mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);
@@ -3050,22 +3043,6 @@ public class DisplayPolicy {
        mService.mAtmService.setProcessAnimatingWhileDozing(w != null ? w.getProcess() : null);
    }

    /**
     * Request a screenshot be taken.
     *
     * @param screenshotType The type of screenshot, for example either
     *                       {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or
     *                       {@link WindowManager#TAKE_SCREENSHOT_PROVIDED_IMAGE}
     * @param source Where the screenshot originated from (see WindowManager.ScreenshotSource)
     */
    public void takeScreenshot(int screenshotType, int source) {
        if (mScreenshotHelper != null) {
            ScreenshotRequest request =
                    new ScreenshotRequest.Builder(screenshotType, source).build();
            mScreenshotHelper.takeScreenshot(request, mHandler, null /* completionConsumer */);
        }
    }

    RefreshRatePolicy getRefreshRatePolicy() {
        return mRefreshRatePolicy;
    }
+0 −21
Original line number Diff line number Diff line
@@ -259,12 +259,6 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
        mPhoneWindowManager.assertTogglePanel();
    }

    @Test
    public void testKeyGestureScreenshot() {
        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT);
        mPhoneWindowManager.assertTakeScreenshotCalled();
    }

    @Test
    public void testKeyGestureTriggerBugReport() throws RemoteException {
        sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT);
@@ -348,21 +342,6 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
        mPhoneWindowManager.assertLaunchSearch();
    }

    @Test
    public void testKeyGestureScreenshotChord() {
        sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
        mPhoneWindowManager.moveTimeForward(500);
        sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
        mPhoneWindowManager.assertTakeScreenshotCalled();
    }

    @Test
    public void testKeyGestureScreenshotChordCancelled() {
        sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
        sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
        mPhoneWindowManager.assertTakeScreenshotNotCalled();
    }

    @Test
    public void testKeyGestureRingerToggleChord() {
        mPhoneWindowManager.overrideVolumeHushMode();
+0 −14
Original line number Diff line number Diff line
@@ -358,7 +358,6 @@ class TestPhoneWindowManager {
        doReturn(mDisplay).when(mDisplayManager).getDisplay(eq(DEFAULT_DISPLAY));
        doReturn(STATE_ON).when(mDisplay).getState();
        doReturn(true).when(mDisplayPolicy).isAwake();
        doNothing().when(mDisplayPolicy).takeScreenshot(anyInt(), anyInt());
        doReturn(mDisplayPolicy).when(mDisplayRotation).getDisplayPolicy();
        doReturn(mScreenOnListener).when(mDisplayPolicy).getScreenOnListener();
        mPhoneWindowManager.setDefaultDisplay(new WindowManagerPolicy.DisplayContentInfo() {
@@ -682,19 +681,6 @@ class TestPhoneWindowManager {
        mKeyEventPolicyFlags = flags;
    }

    /**
     * Below functions will check the policy behavior could be invoked.
     */
    void assertTakeScreenshotCalled() {
        mTestLooper.dispatchAll();
        verify(mDisplayPolicy).takeScreenshot(anyInt(), anyInt());
    }

    void assertTakeScreenshotNotCalled() {
        mTestLooper.dispatchAll();
        verify(mDisplayPolicy, never()).takeScreenshot(anyInt(), anyInt());
    }

    void assertShowGlobalActionsCalled() {
        mTestLooper.dispatchAll();
        verify(mPhoneWindowManager).showGlobalActions();
Loading