From 5a9d4af1708e79201628f4e237f8334d71266f64 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Wed, 31 Jan 2024 12:49:05 -0800 Subject: [PATCH] Log split metrics through individual sessions * Now we log the original source of the first selected app as soon as the user selects it (previously we only did when user selected second app) * We log the item info for the second app to determine second surface. * Added new metrics to log after user has started a split session and ended a split session * We log different cancellation reasons (cancel button, home gesture, general other app interruptions, etc). * One KI/Bug: When the second app is selected via taskbar in overview, the container will say hotseat because we are using Launcher's logger and not Taskbar's. Taskbar's logger manually overwrites the container in TaskbarActivityContext, we may be able to make something hacky that can allow us to overwrite, but that'll have to be a separate change Bug: 322551862 Test: Logged metrics manually with event and itemInfo Change-Id: I177623fd00ce62acf2d4ee983b58561d8c946d59 --- .../taskbar/TaskbarUIController.java | 6 +- .../BaseRecentsViewStateController.java | 3 +- .../uioverrides/QuickstepLauncher.java | 20 +++-- .../quickstep/util/AppPairsController.java | 4 +- .../util/SplitAnimationController.kt | 14 +++- .../quickstep/util/SplitSelectDataHolder.kt | 18 ++-- .../util/SplitSelectStateController.java | 82 +++++++++++++++---- .../util/SplitToWorkspaceController.java | 3 +- .../quickstep/views/LauncherRecentsView.java | 3 +- .../android/quickstep/views/RecentsView.java | 8 +- .../views/SplitInstructionsView.java | 10 ++- .../com/android/quickstep/views/TaskView.java | 2 +- .../util/SplitSelectDataHolderTest.kt | 23 +++--- .../util/SplitSelectStateControllerTest.kt | 12 ++- src/com/android/launcher3/Launcher.java | 7 +- .../launcher3/logging/StatsLogManager.java | 26 ++++++ .../touch/WorkspaceTouchListener.java | 3 +- 17 files changed, 182 insertions(+), 62 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index bb2ac738c3..b6e93bb8a9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -260,7 +260,8 @@ public class TaskbarUIController { taskAttributes.getThumbnailView(), taskAttributes.getThumbnailView().getThumbnail(), null /* intent */, - null /* user */); + null /* user */, + info); return; } } @@ -273,7 +274,8 @@ public class TaskbarUIController { startingView, null /* thumbnail */, intent, - info.user); + info.user, + info); } ); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 9329e16909..dbaeafbee2 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -22,6 +22,7 @@ import static com.android.app.animation.Interpolators.FINAL_FRAME; import static com.android.app.animation.Interpolators.INSTANT; import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; @@ -111,7 +112,7 @@ public abstract class BaseRecentsViewStateController boolean exitingOverview = !FeatureFlags.enableSplitContextually() && !toState.overviewUi; if (mRecentsView.isSplitSelectionActive() && exitingOverview) { setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController() - .createPlaceholderDismissAnim(mLauncher)); + .createPlaceholderDismissAnim(mLauncher, LAUNCHER_SPLIT_SELECTION_EXIT_HOME)); setter.setViewAlpha( mRecentsView.getSplitInstructionsView(), 0, diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index c2a248db6e..fbbbdd0a2f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -38,6 +38,8 @@ import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; import static com.android.launcher3.config.FeatureFlags.enableSplitContextually; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition; import static com.android.launcher3.popup.SystemShortcut.APP_INFO; @@ -596,7 +598,7 @@ public class QuickstepLauncher extends Launcher { list.add(getDragController()); Consumer splitAnimator = animatorSet -> animatorSet.play(mSplitSelectStateController.getSplitAnimationController() - .createPlaceholderDismissAnim(this)); + .createPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_HOME)); switch (mode) { case NO_BUTTON: list.add(new NoButtonQuickSwitchTouchController(this)); @@ -767,8 +769,10 @@ public class QuickstepLauncher extends Launcher { // If Launcher pauses before both split apps are selected, exit split screen. if (!mSplitSelectStateController.isBothSplitAppsConfirmed() && !mSplitSelectStateController.isLaunchingFirstAppFullscreen()) { + mSplitSelectStateController + .logExitReason(LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED); mSplitSelectStateController.getSplitAnimationController() - .playPlaceholderDismissAnim(this); + .playPlaceholderDismissAnim(this, LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED); } } } @@ -1042,17 +1046,17 @@ public class QuickstepLauncher extends Launcher { } @Override - protected void handleSplitAnimationGoingToHome() { - super.handleSplitAnimationGoingToHome(); + protected void handleSplitAnimationGoingToHome(StatsLogManager.EventEnum splitDismissReason) { + super.handleSplitAnimationGoingToHome(splitDismissReason); mSplitSelectStateController.getSplitAnimationController() - .playPlaceholderDismissAnim(this); + .playPlaceholderDismissAnim(this, splitDismissReason); } @Override - public void dismissSplitSelection() { - super.dismissSplitSelection(); + public void dismissSplitSelection(StatsLogManager.LauncherEvent splitDismissEvent) { + super.dismissSplitSelection(splitDismissEvent); mSplitSelectStateController.getSplitAnimationController() - .playPlaceholderDismissAnim(this); + .playPlaceholderDismissAnim(this, splitDismissEvent); } public T getActionsView() { diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java index 839320ed3b..0be65ca155 100644 --- a/quickstep/src/com/android/quickstep/util/AppPairsController.java +++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java @@ -162,10 +162,10 @@ public class AppPairsController { @Nullable Task foundTask2 = foundTasks[1]; if (foundTask2 != null) { - mSplitSelectStateController.setSecondTask(foundTask2); + mSplitSelectStateController.setSecondTask(foundTask2, app2); } else { mSplitSelectStateController.setSecondTask( - app2.intent, app2.user); + app2.intent, app2.user, app2); } mSplitSelectStateController.setLaunchingIconView(appPairIcon); diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt index ad9f5ea1cc..76b3d7bdcd 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt @@ -46,6 +46,7 @@ import com.android.launcher3.Utilities import com.android.launcher3.anim.PendingAnimation import com.android.launcher3.apppairs.AppPairIcon import com.android.launcher3.config.FeatureFlags +import com.android.launcher3.logging.StatsLogManager.EventEnum import com.android.launcher3.statehandlers.DepthController import com.android.launcher3.statemanager.StateManager import com.android.launcher3.statemanager.StatefulActivity @@ -213,17 +214,21 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC } /** Does not play any animation if user is not currently in split selection state. */ - fun playPlaceholderDismissAnim(launcher: StatefulActivity<*>) { + fun playPlaceholderDismissAnim(launcher: StatefulActivity<*>, splitDismissEvent: EventEnum) { if (!splitSelectStateController.isSplitSelectActive) { return } - val anim = createPlaceholderDismissAnim(launcher) + val anim = createPlaceholderDismissAnim(launcher, splitDismissEvent) anim.start() } - /** Returns [AnimatorSet] which slides initial split placeholder view offscreen. */ - fun createPlaceholderDismissAnim(launcher: StatefulActivity<*>) : AnimatorSet { + /** + * Returns [AnimatorSet] which slides initial split placeholder view offscreen and logs an event + * for why split is being dismissed + */ + fun createPlaceholderDismissAnim(launcher: StatefulActivity<*>, + splitDismissEvent: EventEnum) : AnimatorSet { val animatorSet = AnimatorSet() val recentsView : RecentsView<*, *> = launcher.getOverviewPanel() val floatingTask: FloatingTaskView = splitSelectStateController.firstFloatingTaskView @@ -260,6 +265,7 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC splitSelectStateController.splitInstructionsView) } }) + splitSelectStateController.logExitReason(splitDismissEvent) return animatorSet } diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt index c013483bab..06edb14a1f 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt +++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt @@ -87,6 +87,7 @@ class SplitSelectDataHolder( @StagePosition private var initialStagePosition: Int = STAGE_POSITION_UNDEFINED private var itemInfo: ItemInfo? = null + private var secondItemInfo: ItemInfo? = null private var splitEvent: EventEnum? = null private var initialTaskId: Int = INVALID_TASK_ID @@ -144,8 +145,9 @@ class SplitSelectDataHolder( * To be called as soon as user selects the second task (even if animations aren't complete) * @param taskId The second task that will be launched. */ - fun setSecondTask(taskId: Int) { + fun setSecondTask(taskId: Int, itemInfo: ItemInfo) { secondTaskId = taskId + secondItemInfo = itemInfo } /** @@ -153,9 +155,10 @@ class SplitSelectDataHolder( * @param intent The second intent that will be launched. * @param user The user of that intent. */ - fun setSecondTask(intent: Intent, user: UserHandle) { + fun setSecondTask(intent: Intent, user: UserHandle, itemInfo: ItemInfo) { secondIntent = intent secondUser = user + secondItemInfo = itemInfo } /** @@ -163,9 +166,10 @@ class SplitSelectDataHolder( * Sets [secondUser] from that of the pendingIntent * @param pendingIntent The second PendingIntent that will be launched. */ - fun setSecondTask(pendingIntent: PendingIntent) { + fun setSecondTask(pendingIntent: PendingIntent, itemInfo: ItemInfo) { secondPendingIntent = pendingIntent secondUser = pendingIntent.creatorUserHandle + secondItemInfo = itemInfo } /** @@ -173,8 +177,8 @@ class SplitSelectDataHolder( * an extra intent from their RemoteResponse. * See [android.widget.RemoteViews.RemoteResponse.getLaunchOptions].first */ - fun setSecondWidget(pendingIntent: PendingIntent, widgetIntent: Intent?) { - setSecondTask(pendingIntent) + fun setSecondWidget(pendingIntent: PendingIntent, widgetIntent: Intent?, itemInfo: ItemInfo) { + setSecondTask(pendingIntent, itemInfo) widgetSecondIntent = widgetIntent } @@ -407,6 +411,10 @@ class SplitSelectDataHolder( return itemInfo } + fun getSecondItemInfo(): ItemInfo? { + return secondItemInfo + } + private fun isSecondTaskIntentSet(): Boolean { return secondTaskId != INVALID_TASK_ID || secondIntent != null || secondPendingIntent != null diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index 3c90e0cf51..f06418bbd4 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -19,6 +19,10 @@ package com.android.quickstep.util; import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_LEFT_TOP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DESKTOP_MODE_SPLIT_RIGHT_BOTTOM; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTED_SECOND_APP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_COMPLETE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_INITIATED; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; @@ -151,6 +155,11 @@ public class SplitSelectStateController { private SplitInstructionsView mSplitInstructionsView; private final List mSplitSelectionListeners = new ArrayList<>(); + /** + * Tracks metrics from when first app is selected to split launch or cancellation. This also + * gets passed over to shell when attempting to invoke split. + */ + private Pair mSessionInstanceIds; private final BackPressHandler mSplitBackHandler = new BackPressHandler() { @Override @@ -162,7 +171,8 @@ public class SplitSelectStateController { public void onBackInvoked() { // When exiting from split selection, leave current context to go to // homescreen as well - getSplitAnimationController().playPlaceholderDismissAnim(mContext); + getSplitAnimationController().playPlaceholderDismissAnim(mContext, + LAUNCHER_SPLIT_SELECTION_EXIT_HOME); if (mActivityBackCallback != null) { mActivityBackCallback.run(); } @@ -203,6 +213,7 @@ public class SplitSelectStateController { int alreadyRunningTask) { mSplitSelectDataHolder.setInitialTaskSelect(intent, stagePosition, itemInfo, splitEvent, alreadyRunningTask); + createAndLogInstanceIdsForSession(); } /** @@ -213,6 +224,7 @@ public class SplitSelectStateController { @StagePosition int stagePosition, @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) { mSplitSelectDataHolder.setInitialTaskSelect(info, stagePosition, itemInfo, splitEvent); + createAndLogInstanceIdsForSession(); } /** @@ -330,14 +342,12 @@ public class SplitSelectStateController { */ public void launchSplitTasks(@PersistentSnapPosition int snapPosition, @Nullable Consumer callback) { - Pair instanceIds = - LogUtils.getShellShareableInstanceId(); - launchTasks(callback, false /* freezeTaskList */, snapPosition, instanceIds.first); + launchTasks(callback, false /* freezeTaskList */, snapPosition, mSessionInstanceIds.first); mStatsLogManager.logger() - .withItemInfo(mSplitSelectDataHolder.getItemInfo()) - .withInstanceId(instanceIds.second) - .log(mSplitSelectDataHolder.getSplitEvent()); + .withItemInfo(mSplitSelectDataHolder.getSecondItemInfo()) + .withInstanceId(mSessionInstanceIds.second) + .log(LAUNCHER_SPLIT_SELECTED_SECOND_APP); } /** @@ -363,12 +373,27 @@ public class SplitSelectStateController { launchSplitTasks(SNAP_TO_50_50, null); } + /** + * Use to log an event when user exists split selection when the second app **IS NOT** selected. + * This must be called before playing any exit animations since most animations will call + * {@link #resetState()} which removes {@link #mSessionInstanceIds}. + */ + public void logExitReason(StatsLogManager.EventEnum splitExitEvent) { + StatsLogManager.StatsLogger logger = mStatsLogManager.logger(); + if (mSessionInstanceIds != null) { + logger.withInstanceId(mSessionInstanceIds.second); + } else { + Log.w(TAG, "Missing session instanceIds"); + } + logger.log(splitExitEvent); + } + /** * To be called as soon as user selects the second task (even if animations aren't complete) * @param task The second task that will be launched. */ - public void setSecondTask(Task task) { - mSplitSelectDataHolder.setSecondTask(task.key.id); + public void setSecondTask(Task task, ItemInfo itemInfo) { + mSplitSelectDataHolder.setSecondTask(task.key.id, itemInfo); } /** @@ -376,20 +401,20 @@ public class SplitSelectStateController { * @param intent The second intent that will be launched. * @param user The user of that intent. */ - public void setSecondTask(Intent intent, UserHandle user) { - mSplitSelectDataHolder.setSecondTask(intent, user); + public void setSecondTask(Intent intent, UserHandle user, ItemInfo itemInfo) { + mSplitSelectDataHolder.setSecondTask(intent, user, itemInfo); } /** * To be called as soon as user selects the second app (even if animations aren't complete) * @param pendingIntent The second PendingIntent that will be launched. */ - public void setSecondTask(PendingIntent pendingIntent) { - mSplitSelectDataHolder.setSecondTask(pendingIntent); + public void setSecondTask(PendingIntent pendingIntent, ItemInfo itemInfo) { + mSplitSelectDataHolder.setSecondTask(pendingIntent, itemInfo); } public void setSecondWidget(PendingIntent pendingIntent, Intent widgetIntent) { - mSplitSelectDataHolder.setSecondWidget(pendingIntent, widgetIntent); + mSplitSelectDataHolder.setSecondWidget(pendingIntent, widgetIntent, null /*itemInfo*/); } /** @@ -578,7 +603,7 @@ public class SplitSelectStateController { final RemoteTransition remoteTransition = new RemoteTransition(animationRunner, ActivityThread.currentActivityThread().getApplicationThread(), "LaunchAppFullscreen"); - InstanceId instanceId = LogUtils.getShellShareableInstanceId().first; + InstanceId instanceId = mSessionInstanceIds.first; if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { switch (launchData.getSplitLaunchType()) { case SPLIT_SINGLE_TASK_FULLSCREEN -> mSystemUiProxy.startTasks(firstTaskId, @@ -630,6 +655,26 @@ public class SplitSelectStateController { ActivityThread.currentActivityThread().getApplicationThread()); } + /** + * Will initialize {@link #mSessionInstanceIds} if null and log the first split event from + * {@link #mSplitSelectDataHolder} + */ + private void createAndLogInstanceIdsForSession() { + if (mSessionInstanceIds != null) { + Log.w(TAG, "SessionIds should be null"); + } + // Log separately the start of the session and then the first app selected + mSessionInstanceIds = LogUtils.getShellShareableInstanceId(); + mStatsLogManager.logger() + .withInstanceId(mSessionInstanceIds.second) + .log(LAUNCHER_SPLIT_SELECTION_INITIATED); + + mStatsLogManager.logger() + .withItemInfo(mSplitSelectDataHolder.getItemInfo()) + .withInstanceId(mSessionInstanceIds.second) + .log(mSplitSelectDataHolder.getSplitEvent()); + } + public @StagePosition int getActiveSplitStagePosition() { return mSplitSelectDataHolder.getInitialStagePosition(); } @@ -804,6 +849,13 @@ public class SplitSelectStateController { mFirstFloatingTaskView = null; mSplitInstructionsView = null; mLaunchingFirstAppFullscreen = false; + + if (mSessionInstanceIds != null) { + mStatsLogManager.logger() + .withInstanceId(mSessionInstanceIds.second) + .log(LAUNCHER_SPLIT_SELECTION_COMPLETE); + } + mSessionInstanceIds = null; } /** diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java index 50a5a832e1..445a540e5a 100644 --- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java +++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java @@ -45,6 +45,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.quickstep.views.FloatingTaskView; @@ -134,7 +135,7 @@ public class SplitToWorkspaceController { return false; } - mController.setSecondTask(intent, user); + mController.setSecondTask(intent, user, (ItemInfo) tag); startWorkspaceAnimation(view, null /*bitmap*/, bitmapInfo.newIcon(mLauncher)); return true; diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 9bb9775a51..3f1d343663 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -25,6 +25,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.LauncherState.SPRING_LOADED; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported; import android.annotation.TargetApi; @@ -92,7 +93,7 @@ public class LauncherRecentsView extends RecentsView = mock() private val handler: Handler = mock() private val context: StatefulActivity<*> = mock() private val recentsModel: RecentsModel = mock() private val pendingIntent: PendingIntent = mock() - lateinit var splitSelectStateController: SplitSelectStateController + private lateinit var splitSelectStateController: SplitSelectStateController private val primaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId) private val nonPrimaryUserHandle = UserHandle(ActivityManager.RunningTaskInfo().userId + 10) @@ -74,6 +78,9 @@ class SplitSelectStateControllerTest { @Before fun setup() { + `when`(statsLogManager.logger()).thenReturn(statsLogger) + `when`(statsLogger.withInstanceId(any())).thenReturn(statsLogger) + `when`(statsLogger.withItemInfo(any())).thenReturn(statsLogger) splitSelectStateController = SplitSelectStateController( context, @@ -593,9 +600,10 @@ class SplitSelectStateControllerTest { @Test fun secondPendingIntentSet() { val itemInfo = ItemInfo() + val itemInfo2 = ItemInfo() whenever(pendingIntent.creatorUserHandle).thenReturn(primaryUserHandle) splitSelectStateController.setInitialTaskSelect(null, 0, itemInfo, null, 1) - splitSelectStateController.setSecondTask(pendingIntent) + splitSelectStateController.setSecondTask(pendingIntent, itemInfo2) assertTrue(splitSelectStateController.isBothSplitAppsConfirmed) } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 496cb4eb30..39b3fbf762 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -77,6 +77,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED; @@ -1580,7 +1581,7 @@ public class Launcher extends StatefulActivity } if (FeatureFlags.enableSplitContextually()) { - handleSplitAnimationGoingToHome(); + handleSplitAnimationGoingToHome(LAUNCHER_SPLIT_SELECTION_EXIT_HOME); } mOverlayManager.hideOverlay(isStarted() && !isForceInvisible()); handleGestureContract(intent); @@ -1596,7 +1597,7 @@ public class Launcher extends StatefulActivity } /** Handle animating away split placeholder view when user taps on home button */ - protected void handleSplitAnimationGoingToHome() { + protected void handleSplitAnimationGoingToHome(EventEnum splitDismissReason) { // Overridden } @@ -2667,7 +2668,7 @@ public class Launcher extends StatefulActivity } /** Call to dismiss the intermediary split selection state. */ - public void dismissSplitSelection() { + public void dismissSplitSelection(StatsLogManager.LauncherEvent splitDismissEvent) { // Overridden; move this into ActivityContext if necessary for Taskbar } diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 2a0f0302e4..1447d05a98 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -199,6 +199,11 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "User tapped on app info system shortcut.") LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP(515), + /** + * @deprecated Use {@link #LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP} or + * {@link #LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM} + */ + @Deprecated @UiEvent(doc = "User tapped on split screen icon on a task menu.") LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP(518), @@ -722,6 +727,27 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "User tapped on private space uninstall system shortcut.") LAUNCHER_PRIVATE_SPACE_UNINSTALL_SYSTEM_SHORTCUT_TAP(1608), + @UiEvent(doc = "User initiated split selection") + LAUNCHER_SPLIT_SELECTION_INITIATED(1618), + + @UiEvent(doc = "User finished a split selection session") + LAUNCHER_SPLIT_SELECTION_COMPLETE(1619), + + @UiEvent(doc = "User selected both apps for split screen") + LAUNCHER_SPLIT_SELECTED_SECOND_APP(1609), + + @UiEvent(doc = "User exited split selection by going home via swipe, button, or state " + + "transition") + LAUNCHER_SPLIT_SELECTION_EXIT_HOME(1610), + + @UiEvent(doc = "User exited split selection by tapping cancel in split instructions view") + LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON(1611), + + @UiEvent(doc = "User exited split selection when another activity/app came to foreground" + + " after first app had been selected OR if user long-pressed on home. Default exit" + + " metric.") + LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED(1612), + // ADD MORE ; diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java index 8c43f75c08..0ff10c26a5 100644 --- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java +++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java @@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_CLOSE_TAP_OUTSIDE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_LONGPRESS; import android.graphics.PointF; @@ -207,7 +208,7 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS); mLauncher.showDefaultOptions(mTouchDownPoint.x, mTouchDownPoint.y); if (FeatureFlags.enableSplitContextually() && mLauncher.isSplitSelectionActive()) { - mLauncher.dismissSplitSelection(); + mLauncher.dismissSplitSelection(LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED); } } else { cancelLongPress();