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

Commit 59ccaf0c authored by Iago Mendes's avatar Iago Mendes Committed by Android (Google) Code Review
Browse files

Merge "Listen to rotary input events and report them to FocusEventDebugView." into main

parents a296e3d5 f09f083e
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -5627,13 +5627,21 @@ public final class Settings {
        public static final String SHOW_TOUCHES = "show_touches";
        /**
         * Show key presses and other events dispatched to focused windows on the screen.
         * Show key presses dispatched to focused windows on the screen.
         * 0 = no
         * 1 = yes
         * @hide
         */
        public static final String SHOW_KEY_PRESSES = "show_key_presses";
        /**
         * Show rotary input dispatched to focused windows on the screen.
         * 0 = no
         * 1 = yes
         * @hide
         */
        public static final String SHOW_ROTARY_INPUT = "show_rotary_input";
        /**
         * Log raw orientation data from
         * {@link com.android.server.policy.WindowOrientationListener} for use with the
+1 −0
Original line number Diff line number Diff line
@@ -184,6 +184,7 @@ public class SystemSettingsValidators {
        VALIDATORS.put(System.POINTER_LOCATION, BOOLEAN_VALIDATOR);
        VALIDATORS.put(System.SHOW_TOUCHES, BOOLEAN_VALIDATOR);
        VALIDATORS.put(System.SHOW_KEY_PRESSES, BOOLEAN_VALIDATOR);
        VALIDATORS.put(System.SHOW_ROTARY_INPUT, BOOLEAN_VALIDATOR);
        VALIDATORS.put(System.WINDOW_ORIENTATION_LISTENER_LOG, BOOLEAN_VALIDATOR);
        VALIDATORS.put(System.LOCKSCREEN_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
        VALIDATORS.put(System.LOCKSCREEN_DISABLED, BOOLEAN_VALIDATOR);
+1 −0
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ public class SettingsBackupTest {
                    Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
                    Settings.System.SHOW_TOUCHES,
                    Settings.System.SHOW_KEY_PRESSES,
                    Settings.System.SHOW_ROTARY_INPUT,
                    Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
                    Settings.System.SIP_ALWAYS, // value, not a setting
                    Settings.System.SYSTEM_LOCALES, // bug?
+48 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.input;

import android.view.Display;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.MotionEvent;

import com.android.server.UiThread;

/**
 * Receives input events before they are dispatched and reports them to FocusEventDebugView.
 */
class FocusEventDebugGlobalMonitor extends InputEventReceiver {
    private final FocusEventDebugView mDebugView;

    FocusEventDebugGlobalMonitor(FocusEventDebugView debugView, InputManagerService service) {
        super(service.monitorInput("FocusEventDebugGlobalMonitor", Display.DEFAULT_DISPLAY),
            UiThread.getHandler().getLooper());
        mDebugView = debugView;
    }

    @Override
    public void onInputEvent(InputEvent event) {
        try {
            if (event instanceof MotionEvent) {
                mDebugView.reportMotionEvent((MotionEvent) event);
            }
        } finally {
            finishInputEvent(event, false);
        }
    }
}
+104 −25
Original line number Diff line number Diff line
@@ -22,17 +22,20 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import android.animation.LayoutTransition;
import android.annotation.AnyThread;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Typeface;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.InputEvent;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RoundedCorner;
import android.view.View;
import android.view.WindowInsets;
@@ -64,42 +67,31 @@ class FocusEventDebugView extends LinearLayout {
    private static final int KEY_VIEW_MIN_WIDTH_DP = 32;
    private static final int KEY_VIEW_TEXT_SIZE_SP = 12;

    private final InputManagerService mService;
    private final int mOuterPadding;

    // Tracks all keys that are currently pressed/down.
    private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView>
            mPressedKeys = new HashMap<>();

    private final PressedKeyContainer mPressedKeyContainer;
    private final PressedKeyContainer mPressedModifierContainer;
    @Nullable
    private FocusEventDebugGlobalMonitor mFocusEventDebugGlobalMonitor;
    @Nullable
    private PressedKeyContainer mPressedKeyContainer;
    @Nullable
    private PressedKeyContainer mPressedModifierContainer;

    FocusEventDebugView(Context c) {
    FocusEventDebugView(Context c, InputManagerService service) {
        super(c);
        setFocusableInTouchMode(true);

        mService = service;
        final var dm = mContext.getResources().getDisplayMetrics();
        mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, dm);

        setOrientation(HORIZONTAL);
        setLayoutDirection(LAYOUT_DIRECTION_RTL);
        setGravity(Gravity.START | Gravity.BOTTOM);

        mPressedKeyContainer = new PressedKeyContainer(mContext);
        mPressedKeyContainer.setOrientation(HORIZONTAL);
        mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
        mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
        final var scroller = new HorizontalScrollView(mContext);
        scroller.addView(mPressedKeyContainer);
        scroller.setHorizontalScrollBarEnabled(false);
        scroller.addOnLayoutChangeListener(
                (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
        scroller.setHorizontalFadingEdgeEnabled(true);
        addView(scroller, new LayoutParams(0, WRAP_CONTENT, 1));

        mPressedModifierContainer = new PressedKeyContainer(mContext);
        mPressedModifierContainer.setOrientation(VERTICAL);
        mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
        addView(mPressedModifierContainer, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
    }

    @Override
@@ -135,17 +127,82 @@ class FocusEventDebugView extends LinearLayout {
        return super.dispatchKeyEvent(event);
    }

    /** Report an input event to the debug view. */
    @AnyThread
    public void reportEvent(InputEvent event) {
        if (!(event instanceof KeyEvent)) {
            // TODO: Support non-pointer MotionEvents.
    public void updateShowKeyPresses(boolean enabled) {
        post(() -> handleUpdateShowKeyPresses(enabled));
    }

    @AnyThread
    public void updateShowRotaryInput(boolean enabled) {
        post(() -> handleUpdateShowRotaryInput(enabled));
    }

    private void handleUpdateShowKeyPresses(boolean enabled) {
        if (enabled == showKeyPresses()) {
            return;
        }

        if (!enabled) {
            removeView(mPressedKeyContainer);
            mPressedKeyContainer = null;
            removeView(mPressedModifierContainer);
            mPressedModifierContainer = null;
            return;
        }

        mPressedKeyContainer = new PressedKeyContainer(mContext);
        mPressedKeyContainer.setOrientation(HORIZONTAL);
        mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
        mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
        final var scroller = new HorizontalScrollView(mContext);
        scroller.addView(mPressedKeyContainer);
        scroller.setHorizontalScrollBarEnabled(false);
        scroller.addOnLayoutChangeListener(
                (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
        scroller.setHorizontalFadingEdgeEnabled(true);
        addView(scroller, new LayoutParams(0, WRAP_CONTENT, 1));

        mPressedModifierContainer = new PressedKeyContainer(mContext);
        mPressedModifierContainer.setOrientation(VERTICAL);
        mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
        addView(mPressedModifierContainer, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
    }

    private void handleUpdateShowRotaryInput(boolean enabled) {
        if (enabled == showRotaryInput()) {
            return;
        }

        if (!enabled) {
            mFocusEventDebugGlobalMonitor.dispose();
            mFocusEventDebugGlobalMonitor = null;
            return;
        }

        mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
    }

    /** Report a key event to the debug view. */
    @AnyThread
    public void reportKeyEvent(KeyEvent event) {
        post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event)));
    }

    /** Report a motion event to the debug view. */
    @AnyThread
    public void reportMotionEvent(MotionEvent event) {
        if (event.getSource() != InputDevice.SOURCE_ROTARY_ENCODER) {
            return;
        }

        post(() -> handleRotaryInput(MotionEvent.obtain((MotionEvent) event)));
    }

    private void handleKeyEvent(KeyEvent keyEvent) {
        if (!showKeyPresses()) {
            return;
        }

        final var identifier = new Pair<>(keyEvent.getDeviceId(), keyEvent.getScanCode());
        final var container = KeyEvent.isModifierKey(keyEvent.getKeyCode())
                ? mPressedModifierContainer
@@ -185,6 +242,18 @@ class FocusEventDebugView extends LinearLayout {
        keyEvent.recycle();
    }

    private void handleRotaryInput(MotionEvent motionEvent) {
        if (!showRotaryInput()) {
            return;
        }

        float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
        // TODO(b/286086154): replace log with visualization.
        Log.d(TAG, "ROTARY INPUT: " + String.valueOf(scrollAxisValue));

        motionEvent.recycle();
    }

    private static String getLabel(KeyEvent event) {
        switch (event.getKeyCode()) {
            case KeyEvent.KEYCODE_SPACE:
@@ -232,6 +301,16 @@ class FocusEventDebugView extends LinearLayout {
        return label;
    }

    /** Determine whether to show key presses by checking one of the key-related objects. */
    private boolean showKeyPresses() {
        return mPressedKeyContainer != null;
    }

    /** Determine whether to show rotary input by checking one of the rotary-related objects. */
    private boolean showRotaryInput() {
        return mFocusEventDebugGlobalMonitor != null;
    }

    private static class PressedKeyView extends TextView {

        private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{
Loading