1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.quickstep.util 18 19 import android.content.Context 20 import android.hardware.input.InputManager 21 import android.view.InputDevice 22 import com.android.launcher3.util.Executors 23 import com.android.launcher3.util.IntSet 24 25 /** Utility class to maintain a list of actively connected trackpad devices */ 26 class ActiveTrackpadList(ctx: Context, private val updateCallback: Runnable) : 27 IntSet(), InputManager.InputDeviceListener { 28 29 private val inputManager = ctx.getSystemService(InputManager::class.java)!! 30 31 init { 32 inputManager.registerInputDeviceListener(this, Executors.UI_HELPER_EXECUTOR.handler) 33 inputManager.inputDeviceIds.filter(this::isTrackpadDevice).forEach(this::add) 34 } 35 onInputDeviceAddednull36 override fun onInputDeviceAdded(deviceId: Int) { 37 if (isTrackpadDevice(deviceId)) { 38 // This updates internal TIS state so it needs to also run on the main 39 // thread. 40 Executors.MAIN_EXECUTOR.execute { 41 val wasEmpty = isEmpty 42 add(deviceId) 43 if (wasEmpty) update() 44 } 45 } 46 } 47 onInputDeviceChangednull48 override fun onInputDeviceChanged(deviceId: Int) {} 49 onInputDeviceRemovednull50 override fun onInputDeviceRemoved(deviceId: Int) { 51 // This updates internal TIS state so it needs to also run on the main thread. 52 Executors.MAIN_EXECUTOR.execute { 53 remove(deviceId) 54 if (isEmpty) update() 55 } 56 } 57 updatenull58 private fun update() { 59 updateCallback.run() 60 } 61 destroynull62 fun destroy() { 63 inputManager.unregisterInputDeviceListener(this) 64 clear() 65 } 66 67 /** This is a blocking binder call that should run on a bg thread. */ isTrackpadDevicenull68 private fun isTrackpadDevice(deviceId: Int) = 69 inputManager.getInputDevice(deviceId)?.sources == 70 (InputDevice.SOURCE_MOUSE or InputDevice.SOURCE_TOUCHPAD) 71 } 72