/* * Copyright (C) 2022 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; import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE; import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.quickstep.GestureState.DEFAULT_STATE; import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType; import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW; import android.annotation.TargetApi; import android.app.PendingIntent; import android.app.RemoteAction; import android.app.Service; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.Region; import android.graphics.drawable.Icon; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; import android.util.Log; import android.view.Choreographer; import android.view.InputEvent; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.accessibility.AccessibilityManager; import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarManager; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.tracing.LauncherTraceProto; import com.android.launcher3.tracing.TouchInteractionServiceProto; import com.android.launcher3.uioverrides.flags.FlagsFactory; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantInputConsumer; import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer; import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer; import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer; import com.android.quickstep.inputconsumers.ResetGestureInputConsumer; import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; import com.android.quickstep.inputconsumers.StatusBarInputConsumer; import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer; import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActiveGestureLog.CompoundString; import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.ProxyScreenStatusProvider; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.unfold.progress.IUnfoldAnimation; import com.android.wm.shell.back.IBackAnimation; import com.android.wm.shell.bubbles.IBubbles; import com.android.wm.shell.desktopmode.IDesktopMode; import com.android.wm.shell.draganddrop.IDragAndDrop; import com.android.wm.shell.onehanded.IOneHanded; import com.android.wm.shell.pip.IPip; import com.android.wm.shell.recents.IRecentTasks; import com.android.wm.shell.splitscreen.ISplitScreen; import com.android.wm.shell.startingsurface.IStartingWindow; import com.android.wm.shell.transition.IShellTransitions; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; import java.util.LinkedList; import java.util.function.Function; /** * Service connected by system-UI for handling touch interaction. */ @TargetApi(Build.VERSION_CODES.R) public class TouchInteractionService extends Service implements ProtoTraceable { private static final String SUBSTRING_PREFIX = "; "; private static final String NEWLINE_PREFIX = "\n\t\t\t-> "; private static final String TAG = "TouchInteractionService"; private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once"; private final TISBinder mTISBinder = new TISBinder(); /** * Local IOverviewProxy implementation with some methods for local components */ public class TISBinder extends IOverviewProxy.Stub { @Nullable private Runnable mOnOverviewTargetChangeListener = null; @BinderThread public void onInitialize(Bundle bundle) { ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SYSUI_PROXY)); IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP)); IBubbles bubbles = IBubbles.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_BUBBLES)); ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder( KEY_EXTRA_SHELL_SPLIT_SCREEN)); IOneHanded onehanded = IOneHanded.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED)); IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS)); IStartingWindow startingWindow = IStartingWindow.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW)); ISysuiUnlockAnimationController launcherUnlockAnimationController = ISysuiUnlockAnimationController.Stub.asInterface( bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER)); IRecentTasks recentTasks = IRecentTasks.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SHELL_RECENT_TASKS)); IBackAnimation backAnimation = IBackAnimation.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION)); IDesktopMode desktopMode = IDesktopMode.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE)); IUnfoldAnimation unfoldTransition = IUnfoldAnimation.Stub.asInterface( bundle.getBinder(KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER)); IDragAndDrop dragAndDrop = IDragAndDrop.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SHELL_DRAG_AND_DROP)); MAIN_EXECUTOR.execute(() -> { SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip, bubbles, splitscreen, onehanded, shellTransitions, startingWindow, recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode, unfoldTransition, dragAndDrop); TouchInteractionService.this.initInputMonitor("TISBinder#onInitialize()"); preloadOverview(true /* fromInit */); }); sIsInitialized = true; } @BinderThread @Override public void onTaskbarToggled() { if (!FeatureFlags.ENABLE_KEYBOARD_TASKBAR_TOGGLE.get()) return; MAIN_EXECUTOR.execute(() -> { TaskbarActivityContext activityContext = mTaskbarManager.getCurrentActivityContext(); if (activityContext != null) { activityContext.toggleTaskbarStash(); } }); } @BinderThread public void onOverviewToggle() { TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle"); // If currently screen pinning, do not enter overview if (mDeviceState.isScreenPinningActive()) { return; } TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE); } @BinderThread @Override public void onOverviewShown(boolean triggeredFromAltTab) { if (triggeredFromAltTab) { TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_KEYBOARD_INPUT); } else { mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW); } } @BinderThread @Override public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { if (triggeredFromAltTab && !triggeredFromHomeKey) { // onOverviewShownFromAltTab hides the overview and ends at the target app mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE); } } @BinderThread @Override public void onAssistantAvailable(boolean available, boolean longPressHomeEnabled) { MAIN_EXECUTOR.execute(() -> { mDeviceState.setAssistantAvailable(available); TouchInteractionService.this.onAssistantVisibilityChanged(); executeForTaskbarManager(() -> mTaskbarManager .onLongPressHomeEnabled(longPressHomeEnabled)); }); } @BinderThread @Override public void onAssistantVisibilityChanged(float visibility) { MAIN_EXECUTOR.execute(() -> { mDeviceState.setAssistantVisibility(visibility); TouchInteractionService.this.onAssistantVisibilityChanged(); }); } @Override public void onNavigationBarSurface(SurfaceControl surface) { // TODO: implement } @BinderThread public void onSystemUiStateChanged(int stateFlags) { MAIN_EXECUTOR.execute(() -> { int lastFlags = mDeviceState.getSystemUiStateFlags(); mDeviceState.setSystemUiFlags(stateFlags); TouchInteractionService.this.onSystemUiFlagsChanged(lastFlags); }); } @BinderThread public void onActiveNavBarRegionChanges(Region region) { MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region)); } @BinderThread @Override public void onScreenTurnedOn() { MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn); } @BinderThread @Override public void onScreenTurningOn() { MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOn); } @BinderThread @Override public void onScreenTurningOff() { MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurningOff); } @BinderThread @Override public void enterStageSplitFromRunningApp(boolean leftOrTop) { StatefulActivity activity = mOverviewComponentObserver.getActivityInterface().getCreatedActivity(); if (activity != null) { activity.enterStageSplitFromRunningApp(leftOrTop); } } /** * Preloads the Overview activity. * * This method should only be used when the All Set page of the SUW is reached to safely * preload the Launcher for the SUW first reveal. */ public void preloadOverviewForSUWAllSet() { preloadOverview(false, true); } @Override public void onRotationProposal(int rotation, boolean isValid) { executeForTaskbarManager(() -> mTaskbarManager.onRotationProposal(rotation, isValid)); } @Override public void disable(int displayId, int state1, int state2, boolean animate) { executeForTaskbarManager(() -> mTaskbarManager .disableNavBarElements(displayId, state1, state2, animate)); } @Override public void onSystemBarAttributesChanged(int displayId, int behavior) { executeForTaskbarManager(() -> mTaskbarManager .onSystemBarAttributesChanged(displayId, behavior)); } @Override public void onNavButtonsDarkIntensityChanged(float darkIntensity) { executeForTaskbarManager(() -> mTaskbarManager .onNavButtonsDarkIntensityChanged(darkIntensity)); } private void executeForTaskbarManager(final Runnable r) { MAIN_EXECUTOR.execute(() -> { if (mTaskbarManager == null) { return; } r.run(); }); } public TaskbarManager getTaskbarManager() { return mTaskbarManager; } public OverviewCommandHelper getOverviewCommandHelper() { return mOverviewCommandHelper; } /** * Sets a proxy to bypass swipe up behavior */ public void setSwipeUpProxy(Function proxy) { mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null); } /** * Sets the task id where gestures should be blocked */ public void setGestureBlockedTaskId(int taskId) { mDeviceState.setGestureBlockingTaskId(taskId); } /** Sets a listener to be run on Overview Target updates. */ public void setOverviewTargetChangeListener(@Nullable Runnable listener) { mOnOverviewTargetChangeListener = listener; } protected void onOverviewTargetChange() { if (mOnOverviewTargetChangeListener != null) { mOnOverviewTargetChangeListener.run(); mOnOverviewTargetChangeListener = null; } } } 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 = this::createFallbackSwipeHandler; private ActivityManagerWrapper mAM; private OverviewCommandHelper mOverviewCommandHelper; private OverviewComponentObserver mOverviewComponentObserver; private InputConsumerController mInputConsumer; private RecentsAnimationDeviceState mDeviceState; private TaskAnimationManager mTaskAnimationManager; private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP; private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP; private Choreographer mMainChoreographer; private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer; private GestureState mGestureState = DEFAULT_STATE; private InputMonitorCompat mInputMonitorCompat; private InputEventReceiver mInputEventReceiver; private TaskbarManager mTaskbarManager; private Function mSwipeUpProxyProvider = i -> null; @Override public void onCreate() { super.onCreate(); // Initialize anything here that is needed in direct boot mode. // Everything else should be initialized in onUserUnlocked() below. mMainChoreographer = Choreographer.getInstance(); mAM = ActivityManagerWrapper.getInstance(); mDeviceState = new RecentsAnimationDeviceState(this, true); mTaskbarManager = new TaskbarManager(this); mRotationTouchHelper = mDeviceState.getRotationTouchHelper(); BootAwarePreloader.start(this); // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized. mDeviceState.runOnUserUnlocked(this::onUserUnlocked); mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked); mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); ProtoTracer.INSTANCE.get(this).add(this); sConnected = true; } private void disposeEventHandlers(String reason) { Log.d(TAG, "disposeEventHandlers: Reason: " + reason); if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); mInputEventReceiver = null; } if (mInputMonitorCompat != null) { mInputMonitorCompat.dispose(); mInputMonitorCompat = null; } } private void initInputMonitor(String reason) { disposeEventHandlers("Initializing input monitor due to: " + reason); if (mDeviceState.isButtonNavMode()) { return; } mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId()); mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(), mMainChoreographer, this::onInputEvent); mRotationTouchHelper.updateGestureTouchRegions(); } /** * Called when the navigation mode changes, guaranteed to be after the device state has updated. */ private void onNavigationModeChanged() { initInputMonitor("onNavigationModeChanged()"); resetHomeBounceSeenOnQuickstepEnabledFirstTime(); } @UiThread public void onUserUnlocked() { mTaskAnimationManager = new TaskAnimationManager(this); mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState); mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver, mTaskAnimationManager); mResetGestureInputConsumer = new ResetGestureInputConsumer( mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext); mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); mInputConsumer.registerInputConsumer(); onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags()); onAssistantVisibilityChanged(); // Initialize the task tracker TopTaskTracker.INSTANCE.get(this); // Temporarily disable model preload // new ModelPreload().start(this); resetHomeBounceSeenOnQuickstepEnabledFirstTime(); mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange); onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame()); } public OverviewCommandHelper getOverviewCommandHelper() { return mOverviewCommandHelper; } private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() { if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) { // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation // mode doesn't have gestures return; } // Reset home bounce seen on quick step enabled for first time SharedPreferences sharedPrefs = LauncherPrefs.getPrefs(this); if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) { sharedPrefs.edit() .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true) .putBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN, false) .apply(); } } private void onOverviewTargetChange(boolean isHomeAndOverviewSame) { AccessibilityManager am = getSystemService(AccessibilityManager.class); if (isHomeAndOverviewSame) { Intent intent = new Intent(mOverviewComponentObserver.getHomeIntent()) .setAction(INTENT_ACTION_ALL_APPS_TOGGLE); RemoteAction allAppsAction = new RemoteAction( Icon.createWithResource(this, R.drawable.ic_apps), getString(R.string.all_apps_label), getString(R.string.all_apps_label), PendingIntent.getActivity(this, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)); am.registerSystemAction(allAppsAction, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); } else { am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); } StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface() .getCreatedActivity(); if (newOverviewActivity != null) { mTaskbarManager.setActivity(newOverviewActivity); } mTISBinder.onOverviewTargetChange(); } @UiThread private void onSystemUiFlagsChanged(int lastSysUIFlags) { if (mDeviceState.isUserUnlocked()) { int systemUiStateFlags = mDeviceState.getSystemUiStateFlags(); SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags); mOverviewComponentObserver.onSystemUiStateChanged(); mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags); boolean wasFreeformActive = (lastSysUIFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0; boolean isFreeformActive = (systemUiStateFlags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0; if (wasFreeformActive != isFreeformActive) { DesktopVisibilityController controller = LauncherActivityInterface.INSTANCE.getDesktopVisibilityController(); if (controller != null) { controller.setFreeformTasksVisible(isFreeformActive); } } int isShadeExpandedFlag = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0; boolean isExpanded = (systemUiStateFlags & isShadeExpandedFlag) != 0; if (wasExpanded != isExpanded && isExpanded) { // End live tile when expanding the notification panel for the first time from // overview. mTaskAnimationManager.endLiveTile(); } if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) != (systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) { // Update the tracing state if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) { Log.d(TAG, "Starting tracing."); ProtoTracer.INSTANCE.get(this).start(); } else { Log.d(TAG, "Stopping tracing. Dumping to file=" + ProtoTracer.INSTANCE.get(this).getTraceFile()); ProtoTracer.INSTANCE.get(this).stop(); } } } } @UiThread private void onAssistantVisibilityChanged() { if (mDeviceState.isUserUnlocked()) { mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged( mDeviceState.getAssistantVisibility()); } } @Override public void onDestroy() { Log.d(TAG, "Touch service destroyed: user=" + getUserId()); sIsInitialized = false; if (mDeviceState.isUserUnlocked()) { mInputConsumer.unregisterInputConsumer(); mOverviewComponentObserver.onDestroy(); } disposeEventHandlers("TouchInteractionService onDestroy()"); mDeviceState.destroy(); SystemUiProxy.INSTANCE.get(this).clearProxy(); ProtoTracer.INSTANCE.get(this).stop(); ProtoTracer.INSTANCE.get(this).remove(this); getSystemService(AccessibilityManager.class) .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); mTaskbarManager.destroy(); sConnected = false; super.onDestroy(); } @Override public IBinder onBind(Intent intent) { Log.d(TAG, "Touch service connected: user=" + getUserId()); return mTISBinder; } private void onInputEvent(InputEvent ev) { if (!(ev instanceof MotionEvent)) { Log.e(TAG, "Unknown event " + ev); return; } MotionEvent event = (MotionEvent) ev; TestLogging.recordMotionEvent( TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event); if (!mDeviceState.isUserUnlocked()) { return; } Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride( TraceHelper.FLAG_ALLOW_BINDER_TRACKING); final int action = event.getActionMasked(); // Note this will create a new consumer every mouse click, as after ACTION_UP from the click // an ACTION_HOVER_ENTER will fire as well. boolean isHoverActionWithoutConsumer = event.isHoverEvent() && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0; if (action == ACTION_DOWN || isHoverActionWithoutConsumer) { mRotationTouchHelper.setOrientationTransformIfNeeded(event); if ((!mDeviceState.isOneHandedModeActive() && mRotationTouchHelper.isInSwipeUpTouchRegion(event, mOverviewComponentObserver.getActivityInterface())) || isHoverActionWithoutConsumer) { // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger // onConsumerInactive and wipe the previous gesture state GestureState prevGestureState = new GestureState(mGestureState); GestureState newGestureState = createGestureState(mGestureState, getTrackpadGestureType(event)); newGestureState.setSwipeUpStartTimeMs(SystemClock.uptimeMillis()); mConsumer.onConsumerAboutToBeSwitched(); mGestureState = newGestureState; mConsumer = newConsumer(prevGestureState, mGestureState, event); mUncheckedConsumer = mConsumer; } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode() && mDeviceState.canTriggerAssistantAction(event)) { mGestureState = createGestureState(mGestureState, getTrackpadGestureType(event)); // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we // should not interrupt it. QuickSwitch assumes that interruption can only // happen if the next gesture is also quick switch. mUncheckedConsumer = tryCreateAssistantInputConsumer(mGestureState, event); } else if (mDeviceState.canTriggerOneHandedAction(event)) { // Consume gesture event for triggering one handed feature. mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState, InputConsumer.NO_OP, mInputMonitorCompat); } else { mUncheckedConsumer = InputConsumer.NO_OP; } } else { // Other events if (mUncheckedConsumer != InputConsumer.NO_OP) { // Only transform the event if we are handling it in a proper consumer mRotationTouchHelper.setOrientationTransformIfNeeded(event); } } if (mUncheckedConsumer != InputConsumer.NO_OP) { switch (event.getActionMasked()) { case ACTION_DOWN: // fall through case ACTION_UP: ActiveGestureLog.INSTANCE.addLog( /* event= */ "onMotionEvent(" + (int) event.getRawX() + ", " + (int) event.getRawY() + "): " + MotionEvent.actionToString(event.getActionMasked()) + ", " + MotionEvent.classificationToString(event.getClassification()), /* gestureEvent= */ event.getActionMasked() == ACTION_DOWN ? MOTION_DOWN : MOTION_UP); break; case ACTION_MOVE: ActiveGestureLog.INSTANCE.addLog("onMotionEvent: " + MotionEvent.actionToString(event.getActionMasked()) + "," + MotionEvent.classificationToString(event.getClassification()) + ", pointerCount: " + event.getPointerCount(), MOTION_MOVE); break; default: { ActiveGestureLog.INSTANCE.addLog("onMotionEvent: " + MotionEvent.actionToString(event.getActionMasked()) + "," + MotionEvent.classificationToString(event.getClassification())); } } } boolean cancelGesture = mGestureState.getActivityInterface() != null && mGestureState.getActivityInterface().shouldCancelCurrentGesture(); boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture) && mConsumer != null && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture(); if (cancelGesture) { event.setAction(ACTION_CANCEL); } if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN || action == ACTION_POINTER_UP)) { // Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad. if (action == ACTION_POINTER_DOWN) { mGestureState.setTrackpadGestureType(getTrackpadGestureType(event)); } } else if (event.isHoverEvent()) { mUncheckedConsumer.onHoverEvent(event); } else { mUncheckedConsumer.onMotionEvent(event); } if (cleanUpConsumer) { reset(); } TraceHelper.INSTANCE.endFlagsOverride(traceToken); ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate(); } private InputConsumer tryCreateAssistantInputConsumer( GestureState gestureState, MotionEvent motionEvent) { return tryCreateAssistantInputConsumer( InputConsumer.NO_OP, gestureState, motionEvent, CompoundString.NO_OP); } private InputConsumer tryCreateAssistantInputConsumer( InputConsumer base, GestureState gestureState, MotionEvent motionEvent, CompoundString reasonString) { if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) { reasonString.append(SUBSTRING_PREFIX) .append("is gesture-blocked task, using base input consumer"); return base; } else { reasonString.append(SUBSTRING_PREFIX).append("using AssistantInputConsumer"); return new AssistantInputConsumer( this, gestureState, base, mInputMonitorCompat, mDeviceState, motionEvent); } } public GestureState createGestureState(GestureState previousGestureState, GestureState.TrackpadGestureType trackpadGestureType) { final GestureState gestureState; TopTaskTracker.CachedTaskInfo taskInfo; if (mTaskAnimationManager.isRecentsAnimationRunning()) { gestureState = new GestureState(mOverviewComponentObserver, ActiveGestureLog.INSTANCE.getLogId()); taskInfo = previousGestureState.getRunningTask(); gestureState.updateRunningTask(taskInfo); gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId()); gestureState.updatePreviouslyAppearedTaskIds( previousGestureState.getPreviouslyAppearedTaskIds()); } else { gestureState = new GestureState(mOverviewComponentObserver, ActiveGestureLog.INSTANCE.incrementLogId()); taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false); gestureState.updateRunningTask(taskInfo); } gestureState.setTrackpadGestureType(trackpadGestureType); // Log initial state for the gesture. ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=") .append(taskInfo == null ? "no running task" : taskInfo.getPackageName())); ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=") .append(mDeviceState.getSystemUiStateString())); return gestureState; } private InputConsumer newConsumer( GestureState previousGestureState, GestureState newGestureState, MotionEvent event) { AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState); if (progressProxy != null) { InputConsumer consumer = new ProgressDelegateInputConsumer( this, mTaskAnimationManager, mGestureState, mInputMonitorCompat, progressProxy); logInputConsumerSelectionReason(consumer, newCompoundString( "mSwipeUpProxyProvider has been set, using ProgressDelegateInputConsumer")); return consumer; } boolean canStartSystemGesture = mDeviceState.canStartSystemGesture(); if (!mDeviceState.isUserUnlocked()) { CompoundString reasonString = newCompoundString("device locked"); InputConsumer consumer; if (canStartSystemGesture) { // This handles apps launched in direct boot mode (e.g. dialer) as well as apps // launched while device is locked even after exiting direct boot mode (e.g. camera). consumer = createDeviceLockedInputConsumer( newGestureState, reasonString.append(SUBSTRING_PREFIX) .append("can start system gesture")); } else { consumer = getDefaultInputConsumer( reasonString.append(SUBSTRING_PREFIX) .append("cannot start system gesture")); } logInputConsumerSelectionReason(consumer, reasonString); return consumer; } CompoundString reasonString; InputConsumer base; // When there is an existing recents animation running, bypass systemState check as this is // a followup gesture and the first gesture started in a valid system state. if (canStartSystemGesture || previousGestureState.isRecentsAnimationRunning()) { reasonString = newCompoundString(canStartSystemGesture ? "can start system gesture" : "recents animation was running") .append(", trying to use base consumer"); base = newBaseConsumer(previousGestureState, newGestureState, event, reasonString); } else { reasonString = newCompoundString( "cannot start system gesture and recents animation was not running") .append(", trying to use default input consumer"); base = getDefaultInputConsumer(reasonString); } if (mDeviceState.isGesturalNavMode()) { handleOrientationSetup(base); } if (mDeviceState.isFullyGesturalNavMode()) { String reasonPrefix = "device is in gesture navigation mode"; if (mDeviceState.canTriggerAssistantAction(event)) { reasonString.append(NEWLINE_PREFIX) .append(reasonPrefix) .append(SUBSTRING_PREFIX) .append("gesture can trigger the assistant") .append(", trying to use assistant input consumer"); base = tryCreateAssistantInputConsumer(base, newGestureState, event, reasonString); } // If Taskbar is present, we listen for long press or cursor hover events to unstash it. TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext(); if (tac != null) { // Present always on large screen or on small screen w/ flag DeviceProfile dp = tac.getDeviceProfile(); boolean useTaskbarConsumer = dp.isTaskbarPresent && !TaskbarManager.isPhoneMode(dp) && !tac.isInStashedLauncherState(); if (canStartSystemGesture && useTaskbarConsumer) { reasonString.append(NEWLINE_PREFIX) .append(reasonPrefix) .append(SUBSTRING_PREFIX) .append("TaskbarActivityContext != null, " + "using TaskbarUnstashInputConsumer"); base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac); } } if (mDeviceState.isBubblesExpanded()) { reasonString = newCompoundString(reasonPrefix) .append(SUBSTRING_PREFIX) .append("bubbles expanded, trying to use default input consumer"); // Bubbles can handle home gesture itself. base = getDefaultInputConsumer(reasonString); } if (mDeviceState.isSystemUiDialogShowing()) { reasonString = newCompoundString(reasonPrefix) .append(SUBSTRING_PREFIX) .append("system dialog is showing, using SysUiOverlayInputConsumer"); base = new SysUiOverlayInputConsumer( getBaseContext(), mDeviceState, mInputMonitorCompat); } if (ENABLE_TRACKPAD_GESTURE.get() && mGestureState.isTrackpadGesture() && !previousGestureState.isRecentsAnimationRunning()) { reasonString = newCompoundString(reasonPrefix) .append(SUBSTRING_PREFIX) .append("Trackpad 3-finger gesture, using StatusBarInputConsumer"); base = new StatusBarInputConsumer(getBaseContext(), base, mInputMonitorCompat); } if (mDeviceState.isScreenPinningActive()) { reasonString = newCompoundString(reasonPrefix) .append(SUBSTRING_PREFIX) .append("screen pinning is active, using ScreenPinnedInputConsumer"); // Note: we only allow accessibility to wrap this, and it replaces the previous // base input consumer (which should be NO_OP anyway since topTaskLocked == true). base = new ScreenPinnedInputConsumer(this, newGestureState); } if (mDeviceState.canTriggerOneHandedAction(event)) { reasonString.append(NEWLINE_PREFIX) .append(reasonPrefix) .append(SUBSTRING_PREFIX) .append("gesture can trigger one handed mode") .append(", using OneHandedModeInputConsumer"); base = new OneHandedModeInputConsumer( this, mDeviceState, base, mInputMonitorCompat); } if (mDeviceState.isAccessibilityMenuAvailable()) { reasonString.append(NEWLINE_PREFIX) .append(reasonPrefix) .append(SUBSTRING_PREFIX) .append("accessibility menu is available") .append(", using AccessibilityInputConsumer"); base = new AccessibilityInputConsumer( this, mDeviceState, mGestureState, base, mInputMonitorCompat); } } else { String reasonPrefix = "device is not in gesture navigation mode"; if (mDeviceState.isScreenPinningActive()) { reasonString = newCompoundString(reasonPrefix) .append(SUBSTRING_PREFIX) .append("screen pinning is active, trying to use default input consumer"); base = getDefaultInputConsumer(reasonString); } if (mDeviceState.canTriggerOneHandedAction(event)) { reasonString.append(NEWLINE_PREFIX) .append(reasonPrefix) .append(SUBSTRING_PREFIX) .append("gesture can trigger one handed mode") .append(", using OneHandedModeInputConsumer"); base = new OneHandedModeInputConsumer( this, mDeviceState, base, mInputMonitorCompat); } } logInputConsumerSelectionReason(base, reasonString); return base; } private CompoundString newCompoundString(String substring) { return new CompoundString(NEWLINE_PREFIX).append(substring); } private void logInputConsumerSelectionReason( InputConsumer consumer, CompoundString reasonString) { ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ") .append(consumer.getName()) .append(". reason(s):") .append(reasonString)); if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) { ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER); } } private void handleOrientationSetup(InputConsumer baseInputConsumer) { baseInputConsumer.notifyOrientationSetup(); } private InputConsumer newBaseConsumer( GestureState previousGestureState, GestureState gestureState, MotionEvent event, CompoundString reasonString) { if (mDeviceState.isKeyguardShowingOccluded()) { // This handles apps showing over the lockscreen (e.g. camera) return createDeviceLockedInputConsumer( gestureState, reasonString.append(SUBSTRING_PREFIX) .append("keyguard is showing occluded") .append(", trying to use device locked input consumer")); } reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded"); // Use overview input consumer for sharesheets on top of home. boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted() && gestureState.getRunningTask() != null && gestureState.getRunningTask().isRootChooseActivity(); if (gestureState.getRunningTask() != null && gestureState.getRunningTask().isExcludedAssistant()) { // In the case where we are in the excluded assistant state, ignore it and treat the // running activity as the task behind the assistant gestureState.updateRunningTask(TopTaskTracker.INSTANCE.get(this) .getCachedTopTask(true /* filterOnlyVisibleRecents */)); forceOverviewInputConsumer = gestureState.getRunningTask().isHomeTask(); } boolean previousGestureAnimatedToLauncher = previousGestureState.isRunningAnimationToLauncher(); // with shell-transitions, home is resumed during recents animation, so // explicitly check against recents animation too. boolean launcherResumedThroughShellTransition = gestureState.getActivityInterface().isResumed() && !previousGestureState.isRecentsAnimationRunning(); if (gestureState.getActivityInterface().isInLiveTileMode()) { return createOverviewInputConsumer( previousGestureState, gestureState, event, forceOverviewInputConsumer, reasonString.append(SUBSTRING_PREFIX) .append("is in live tile mode, trying to use overview input consumer")); } else if (gestureState.getRunningTask() == null) { return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX) .append("running task == null")); } else if (previousGestureAnimatedToLauncher || launcherResumedThroughShellTransition || forceOverviewInputConsumer) { return createOverviewInputConsumer( previousGestureState, gestureState, event, forceOverviewInputConsumer, reasonString.append(SUBSTRING_PREFIX) .append(previousGestureAnimatedToLauncher ? "previous gesture animated to launcher" : (launcherResumedThroughShellTransition ? "launcher resumed through a shell transition" : "forceOverviewInputConsumer == true")) .append(", trying to use overview input consumer")); } else if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) { return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX) .append("is gesture-blocked task, trying to use default input consumer")); } else { reasonString.append(SUBSTRING_PREFIX) .append("using OtherActivityInputConsumer"); return createOtherActivityInputConsumer(gestureState, event); } } public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() { return !mOverviewComponentObserver.isHomeAndOverviewSame() ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory; } private InputConsumer createOtherActivityInputConsumer(GestureState gestureState, MotionEvent event) { final AbsSwipeUpHandler.Factory factory = getSwipeUpHandlerFactory(); final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame() || gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event); final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event); return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager, gestureState, shouldDefer, this::onConsumerInactive, mInputMonitorCompat, mInputEventReceiver, disableHorizontalSwipe, factory); } private InputConsumer createDeviceLockedInputConsumer( GestureState gestureState, CompoundString reasonString) { if (mDeviceState.isFullyGesturalNavMode() && gestureState.getRunningTask() != null) { reasonString.append(SUBSTRING_PREFIX) .append("device is in gesture nav mode and running task != null") .append(", using DeviceLockedInputConsumer"); return new DeviceLockedInputConsumer( this, mDeviceState, mTaskAnimationManager, gestureState, mInputMonitorCompat); } else { return getDefaultInputConsumer(reasonString .append(SUBSTRING_PREFIX) .append(mDeviceState.isFullyGesturalNavMode() ? "running task == null" : "device is not in gesture nav mode") .append(", trying to use default input consumer")); } } public InputConsumer createOverviewInputConsumer( GestureState previousGestureState, GestureState gestureState, MotionEvent event, boolean forceOverviewInputConsumer, CompoundString reasonString) { StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity(); if (activity == null) { return getDefaultInputConsumer( reasonString.append(SUBSTRING_PREFIX) .append("activity == null, trying to use default input consumer")); } boolean hasWindowFocus = activity.getRootView().hasWindowFocus(); boolean isPreviousGestureAnimatingToLauncher = previousGestureState.isRunningAnimationToLauncher(); boolean isInLiveTileMode = gestureState.getActivityInterface().isInLiveTileMode(); reasonString.append(SUBSTRING_PREFIX) .append(hasWindowFocus ? "activity has window focus" : (isPreviousGestureAnimatingToLauncher ? "previous gesture is still animating to launcher" : isInLiveTileMode ? "device is in live mode" : "all overview focus conditions failed")); if (hasWindowFocus || isPreviousGestureAnimatingToLauncher || isInLiveTileMode) { reasonString.append(SUBSTRING_PREFIX) .append("overview should have focus, using OverviewInputConsumer"); return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat, false /* startingInActivityBounds */); } else { reasonString.append(SUBSTRING_PREFIX).append( "overview shouldn't have focus, using OverviewWithoutFocusInputConsumer"); final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event); return new OverviewWithoutFocusInputConsumer(activity, mDeviceState, gestureState, mInputMonitorCompat, disableHorizontalSwipe); } } /** * To be called by the consumer when it's no longer active. This can be called by any consumer * in the hierarchy at any point during the gesture (ie. if a delegate consumer starts * intercepting touches, the base consumer can try to call this). */ private void onConsumerInactive(InputConsumer caller) { if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) { reset(); } } private void reset() { mConsumer = mUncheckedConsumer = getDefaultInputConsumer(); mGestureState = DEFAULT_STATE; // By default, use batching of the input events, but check receiver before using in the rare // case that the monitor was disposed before the swipe settled if (mInputEventReceiver != null) { mInputEventReceiver.setBatchingEnabled(true); } } private @NonNull InputConsumer getDefaultInputConsumer() { return getDefaultInputConsumer(CompoundString.NO_OP); } /** * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP. */ private @NonNull InputConsumer getDefaultInputConsumer(@NonNull CompoundString reasonString) { if (mResetGestureInputConsumer != null) { reasonString.append(SUBSTRING_PREFIX).append( "mResetGestureInputConsumer initialized, using ResetGestureInputConsumer"); return mResetGestureInputConsumer; } else { reasonString.append(SUBSTRING_PREFIX).append( "mResetGestureInputConsumer not initialized, using no-op input consumer"); // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to // NO_OP until then (we never want these to be null). return InputConsumer.NO_OP; } } private void preloadOverview(boolean fromInit) { preloadOverview(fromInit, false); } private void preloadOverview(boolean fromInit, boolean forSUWAllSet) { if (!mDeviceState.isUserUnlocked()) { return; } if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) { // Prevent the overview from being started before the real home on first boot. return; } if ((RestoreDbTask.isPending(this) && !forSUWAllSet) || !mDeviceState.isUserSetupComplete()) { // Preloading while a restore is pending may cause launcher to start the restore // too early. return; } final BaseActivityInterface activityInterface = mOverviewComponentObserver.getActivityInterface(); final Intent overviewIntent = new Intent( mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState()); if (activityInterface.getCreatedActivity() != null && fromInit) { // The activity has been created before the initialization of overview service. It is // usually happens when booting or launcher is the top activity, so we should already // have the latest state. return; } // TODO(b/258022658): Remove temporary logging. Log.i(TAG, "preloadOverview: forSUWAllSet=" + forSUWAllSet + ", isHomeAndOverviewSame=" + mOverviewComponentObserver.isHomeAndOverviewSame()); mTaskAnimationManager.preloadRecentsAnimation(overviewIntent); } @Override public void onConfigurationChanged(Configuration newConfig) { if (!mDeviceState.isUserUnlocked()) { return; } final BaseActivityInterface activityInterface = mOverviewComponentObserver.getActivityInterface(); final BaseDraggingActivity activity = activityInterface.getCreatedActivity(); if (activity == null || activity.isStarted()) { // We only care about the existing background activity. return; } if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(), activity.getResources().getConfiguration().diff(newConfig))) { // Since navBar gestural height are different between portrait and landscape, // can handle orientation changes and refresh navigation gestural region through // onOneHandedModeChanged() int newGesturalHeight = ResourceUtils.getNavbarSize( ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getApplicationContext().getResources()); mDeviceState.onOneHandedModeChanged(newGesturalHeight); return; } preloadOverview(false /* fromInit */); } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) { if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) { LinkedList args = new LinkedList(Arrays.asList(rawArgs)); switch (args.pollFirst()) { case "cmd": if (args.peekFirst() == null) { printAvailableCommands(pw); } else { onCommand(pw, args); } break; } } else { // Dump everything FlagsFactory.dump(pw); if (mDeviceState.isUserUnlocked()) { PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw); } mDeviceState.dump(pw); if (mOverviewComponentObserver != null) { mOverviewComponentObserver.dump(pw); } if (mOverviewCommandHelper != null) { mOverviewCommandHelper.dump(pw); } if (mGestureState != null) { mGestureState.dump(pw); } pw.println("Input state:"); pw.println(" mInputMonitorCompat=" + mInputMonitorCompat); pw.println(" mInputEventReceiver=" + mInputEventReceiver); DisplayController.INSTANCE.get(this).dump(pw); pw.println("TouchState:"); BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null : mOverviewComponentObserver.getActivityInterface().getCreatedActivity(); boolean resumed = mOverviewComponentObserver != null && mOverviewComponentObserver.getActivityInterface().isResumed(); pw.println(" createdOverviewActivity=" + createdOverviewActivity); pw.println(" resumed=" + resumed); pw.println(" mConsumer=" + mConsumer.getName()); ActiveGestureLog.INSTANCE.dump("", pw); RecentsModel.INSTANCE.get(this).dump("", pw); pw.println("ProtoTrace:"); pw.println(" file=" + ProtoTracer.INSTANCE.get(this).getTraceFile()); if (createdOverviewActivity != null) { createdOverviewActivity.getDeviceProfile().dump(this, "", pw); } mTaskbarManager.dumpLogs("", pw); } } private void printAvailableCommands(PrintWriter pw) { pw.println("Available commands:"); pw.println(" clear-touch-log: Clears the touch interaction log"); pw.println(" print-gesture-log: only prints the ActiveGestureLog dump"); } private void onCommand(PrintWriter pw, LinkedList args) { String cmd = args.pollFirst(); if (cmd == null) { pw.println("Command missing"); printAvailableCommands(pw); return; } switch (cmd) { case "clear-touch-log": ActiveGestureLog.INSTANCE.clear(); break; case "print-gesture-log": ActiveGestureLog.INSTANCE.dump("", pw); break; default: pw.println("Command does not exist: " + cmd); printAvailableCommands(pw); } } private AbsSwipeUpHandler createLauncherSwipeHandler( GestureState gestureState, long touchTimeMs) { return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager, gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(), mInputConsumer); } private AbsSwipeUpHandler createFallbackSwipeHandler( GestureState gestureState, long touchTimeMs) { return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager, gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(), mInputConsumer); } @Override public void writeToProto(LauncherTraceProto.Builder proto) { TouchInteractionServiceProto.Builder serviceProto = TouchInteractionServiceProto.newBuilder(); serviceProto.setServiceConnected(true); if (mOverviewComponentObserver != null) { mOverviewComponentObserver.writeToProto(serviceProto); } mConsumer.writeToProto(serviceProto); proto.setTouchInteractionService(serviceProto); } }