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:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user