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

Commit b0f55833 authored by Abdelrahman Awadalla's avatar Abdelrahman Awadalla Committed by Android (Google) Code Review
Browse files

Merge "Guarding the touchpad visualiser window with the flag and settings" into main

parents 55217ac5 b204fdee
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -597,9 +597,6 @@ public class InputManagerService extends IInputManager.Stub
        mKeyRemapper.systemRunning();
        mPointerIconCache.systemRunning();
        mKeyboardGlyphManager.systemRunning();
        if (mTouchpadDebugViewController != null) {
            mTouchpadDebugViewController.systemRunning();
        }
    }

    private void reloadDeviceAliases() {
@@ -3340,6 +3337,13 @@ public class InputManagerService extends IInputManager.Stub
        }
    }

    void updateTouchpadVisualizerEnabled(boolean enabled) {
        mNative.setShouldNotifyTouchpadHardwareState(enabled);
        if (mTouchpadDebugViewController != null) {
            mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(enabled);
        }
    }

    void updatePointerLocationEnabled(boolean enabled) {
        mWindowManagerCallbacks.notifyPointerLocationChanged(enabled);
    }
+1 −1
Original line number Diff line number Diff line
@@ -180,7 +180,7 @@ class InputSettingsObserver extends ContentObserver {
    }

    private void updateTouchpadHardwareStateNotificationsEnabled() {
        mNative.setShouldNotifyTouchpadHardwareState(InputSettings.useTouchpadVisualizer(mContext));
        mService.updateTouchpadVisualizerEnabled(InputSettings.useTouchpadVisualizer(mContext));
    }

    private void updateTouchpadRightClickZoneEnabled() {
+45 −35
Original line number Diff line number Diff line
@@ -18,12 +18,10 @@ package com.android.server.input.debug;

import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Slog;
import android.view.Display;
import android.view.InputDevice;
import android.view.WindowManager;

@@ -32,7 +30,7 @@ import com.android.server.input.TouchpadHardwareProperties;

import java.util.Objects;

public class TouchpadDebugViewController {
public class TouchpadDebugViewController implements InputManager.InputDeviceListener {

    private static final String TAG = "TouchpadDebugView";

@@ -43,28 +41,16 @@ public class TouchpadDebugViewController {
    private TouchpadDebugView mTouchpadDebugView;

    private final InputManagerService mInputManagerService;
    private boolean mTouchpadVisualizerEnabled = false;

    public TouchpadDebugViewController(Context context, Looper looper,
            InputManagerService inputManagerService) {
        final DisplayManager displayManager = Objects.requireNonNull(
                context.getSystemService(DisplayManager.class));
        final Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
        mContext = context.createDisplayContext(defaultDisplay);
        //TODO(b/363979581): Handle multi-display scenarios
        mContext = context;
        mHandler = new Handler(looper);
        mInputManagerService = inputManagerService;
    }

    public void systemRunning() {
        final InputManager inputManager = Objects.requireNonNull(
                mContext.getSystemService(InputManager.class));
        inputManager.registerInputDeviceListener(mInputDeviceListener, mHandler);
        for (int deviceId : inputManager.getInputDeviceIds()) {
            mInputDeviceListener.onInputDeviceAdded(deviceId);
        }
    }

    private final InputManager.InputDeviceListener mInputDeviceListener =
            new InputManager.InputDeviceListener() {
    @Override
    public void onInputDeviceAdded(int deviceId) {
        final InputManager inputManager = Objects.requireNonNull(
@@ -72,7 +58,8 @@ public class TouchpadDebugViewController {
        InputDevice inputDevice = inputManager.getInputDevice(deviceId);

        if (Objects.requireNonNull(inputDevice).supportsSource(
                            InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)) {
                InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)
                && mTouchpadVisualizerEnabled) {
            showDebugView(deviceId);
        }
    }
@@ -85,7 +72,30 @@ public class TouchpadDebugViewController {
    @Override
    public void onInputDeviceChanged(int deviceId) {
    }
            };

    /**
     * Notify the controller that the touchpad visualizer setting value has changed.
     * This must be called from the same looper thread as {@code mHandler}.
     */
    public void updateTouchpadVisualizerEnabled(boolean touchpadVisualizerEnabled) {
        if (mTouchpadVisualizerEnabled == touchpadVisualizerEnabled) {
            return;
        }
        mTouchpadVisualizerEnabled = touchpadVisualizerEnabled;
        final InputManager inputManager = Objects.requireNonNull(
                mContext.getSystemService(InputManager.class));
        if (touchpadVisualizerEnabled) {
            inputManager.registerInputDeviceListener(this, mHandler);
            for (int deviceId : inputManager.getInputDeviceIds()) {
                onInputDeviceAdded(deviceId);
            }
        } else {
            if (mTouchpadDebugView != null) {
                hideDebugView(mTouchpadDebugView.getTouchpadId());
            }
            inputManager.unregisterInputDeviceListener(this);
        }
    }

    private void showDebugView(int touchpadId) {
        if (mTouchpadDebugView != null) {
+215 −0
Original line number Diff line number Diff line
/*
 * Copyright 2024 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.debug;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.InputDevice;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;

import androidx.test.platform.app.InstrumentationRegistry;

import com.android.server.input.InputManagerService;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

/**
 * Build/Install/Run:
 * atest TouchpadDebugViewControllerTests
 */

@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class TouchpadDebugViewControllerTests {
    private static final int DEVICE_ID = 1000;
    private static final String TAG = "TouchpadDebugViewController";

    @Rule
    public final MockitoRule mockito = MockitoJUnit.rule();

    private Context mContext;
    private TouchpadDebugViewController mTouchpadDebugViewController;
    @Mock
    private InputManager mInputManagerMock;
    @Mock
    private InputManagerService mInputManagerServiceMock;
    @Mock
    private WindowManager mWindowManagerMock;
    private TestableLooper mTestableLooper;

    @Before
    public void setup() throws Exception {
        mContext = InstrumentationRegistry.getInstrumentation().getContext();
        TestableContext mTestableContext = new TestableContext(mContext);
        mTestableContext.addMockSystemService(WindowManager.class, mWindowManagerMock);

        Rect bounds = new Rect(0, 0, 2560, 1600);
        WindowMetrics metrics = new WindowMetrics(bounds, new WindowInsets(bounds), 1.0f);

        when(mWindowManagerMock.getCurrentWindowMetrics()).thenReturn(metrics);

        unMockTouchpad();

        mTestableLooper = TestableLooper.get(this);

        mTestableContext.addMockSystemService(InputManager.class, mInputManagerMock);

        mTouchpadDebugViewController = new TouchpadDebugViewController(mTestableContext,
                mTestableLooper.getLooper(), mInputManagerServiceMock);
    }

    private InputDevice createTouchpadInputDevice(int id) {
        return new InputDevice.Builder()
                .setId(id)
                .setSources(InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)
                .setName("Test Device " + id)
                .build();
    }

    private void mockTouchpad() {
        when(mInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
        when(mInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
                createTouchpadInputDevice(DEVICE_ID));
    }

    private void unMockTouchpad() {
        when(mInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{});
        when(mInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(null);
    }

    @Test
    public void touchpadConnectedWhileSettingDisabled() throws Exception {
        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);

        mockTouchpad();
        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);

        verify(mWindowManagerMock, never()).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());
    }

    @Test
    public void settingEnabledWhileNoTouchpadConnected() throws Exception {
        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);

        verify(mWindowManagerMock, never()).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());
    }

    @Test
    public void touchpadConnectedWhileSettingEnabled() throws Exception {
        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);

        mockTouchpad();
        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);

        verify(mWindowManagerMock, times(1)).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());
    }

    @Test
    public void touchpadConnectedWhileSettingEnabledThenDisabled() throws Exception {
        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);

        mockTouchpad();
        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);

        verify(mWindowManagerMock, times(1)).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());

        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);

        verify(mWindowManagerMock, times(1)).addView(any(), any());
        verify(mWindowManagerMock, times(1)).removeView(any());
    }

    @Test
    public void touchpadConnectedWhileSettingDisabledThenEnabled() throws Exception {
        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);

        mockTouchpad();
        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);

        verify(mWindowManagerMock, never()).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());

        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);

        verify(mWindowManagerMock, times(1)).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());
    }

    @Test
    public void touchpadConnectedWhileSettingDisabledThenTouchpadDisconnected() throws Exception {
        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);

        mockTouchpad();
        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);

        verify(mWindowManagerMock, never()).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());

        unMockTouchpad();
        mTouchpadDebugViewController.onInputDeviceRemoved(DEVICE_ID);

        verify(mWindowManagerMock, never()).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());
    }

    @Test
    public void touchpadConnectedWhileSettingEnabledThenTouchpadDisconnectedThenSettingDisabled()
            throws Exception {
        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);

        mockTouchpad();
        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);

        verify(mWindowManagerMock, times(1)).addView(any(), any());
        verify(mWindowManagerMock, never()).removeView(any());

        unMockTouchpad();
        mTouchpadDebugViewController.onInputDeviceRemoved(DEVICE_ID);

        verify(mWindowManagerMock, times(1)).addView(any(), any());
        verify(mWindowManagerMock, times(1)).removeView(any());

        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);

        verify(mWindowManagerMock, times(1)).addView(any(), any());
        verify(mWindowManagerMock, times(1)).removeView(any());
    }
}