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
This commit is contained in:
Vinit Nayak
2024-02-13 17:55:08 -08:00
parent 9ac91ffa63
commit 5a9d4af170
17 changed files with 182 additions and 62 deletions
@@ -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);
}
);
}
@@ -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<T extends RecentsView>
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,
@@ -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<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 @@ 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 extends OverviewActionsView> T getActionsView() {
@@ -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);
@@ -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
}
@@ -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
@@ -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<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 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<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);
}
/**
@@ -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;
}
/**
@@ -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;
@@ -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<QuickstepLauncher, Launcher
stateManager.goToState(NORMAL, animated);
if (FeatureFlags.enableSplitContextually()) {
mSplitSelectStateController.getSplitAnimationController()
.playPlaceholderDismissAnim(mActivity);
.playPlaceholderDismissAnim(mActivity, LAUNCHER_SPLIT_SELECTION_EXIT_HOME);
}
AbstractFloatingView.closeAllOpenViews(mActivity, animated);
}
@@ -150,6 +150,7 @@ import com.android.launcher3.config.FeatureFlags;
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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* 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 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
+ ") 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();
@@ -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.Utilities;
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 @@ public class SplitInstructionsView extends LinearLayout {
}
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);
}
@@ -835,7 +835,7 @@ public class TaskView extends FrameLayout implements Reusable {
return getRecentsView().confirmSplitSelect(this, container.getTask(),
container.getIconView().getDrawable(), container.getThumbnailView(),
container.getThumbnailView().getThumbnail(), /* intent */ null,
/* user */ null);
/* user */ null, container.getItemInfo());
}
return false;
}
@@ -60,6 +60,7 @@ class SplitSelectDataHolderTest {
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 @@ class SplitSelectDataHolderTest {
null,
INVALID_TASK_ID
)
splitSelectDataHolder.setSecondTask(sampleTaskId)
splitSelectDataHolder.setSecondTask(sampleTaskId, sampleItemInfo2)
assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed())
}
@@ -145,7 +146,7 @@ class SplitSelectDataHolderTest {
null,
null
)
splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser)
splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser, sampleItemInfo2)
assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed())
}
@@ -158,7 +159,7 @@ class SplitSelectDataHolderTest {
null,
INVALID_TASK_ID
)
splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser)
splitSelectDataHolder.setSecondTask(sampleShortcut, sampleUser, sampleItemInfo2)
assertTrue(splitSelectDataHolder.isBothSplitAppsConfirmed())
}
@@ -170,7 +171,7 @@ class SplitSelectDataHolderTest {
sampleItemInfo,
null
)
splitSelectDataHolder.setSecondTask(sampleTaskId2)
splitSelectDataHolder.setSecondTask(sampleTaskId2, sampleItemInfo2)
val launchData = splitSelectDataHolder.getSplitLaunchData()
assertEquals(launchData.splitLaunchType, SPLIT_TASK_TASK)
@@ -194,7 +195,7 @@ class SplitSelectDataHolderTest {
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 @@ class SplitSelectDataHolderTest {
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 @@ class SplitSelectDataHolderTest {
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 @@ class SplitSelectDataHolderTest {
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 @@ class SplitSelectDataHolderTest {
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 @@ class SplitSelectDataHolderTest {
null,
null
)
splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser)
splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser, sampleItemInfo2)
splitSelectDataHolder.resetState()
assertFalse(splitSelectDataHolder.isSplitSelectActive())
}
@@ -402,7 +403,7 @@ class SplitSelectDataHolderTest {
null,
INVALID_TASK_ID
)
splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser)
splitSelectDataHolder.setSecondTask(sampleIntent, sampleUser, sampleItemInfo2)
splitSelectDataHolder.resetState()
assertFalse(splitSelectDataHolder.isSplitSelectActive())
}
@@ -27,6 +27,7 @@ import android.os.UserHandle
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.Assert.assertTrue
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 @@ class SplitSelectStateControllerTest {
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 @@ 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)
}
+4 -3
View File
@@ -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<LauncherState>
}
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<LauncherState>
}
/** 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<LauncherState>
}
/** 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
}
@@ -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
;
@@ -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();