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
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index bb2ac73..b6e93bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -260,7 +260,8 @@
taskAttributes.getThumbnailView(),
taskAttributes.getThumbnailView().getThumbnail(),
null /* intent */,
- null /* user */);
+ null /* user */,
+ info);
return;
}
}
@@ -273,7 +274,8 @@
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 9329e16..dbaeafb 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.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 @@
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 c2a248d..fbbbdd0 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.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 @@
list.add(getDragController());
Consumer<AnimatorSet> 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 @@
// 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 @@
}
@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 extends OverviewActionsView> T getActionsView() {
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index 839320e..0be65ca 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -162,10 +162,10 @@
@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 ad9f5ea..76b3d7b 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.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 @@
}
/** 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 @@
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 c013483..06edb14 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
@@ -87,6 +87,7 @@
@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 @@
* 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 @@
* @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 @@
* 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 @@
* 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 @@
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 3c90e0c..f06418b 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -19,6 +19,10 @@
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 @@
private SplitInstructionsView mSplitInstructionsView;
private final List<SplitSelectionListener> 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<InstanceId, com.android.launcher3.logging.InstanceId> mSessionInstanceIds;
private final BackPressHandler mSplitBackHandler = new BackPressHandler() {
@Override
@@ -162,7 +171,8 @@
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 @@
int alreadyRunningTask) {
mSplitSelectDataHolder.setInitialTaskSelect(intent, stagePosition, itemInfo, splitEvent,
alreadyRunningTask);
+ createAndLogInstanceIdsForSession();
}
/**
@@ -213,6 +224,7 @@
@StagePosition int stagePosition, @NonNull ItemInfo itemInfo,
StatsLogManager.EventEnum splitEvent) {
mSplitSelectDataHolder.setInitialTaskSelect(info, stagePosition, itemInfo, splitEvent);
+ createAndLogInstanceIdsForSession();
}
/**
@@ -330,14 +342,12 @@
*/
public void launchSplitTasks(@PersistentSnapPosition int snapPosition,
@Nullable Consumer<Boolean> callback) {
- Pair<InstanceId, com.android.launcher3.logging.InstanceId> 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);
}
/**
@@ -364,11 +374,26 @@
}
/**
+ * 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 @@
* @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 @@
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 @@
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 @@
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 50a5a83..445a540 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.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 @@
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 9bb9775a..3f1d343 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_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 @@
stateManager.goToState(NORMAL, animated);
if (FeatureFlags.enableSplitContextually()) {
mSplitSelectStateController.getSplitAnimationController()
- .playPlaceholderDismissAnim(mActivity);
+ .playPlaceholderDismissAnim(mActivity, LAUNCHER_SPLIT_SELECTION_EXIT_HOME);
}
AbstractFloatingView.closeAllOpenViews(mActivity, animated);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9884d8d..a130511 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -150,6 +150,7 @@
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -4793,7 +4794,8 @@
* false otherwise
*/
public boolean confirmSplitSelect(TaskView containerTaskView, Task task, Drawable drawable,
- View secondView, @Nullable Bitmap thumbnail, Intent intent, UserHandle user) {
+ View secondView, @Nullable Bitmap thumbnail, Intent intent, UserHandle user,
+ ItemInfo itemInfo) {
if (canLaunchFullscreenTask()) {
return false;
}
@@ -4812,9 +4814,9 @@
+ ") is not dockable / does not support splitscreen"));
return true;
}
- mSplitSelectStateController.setSecondTask(task);
+ mSplitSelectStateController.setSecondTask(task, itemInfo);
} else {
- mSplitSelectStateController.setSecondTask(intent, user);
+ mSplitSelectStateController.setSecondTask(intent, user, itemInfo);
}
RectF secondTaskStartingBounds = new RectF();
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index c4b93b7..a11a913 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -16,6 +16,8 @@
package com.android.quickstep.views;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
@@ -39,6 +41,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.quickstep.util.SplitSelectStateController;
/**
* A rounded rectangular component containing a single TextView.
@@ -130,8 +133,11 @@
}
private void exitSplitSelection() {
- ((RecentsView) mLauncher.getOverviewPanel()).getSplitSelectController()
- .getSplitAnimationController().playPlaceholderDismissAnim(mLauncher);
+ SplitSelectStateController splitSelectController =
+ ((RecentsView) mLauncher.getOverviewPanel()).getSplitSelectController();
+
+ splitSelectController.getSplitAnimationController().playPlaceholderDismissAnim(mLauncher,
+ LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON);
mLauncher.getStateManager().goToState(LauncherState.NORMAL);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 5057c38..05cd8f8 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -835,7 +835,7 @@
return getRecentsView().confirmSplitSelect(this, container.getTask(),
container.getIconView().getDrawable(), container.getThumbnailView(),
container.getThumbnailView().getThumbnail(), /* intent */ null,
- /* user */ null);
+ /* user */ null, container.getItemInfo());
}
return false;
}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt
index c6c5be4..b4f1692 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectDataHolderTest.kt
@@ -60,6 +60,7 @@
private val sampleShortcut = Intent()
private val sampleShortcut2 = Intent()
private val sampleItemInfo = ItemInfo()
+ private val sampleItemInfo2 = ItemInfo()
private val samplePackage =
AbstractLauncherUiTest.resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)
@@ -133,7 +134,7 @@
null,
INVALID_TASK_ID
)
- splitSelectDataHolder.setSecondTask(sampleTaskId)
+ splitSelectDataHolder.setSecondTask(sampleTaskId, sampleItemInfo2)
assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed())
}
@@ -145,7 +146,7 @@
null,
null
)
- splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser)
+ splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser, sampleItemInfo2)
assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed())
}
@@ -158,7 +159,7 @@
null,
INVALID_TASK_ID
)
- splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser)
+ splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser, sampleItemInfo2)
assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed())
}
@@ -170,7 +171,7 @@
sampleItemInfo,
null
)
- splitSelectDataHolder.setSecondTask(sampleTaskId2)
+ splitSelectDataHolder.setSecondTask(sampleTaskId2, sampleItemInfo2)
val launchData = splitSelectDataHolder.getSplitLaunchData()
assertEquals(launchData.splitLaunchType, SPLIT_TASK_TASK)
@@ -194,7 +195,7 @@
sampleItemInfo,
null
)
- splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser)
+ splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser, sampleItemInfo2)
val launchData = splitSelectDataHolder.getSplitLaunchData()
assertEquals(launchData.splitLaunchType, SPLIT_TASK_PENDINGINTENT)
@@ -218,7 +219,7 @@
sampleItemInfo,
null
)
- splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser)
+ splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser, sampleItemInfo2)
val launchData = splitSelectDataHolder.getSplitLaunchData()
assertEquals(launchData.splitLaunchType, SPLIT_TASK_SHORTCUT)
@@ -243,7 +244,7 @@
null,
INVALID_TASK_ID
)
- splitSelectDataHolder.setSecondTask(sampleTaskId)
+ splitSelectDataHolder.setSecondTask(sampleTaskId, sampleItemInfo2)
val launchData = splitSelectDataHolder.getSplitLaunchData()
assertEquals(launchData.splitLaunchType, SPLIT_PENDINGINTENT_TASK)
@@ -268,7 +269,7 @@
null,
INVALID_TASK_ID
)
- splitSelectDataHolder.setSecondTask(sampleTaskId)
+ splitSelectDataHolder.setSecondTask(sampleTaskId, sampleItemInfo2)
val launchData = splitSelectDataHolder.getSplitLaunchData()
assertEquals(launchData.splitLaunchType, SPLIT_SHORTCUT_TASK)
@@ -293,7 +294,7 @@
null,
INVALID_TASK_ID
)
- splitSelectDataHolder.setSecondTask(sampleIntent2, sampleUser)
+ splitSelectDataHolder.setSecondTask(sampleIntent2, sampleUser, sampleItemInfo2)
val launchData = splitSelectDataHolder.getSplitLaunchData()
assertEquals(launchData.splitLaunchType, SPLIT_PENDINGINTENT_PENDINGINTENT)
@@ -388,7 +389,7 @@
null,
null
)
- splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser)
+ splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser, sampleItemInfo2)
splitSelectDataHolder.resetState()
assertFalse(splitSelectDataHolder.isSplitSelectActive())
}
@@ -402,7 +403,7 @@
null,
INVALID_TASK_ID
)
- splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser)
+ splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser, sampleItemInfo2)
splitSelectDataHolder.resetState()
assertFalse(splitSelectDataHolder.isSplitSelectActive())
}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index 1e39a34..18b1ea0 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -27,6 +27,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.LauncherState
import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.logging.StatsLogManager.StatsLogger
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.statehandlers.DepthController
import com.android.launcher3.statemanager.StateManager
@@ -45,6 +46,8 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.any
+import org.mockito.Mockito.`when`
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
@@ -56,13 +59,14 @@
private val systemUiProxy: SystemUiProxy = mock()
private val depthController: DepthController = mock()
private val statsLogManager: StatsLogManager = mock()
+ private val statsLogger: StatsLogger = mock()
private val stateManager: StateManager<LauncherState> = 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 @@
@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 @@
@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 496cb4e..39b3fbf 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.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 @@
}
if (FeatureFlags.enableSplitContextually()) {
- handleSplitAnimationGoingToHome();
+ handleSplitAnimationGoingToHome(LAUNCHER_SPLIT_SELECTION_EXIT_HOME);
}
mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
handleGestureContract(intent);
@@ -1596,7 +1597,7 @@
}
/** 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 @@
}
/** 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 2a0f030..1447d05 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -199,6 +199,11 @@
@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 @@
@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 8c43f75..0ff10c2 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -24,6 +24,7 @@
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 @@
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();