diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java index a04ff2ea44..620e2b7cae 100644 --- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -217,7 +217,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { @Override protected boolean isLauncherInitialized() { - return super.isLauncherInitialized() && TouchInteractionService.isInitialized(); + return super.isLauncherInitialized() && SystemUiProxy.INSTANCE.get(mContext).isActive(); } private void enableBlockingTimeout( diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index cb5d289c21..3bfdc21d88 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -45,13 +45,11 @@ import android.content.IIntentSender; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Region; -import android.hardware.input.InputManager; import android.os.Bundle; import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; import android.os.Trace; -import android.util.ArraySet; import android.util.Log; import android.view.Choreographer; import android.view.InputDevice; @@ -59,7 +57,6 @@ import android.view.InputEvent; import android.view.MotionEvent; import androidx.annotation.BinderThread; -import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; @@ -98,6 +95,7 @@ import com.android.quickstep.inputconsumers.ResetGestureInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActiveGestureLog.CompoundString; import com.android.quickstep.util.ActiveGestureProtoLogProxy; +import com.android.quickstep.util.ActiveTrackpadList; import com.android.quickstep.util.ContextualSearchInvoker; import com.android.quickstep.util.ContextualSearchStateManager; import com.android.quickstep.views.RecentsViewContainer; @@ -124,7 +122,6 @@ import com.android.wm.shell.startingsurface.IStartingWindow; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; -import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; @@ -188,7 +185,6 @@ public class TouchInteractionService extends Service { tis.initInputMonitor("TISBinder#onInitialize()"); tis.preloadOverview(true /* fromInit */); })); - sIsInitialized = true; } @BinderThread @@ -504,72 +500,8 @@ public class TouchInteractionService extends Service { } } - private final InputManager.InputDeviceListener mInputDeviceListener = - new InputManager.InputDeviceListener() { - @Override - public void onInputDeviceAdded(int deviceId) { - if (isTrackpadDevice(deviceId)) { - // This updates internal TIS state so it needs to also run on the main - // thread. - MAIN_EXECUTOR.execute(() -> { - boolean wasEmpty = mTrackpadsConnected.isEmpty(); - mTrackpadsConnected.add(deviceId); - if (wasEmpty) { - update(); - } - }); - } - } - - @Override - public void onInputDeviceChanged(int deviceId) { - } - - @Override - public void onInputDeviceRemoved(int deviceId) { - // This updates internal TIS state so it needs to also run on the main - // thread. - MAIN_EXECUTOR.execute(() -> { - mTrackpadsConnected.remove(deviceId); - if (mTrackpadsConnected.isEmpty()) { - update(); - } - }); - } - - @MainThread - private void update() { - if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) { - // Don't destroy and reinitialize input monitor due to trackpad - // connecting when it's already set up. - return; - } - initInputMonitor("onTrackpadConnected()"); - } - - private boolean isTrackpadDevice(int deviceId) { - // This is a blocking binder call that should run on a bg thread. - InputDevice inputDevice = mInputManager.getInputDevice(deviceId); - if (inputDevice == null) { - return false; - } - return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE - | InputDevice.SOURCE_TOUCHPAD); - } - }; - - private static boolean sConnected = false; - private static boolean sIsInitialized = false; private RotationTouchHelper mRotationTouchHelper; - public static boolean isConnected() { - return sConnected; - } - - public static boolean isInitialized() { - return sIsInitialized; - } - private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory = this::createLauncherSwipeHandler; private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory = @@ -618,8 +550,7 @@ public class TouchInteractionService extends Service { private TaskbarManager mTaskbarManager; private Function mSwipeUpProxyProvider = i -> null; private AllAppsActionManager mAllAppsActionManager; - private InputManager mInputManager; - private final Set mTrackpadsConnected = new ArraySet<>(); + private ActiveTrackpadList mTrackpadsConnected; private NavigationMode mGestureStartNavMode = null; @@ -638,13 +569,15 @@ public class TouchInteractionService extends Service { mRotationTouchHelper = mDeviceState.getRotationTouchHelper(); mAllAppsActionManager = new AllAppsActionManager( this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent); - mInputManager = getSystemService(InputManager.class); - mInputManager.registerInputDeviceListener(mInputDeviceListener, - UI_HELPER_EXECUTOR.getHandler()); - int [] inputDevices = mInputManager.getInputDeviceIds(); - for (int inputDeviceId : inputDevices) { - mInputDeviceListener.onInputDeviceAdded(inputDeviceId); - } + mTrackpadsConnected = new ActiveTrackpadList(this, () -> { + if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) { + // Don't destroy and reinitialize input monitor due to trackpad + // connecting when it's already set up. + return; + } + initInputMonitor("onTrackpadConnected()"); + }); + mDesktopVisibilityController = new DesktopVisibilityController(this); mTaskbarManager = new TaskbarManager( this, mAllAppsActionManager, mNavCallbacks, mDesktopVisibilityController); @@ -656,8 +589,6 @@ public class TouchInteractionService extends Service { // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized. LockedUserState.get(this).runOnUserUnlocked(mUserUnlockedRunnable); mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); - sConnected = true; - ScreenOnTracker.INSTANCE.get(this).addListener(mScreenOnListener); } @@ -793,7 +724,6 @@ public class TouchInteractionService extends Service { public void onDestroy() { Log.d(TAG, "onDestroy: user=" + getUserId() + " instance=" + System.identityHashCode(this)); - sIsInitialized = false; if (LockedUserState.get(this).isUserUnlocked()) { mInputConsumer.unregisterInputConsumer(); mOverviewComponentObserver.setHomeDisabled(false); @@ -805,16 +735,13 @@ public class TouchInteractionService extends Service { mAllAppsActionManager.onDestroy(); - mInputManager.unregisterInputDeviceListener(mInputDeviceListener); - mTrackpadsConnected.clear(); - + mTrackpadsConnected.destroy(); mTaskbarManager.destroy(); if (mDesktopAppLaunchTransitionManager != null) { mDesktopAppLaunchTransitionManager.unregisterTransitions(); } mDesktopAppLaunchTransitionManager = null; mDesktopVisibilityController.onDestroy(); - sConnected = false; LockedUserState.get(this).removeOnUserUnlockedRunnable(mUserUnlockedRunnable); ScreenOnTracker.INSTANCE.get(this).removeListener(mScreenOnListener); diff --git a/quickstep/src/com/android/quickstep/util/ActiveTrackpadList.kt b/quickstep/src/com/android/quickstep/util/ActiveTrackpadList.kt new file mode 100644 index 0000000000..63bd03dab6 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/ActiveTrackpadList.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 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.quickstep.util + +import android.content.Context +import android.hardware.input.InputManager +import android.view.InputDevice +import com.android.launcher3.util.Executors +import com.android.launcher3.util.IntSet + +/** Utility class to maintain a list of actively connected trackpad devices */ +class ActiveTrackpadList(ctx: Context, private val updateCallback: Runnable) : + IntSet(), InputManager.InputDeviceListener { + + private val inputManager = ctx.getSystemService(InputManager::class.java)!! + + init { + inputManager.registerInputDeviceListener(this, Executors.UI_HELPER_EXECUTOR.handler) + inputManager.inputDeviceIds.forEach { deviceId -> onInputDeviceAdded(deviceId) } + } + + override fun onInputDeviceAdded(deviceId: Int) { + if (isTrackpadDevice(deviceId)) { + // This updates internal TIS state so it needs to also run on the main + // thread. + Executors.MAIN_EXECUTOR.execute { + val wasEmpty = isEmpty + add(deviceId) + if (wasEmpty) update() + } + } + } + + override fun onInputDeviceChanged(deviceId: Int) {} + + override fun onInputDeviceRemoved(deviceId: Int) { + // This updates internal TIS state so it needs to also run on the main thread. + Executors.MAIN_EXECUTOR.execute { + remove(deviceId) + if (isEmpty) update() + } + } + + private fun update() { + updateCallback.run() + } + + fun destroy() { + inputManager.unregisterInputDeviceListener(this) + clear() + } + + /** This is a blocking binder call that should run on a bg thread. */ + private fun isTrackpadDevice(deviceId: Int) = + inputManager.getInputDevice(deviceId)?.sources == + (InputDevice.SOURCE_MOUSE or InputDevice.SOURCE_TOUCHPAD) +}