Merge "Cleanup unused workspacePageIndicator" into main
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index d973149..e940553 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
+import android.util.Log;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.RemoteAnimationTarget;
@@ -196,6 +197,7 @@
if (skipFirstFrame) {
// Because t=0 has the app icon in its original spot, we can skip the
// first frame and have the same movement one frame earlier.
+ Log.d("b/311077782", "LauncherAnimationRunner.setAnimation");
mAnimator.setCurrentPlayTime(
Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
index 94e2244..caf3320 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
@@ -47,6 +47,7 @@
private final int[] mTmpArr = new int[2];
private @Nullable ObjectAnimator mColorChangeAnim;
+ private Boolean mIsRegionDark;
public StashedHandleView(Context context) {
this(context, null);
@@ -95,7 +96,11 @@
* @param animate Whether to animate the change, or apply it immediately.
*/
public void updateHandleColor(boolean isRegionDark, boolean animate) {
+ if (mIsRegionDark != null && mIsRegionDark == isRegionDark) {
+ return;
+ }
int newColor = isRegionDark ? mStashedHandleLightColor : mStashedHandleDarkColor;
+ mIsRegionDark = isRegionDark;
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 1471234..487bc54 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -186,6 +186,7 @@
private final WindowManager mWindowManager;
private DeviceProfile mDeviceProfile;
private WindowManager.LayoutParams mWindowLayoutParams;
+ private WindowManager.LayoutParams mLastUpdatedLayoutParams;
private boolean mIsFullscreen;
// The size we should return to when we call setTaskbarWindowFullscreen(false)
private int mLastRequestedNonFullscreenSize;
@@ -442,6 +443,7 @@
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, getResources(), false);
mLastRequestedNonFullscreenSize = getDefaultTaskbarWindowSize();
mWindowLayoutParams = createAllWindowParams();
+ mLastUpdatedLayoutParams = new WindowManager.LayoutParams();
// Initialize controllers after all are constructed.
mControllers.init(sharedState);
@@ -1727,6 +1729,12 @@
void notifyUpdateLayoutParams() {
if (mDragLayer.isAttachedToWindow()) {
+ // Copy the current windowLayoutParams to mLastUpdatedLayoutParams and compare the diff.
+ // If there is no change, we will skip the call to updateViewLayout.
+ int changes = mLastUpdatedLayoutParams.copyFrom(mWindowLayoutParams);
+ if (changes == 0) {
+ return;
+ }
if (enableTaskbarNoRecreate()) {
mWindowManager.updateViewLayout(mDragLayer.getRootView(), mWindowLayoutParams);
} else {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
deleted file mode 100644
index 8f533a3..0000000
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.PagedView.INVALID_PAGE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.BinderThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
-import com.android.internal.jank.Cuj;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logger.LauncherAtom;
-import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.taskbar.TaskbarUIController;
-import com.android.launcher3.util.RunnableList;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.RecentsViewContainer;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * Helper class to handle various atomic commands for switching between Overview.
- */
-public class OverviewCommandHelper {
- private static final String TAG = "OverviewCommandHelper";
-
- public static final int TYPE_SHOW = 1;
- public static final int TYPE_KEYBOARD_INPUT = 2;
- public static final int TYPE_HIDE = 3;
- public static final int TYPE_TOGGLE = 4;
- public static final int TYPE_HOME = 5;
-
- /**
- * Use case for needing a queue is double tapping recents button in 3 button nav.
- * Size of 2 should be enough. We'll toss in one more because we're kind hearted.
- */
- private final static int MAX_QUEUE_SIZE = 3;
-
- private static final String TRANSITION_NAME = "Transition:toOverview";
-
- private final TouchInteractionService mService;
- private final OverviewComponentObserver mOverviewComponentObserver;
- private final TaskAnimationManager mTaskAnimationManager;
- private final ArrayList<CommandInfo> mPendingCommands = new ArrayList<>();
-
- /**
- * Index of the TaskView that should be focused when launching Overview. Persisted so that we
- * do not lose the focus across multiple calls of
- * {@link OverviewCommandHelper#executeCommand(CommandInfo)} for the same command
- */
- private int mKeyboardTaskFocusIndex = -1;
-
- /**
- * Whether we should incoming toggle commands while a previous toggle command is still ongoing.
- * This serves as a rate-limiter to prevent overlapping animations that can clobber each other
- * and prevent clean-up callbacks from running. This thus prevents a recurring set of bugs with
- * janky recents animations and unresponsive home and overview buttons.
- */
- private boolean mWaitForToggleCommandComplete = false;
-
- public OverviewCommandHelper(TouchInteractionService service,
- OverviewComponentObserver observer,
- TaskAnimationManager taskAnimationManager) {
- mService = service;
- mOverviewComponentObserver = observer;
- mTaskAnimationManager = taskAnimationManager;
- }
-
- /**
- * Called when the command finishes execution.
- */
- private void scheduleNextTask(CommandInfo command) {
- if (mPendingCommands.isEmpty()) {
- Log.d(TAG, "no pending commands to schedule");
- return;
- }
- if (mPendingCommands.get(0) != command) {
- Log.d(TAG, "next task not scheduled."
- + " mPendingCommands[0] type is " + mPendingCommands.get(0)
- + " - command type is: " + command);
- return;
- }
- Log.d(TAG, "scheduleNextTask called: " + command);
- mPendingCommands.remove(0);
- executeNext();
- }
-
- /**
- * Executes the next command from the queue. If the command finishes immediately (returns true),
- * it continues to execute the next command, until the queue is empty of a command defer's its
- * completion (returns false).
- */
- @UiThread
- private void executeNext() {
- if (mPendingCommands.isEmpty()) {
- Log.d(TAG, "executeNext - mPendingCommands is empty");
- return;
- }
- CommandInfo cmd = mPendingCommands.get(0);
-
- boolean result = executeCommand(cmd);
- Log.d(TAG, "executeNext cmd type: " + cmd + ", result: " + result);
- if (result) {
- scheduleNextTask(cmd);
- }
- }
-
- @UiThread
- private void addCommand(CommandInfo cmd) {
- boolean wasEmpty = mPendingCommands.isEmpty();
- mPendingCommands.add(cmd);
- if (wasEmpty) {
- executeNext();
- }
- }
-
- /**
- * Adds a command to be executed next, after all pending tasks are completed.
- * Max commands that can be queued is {@link #MAX_QUEUE_SIZE}.
- * Requests after reaching that limit will be silently dropped.
- */
- @BinderThread
- public void addCommand(int type) {
- if (mPendingCommands.size() >= MAX_QUEUE_SIZE) {
- Log.d(TAG, "the pending command queue is full (" + mPendingCommands.size() + "). "
- + "command not added: " + type);
- return;
- }
- Log.d(TAG, "adding command type: " + type);
- CommandInfo cmd = new CommandInfo(type);
- MAIN_EXECUTOR.execute(() -> addCommand(cmd));
- }
-
- @UiThread
- public void clearPendingCommands() {
- Log.d(TAG, "clearing pending commands - size: " + mPendingCommands.size());
- mPendingCommands.clear();
- }
-
- @UiThread
- public boolean canStartHomeSafely() {
- return mPendingCommands.isEmpty() || mPendingCommands.get(0).type == TYPE_HOME;
- }
-
- @Nullable
- private TaskView getNextTask(RecentsView view) {
- final TaskView runningTaskView = view.getRunningTaskView();
-
- if (runningTaskView == null) {
- return view.getTaskViewAt(0);
- } else {
- final TaskView nextTask = view.getNextTaskView();
- return nextTask != null ? nextTask : runningTaskView;
- }
- }
-
- private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
- RunnableList callbackList = null;
- if (taskView != null) {
- mWaitForToggleCommandComplete = true;
- taskView.setEndQuickSwitchCuj(true);
- callbackList = taskView.launchTasks();
- }
-
- if (callbackList != null) {
- callbackList.add(() -> {
- Log.d(TAG, "launching task callback: " + cmd);
- scheduleNextTask(cmd);
- mWaitForToggleCommandComplete = false;
- });
- Log.d(TAG, "launching task - waiting for callback: " + cmd);
- return false;
- } else {
- recents.startHome();
- mWaitForToggleCommandComplete = false;
- return true;
- }
- }
-
- /**
- * Executes the task and returns true if next task can be executed. If false, then the next
- * task is deferred until {@link #scheduleNextTask} is called
- */
- private <T extends StatefulActivity<?> & RecentsViewContainer> boolean executeCommand(
- CommandInfo cmd) {
- if (mWaitForToggleCommandComplete && cmd.type == TYPE_TOGGLE) {
- Log.d(TAG, "executeCommand: " + cmd
- + " - waiting for toggle command complete");
- return true;
- }
- BaseActivityInterface<?, T> activityInterface =
- mOverviewComponentObserver.getActivityInterface();
-
- RecentsView<?, ?> visibleRecentsView = activityInterface.getVisibleRecentsView();
- RecentsView<?, ?> createdRecentsView;
-
- Log.d(TAG, "executeCommand: " + cmd
- + " - visibleRecentsView: " + visibleRecentsView);
- if (visibleRecentsView == null) {
- T activity = activityInterface.getCreatedContainer();
- createdRecentsView = activity == null ? null : activity.getOverviewPanel();
- DeviceProfile dp = activity == null ? null : activity.getDeviceProfile();
- TaskbarUIController uiController = activityInterface.getTaskbarController();
- boolean allowQuickSwitch = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
- && uiController != null
- && dp != null
- && (dp.isTablet || dp.isTwoPanels);
-
- switch (cmd.type) {
- case TYPE_HIDE:
- if (!allowQuickSwitch) {
- return true;
- }
- mKeyboardTaskFocusIndex = uiController.launchFocusedTask();
- if (mKeyboardTaskFocusIndex == -1) {
- return true;
- }
- break;
- case TYPE_KEYBOARD_INPUT:
- if (allowQuickSwitch) {
- uiController.openQuickSwitchView();
- return true;
- } else {
- mKeyboardTaskFocusIndex = 0;
- break;
- }
- case TYPE_HOME:
- ActiveGestureLog.INSTANCE.addLog(
- "OverviewCommandHelper.executeCommand(TYPE_HOME)");
- // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
- // we should still call it on main thread because launcher is waiting for
- // ActivityTaskManager to resume it. Also calling startActivity() on bg thread
- // could potentially delay resuming launcher. See b/348668521 for more details.
- mService.startActivity(mOverviewComponentObserver.getHomeIntent());
- return true;
- case TYPE_SHOW:
- // When Recents is not currently visible, the command's type is TYPE_SHOW
- // when overview is triggered via the keyboard overview button or Action+Tab
- // keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button
- // nav is TYPE_TOGGLE.
- mKeyboardTaskFocusIndex = 0;
- break;
- default:
- // continue below to handle displaying Recents.
- }
- } else {
- createdRecentsView = visibleRecentsView;
- switch (cmd.type) {
- case TYPE_SHOW:
- // already visible
- return true;
- case TYPE_KEYBOARD_INPUT: {
- if (visibleRecentsView.isHandlingTouch()) {
- return true;
- }
- }
- case TYPE_HIDE: {
- if (visibleRecentsView.isHandlingTouch()) {
- return true;
- }
- mKeyboardTaskFocusIndex = INVALID_PAGE;
- int currentPage = visibleRecentsView.getNextPage();
- TaskView tv = (currentPage >= 0
- && currentPage < visibleRecentsView.getTaskViewCount())
- ? (TaskView) visibleRecentsView.getPageAt(currentPage)
- : null;
- return launchTask(visibleRecentsView, tv, cmd);
- }
- case TYPE_TOGGLE:
- return launchTask(visibleRecentsView, getNextTask(visibleRecentsView), cmd);
- case TYPE_HOME:
- visibleRecentsView.startHome();
- return true;
- }
- }
-
- if (createdRecentsView != null) {
- createdRecentsView.setKeyboardTaskFocusIndex(mKeyboardTaskFocusIndex);
- }
- // Handle recents view focus when launching from home
- Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- updateRecentsViewFocus(cmd);
- logShowOverviewFrom(cmd.type);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- Log.d(TAG, "switching to Overview state - onAnimationEnd: " + cmd);
- super.onAnimationEnd(animation);
- onRecentsViewFocusUpdated(cmd);
- scheduleNextTask(cmd);
- }
- };
- if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
- Log.d(TAG, "switching to Overview state - waiting: " + cmd);
- // If successfully switched, wait until animation finishes
- return false;
- }
-
- final T activity = activityInterface.getCreatedContainer();
- if (activity != null) {
- InteractionJankMonitorWrapper.begin(
- activity.getRootView(),
- Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
- }
-
- GestureState gestureState = mService.createGestureState(GestureState.DEFAULT_STATE,
- GestureState.TrackpadGestureType.NONE);
- gestureState.setHandlingAtomicEvent(true);
- AbsSwipeUpHandler interactionHandler = mService.getSwipeUpHandlerFactory()
- .newHandler(gestureState, cmd.createTime);
- interactionHandler.setGestureEndCallback(
- () -> onTransitionComplete(cmd, interactionHandler));
- interactionHandler.initWhenReady("OverviewCommandHelper: cmd.type=" + cmd.type);
-
- RecentsAnimationListener recentAnimListener = new RecentsAnimationListener() {
- @Override
- public void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
- updateRecentsViewFocus(cmd);
- logShowOverviewFrom(cmd.type);
- activityInterface.runOnInitBackgroundStateUI(() ->
- interactionHandler.onGestureEnded(0, new PointF()));
- cmd.removeListener(this);
- }
-
- @Override
- public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
- interactionHandler.onGestureCancelled();
- cmd.removeListener(this);
-
- T createdActivity = activityInterface.getCreatedContainer();
- if (createdActivity == null) {
- return;
- }
- if (createdRecentsView != null) {
- createdRecentsView.onRecentsAnimationComplete();
- }
- }
- };
-
- if (visibleRecentsView != null) {
- visibleRecentsView.moveRunningTaskToFront();
- }
- if (mTaskAnimationManager.isRecentsAnimationRunning()) {
- cmd.mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(gestureState);
- cmd.mActiveCallbacks.addListener(interactionHandler);
- mTaskAnimationManager.notifyRecentsAnimationState(interactionHandler);
- interactionHandler.onGestureStarted(true /*isLikelyToStartNewTask*/);
-
- cmd.mActiveCallbacks.addListener(recentAnimListener);
- mTaskAnimationManager.notifyRecentsAnimationState(recentAnimListener);
- } else {
- Intent intent = new Intent(interactionHandler.getLaunchIntent());
- intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
- cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
- gestureState, intent, interactionHandler);
- interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
- cmd.mActiveCallbacks.addListener(recentAnimListener);
- }
- Trace.beginAsyncSection(TRANSITION_NAME, 0);
- Log.d(TAG, "switching via recents animation - onGestureStarted: " + cmd);
- return false;
- }
-
- private void onTransitionComplete(CommandInfo cmd, AbsSwipeUpHandler handler) {
- Log.d(TAG, "switching via recents animation - onTransitionComplete: " + cmd);
- cmd.removeListener(handler);
- Trace.endAsyncSection(TRANSITION_NAME, 0);
- onRecentsViewFocusUpdated(cmd);
- scheduleNextTask(cmd);
- }
-
- private void updateRecentsViewFocus(CommandInfo cmd) {
- RecentsView recentsView =
- mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
- if (recentsView == null || (cmd.type != TYPE_KEYBOARD_INPUT && cmd.type != TYPE_HIDE
- && cmd.type != TYPE_SHOW)) {
- return;
- }
- // When the overview is launched via alt tab (cmd type is TYPE_KEYBOARD_INPUT),
- // the touch mode somehow is not change to false by the Android framework.
- // The subsequent tab to go through tasks in overview can only be dispatched to
- // focuses views, while focus can only be requested in
- // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
- // here we launch overview with live tile.
- recentsView.getViewRootImpl().touchModeChanged(false);
- // Ensure that recents view has focus so that it receives the followup key inputs
- if (requestFocus(recentsView.getTaskViewAt(mKeyboardTaskFocusIndex))) {
- return;
- }
- if (requestFocus(recentsView.getNextTaskView())) {
- return;
- }
- if (requestFocus(recentsView.getTaskViewAt(0))) {
- return;
- }
- requestFocus(recentsView);
- }
-
- private void onRecentsViewFocusUpdated(CommandInfo cmd) {
- RecentsView recentsView =
- mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
- if (recentsView == null
- || cmd.type != TYPE_HIDE
- || mKeyboardTaskFocusIndex == INVALID_PAGE) {
- return;
- }
- recentsView.setKeyboardTaskFocusIndex(INVALID_PAGE);
- recentsView.setCurrentPage(mKeyboardTaskFocusIndex);
- mKeyboardTaskFocusIndex = INVALID_PAGE;
- }
-
- private boolean requestFocus(@Nullable View taskView) {
- if (taskView == null) {
- return false;
- }
- taskView.post(() -> {
- taskView.requestFocus();
- taskView.requestAccessibilityFocus();
- });
- return true;
- }
-
- private <T extends StatefulActivity<?> & RecentsViewContainer>
- void logShowOverviewFrom(int cmdType) {
- BaseActivityInterface<?, T> activityInterface =
- mOverviewComponentObserver.getActivityInterface();
- var container = activityInterface.getCreatedContainer();
- if (container != null) {
- StatsLogManager.LauncherEvent event;
- switch (cmdType) {
- case TYPE_SHOW -> event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT;
- case TYPE_HIDE ->
- event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH;
- case TYPE_TOGGLE -> event = LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON;
- default -> {
- return;
- }
- }
-
- StatsLogManager.newInstance(container.asContext())
- .logger()
- .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
- .setTaskSwitcherContainer(
- LauncherAtom.TaskSwitcherContainer.getDefaultInstance())
- .build())
- .log(event);
- }
- }
-
- public void dump(PrintWriter pw) {
- pw.println("OverviewCommandHelper:");
- pw.println(" mPendingCommands=" + mPendingCommands.size());
- if (!mPendingCommands.isEmpty()) {
- pw.println(" pendingCommandType=" + mPendingCommands.get(0).type);
- }
- pw.println(" mKeyboardTaskFocusIndex=" + mKeyboardTaskFocusIndex);
- pw.println(" mWaitForToggleCommandComplete=" + mWaitForToggleCommandComplete);
- }
-
- private static class CommandInfo {
- public final long createTime = SystemClock.elapsedRealtime();
- public final int type;
- RecentsAnimationCallbacks mActiveCallbacks;
-
- CommandInfo(int type) {
- this.type = type;
- }
-
- void removeListener(RecentsAnimationListener listener) {
- if (mActiveCallbacks != null) {
- mActiveCallbacks.removeListener(listener);
- }
- }
-
- @NonNull
- @Override
- public String toString() {
- return "CommandInfo("
- + "type=" + type + ", "
- + "createTime=" + createTime + ", "
- + "mActiveCallbacks=" + mActiveCallbacks
- + ")";
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
new file mode 100644
index 0000000..f6b9e4e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.content.Intent
+import android.graphics.PointF
+import android.os.SystemClock
+import android.os.Trace
+import android.util.Log
+import android.view.View
+import androidx.annotation.BinderThread
+import androidx.annotation.UiThread
+import com.android.internal.jank.Cuj
+import com.android.launcher3.PagedView
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.logger.LauncherAtom
+import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.*
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.RunnableList
+import com.android.quickstep.util.ActiveGestureLog
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskView
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper
+import java.io.PrintWriter
+
+/** Helper class to handle various atomic commands for switching between Overview. */
+class OverviewCommandHelper(
+ private val touchInteractionService: TouchInteractionService,
+ private val overviewComponentObserver: OverviewComponentObserver,
+ private val taskAnimationManager: TaskAnimationManager
+) {
+ private val pendingCommands = mutableListOf<CommandInfo>()
+
+ /**
+ * Index of the TaskView that should be focused when launching Overview. Persisted so that we do
+ * not lose the focus across multiple calls of [OverviewCommandHelper.executeCommand] for the
+ * same command
+ */
+ private var keyboardTaskFocusIndex = -1
+
+ /**
+ * Whether we should incoming toggle commands while a previous toggle command is still ongoing.
+ * This serves as a rate-limiter to prevent overlapping animations that can clobber each other
+ * and prevent clean-up callbacks from running. This thus prevents a recurring set of bugs with
+ * janky recents animations and unresponsive home and overview buttons.
+ */
+ private var waitForToggleCommandComplete = false
+
+ /** Called when the command finishes execution. */
+ private fun scheduleNextTask(command: CommandInfo) {
+ if (pendingCommands.isEmpty()) {
+ Log.d(TAG, "no pending commands to schedule")
+ return
+ }
+ if (pendingCommands.first() !== command) {
+ Log.d(
+ TAG,
+ "next task not scheduled. First pending command type " +
+ "is ${pendingCommands.first()} - command type is: $command"
+ )
+ return
+ }
+ Log.d(TAG, "scheduleNextTask called: $command")
+ pendingCommands.removeFirst()
+ executeNext()
+ }
+
+ /**
+ * Executes the next command from the queue. If the command finishes immediately (returns true),
+ * it continues to execute the next command, until the queue is empty of a command defer's its
+ * completion (returns false).
+ */
+ @UiThread
+ private fun executeNext() {
+ if (pendingCommands.isEmpty()) {
+ Log.d(TAG, "executeNext - pendingCommands is empty")
+ return
+ }
+ val command = pendingCommands.first()
+ val result = executeCommand(command)
+ Log.d(TAG, "executeNext command type: $command, result: $result")
+ if (result) {
+ scheduleNextTask(command)
+ }
+ }
+
+ @UiThread
+ private fun addCommand(command: CommandInfo) {
+ val wasEmpty = pendingCommands.isEmpty()
+ pendingCommands.add(command)
+ if (wasEmpty) {
+ executeNext()
+ }
+ }
+
+ /**
+ * Adds a command to be executed next, after all pending tasks are completed. Max commands that
+ * can be queued is [.MAX_QUEUE_SIZE]. Requests after reaching that limit will be silently
+ * dropped.
+ */
+ @BinderThread
+ fun addCommand(type: Int) {
+ if (pendingCommands.size >= MAX_QUEUE_SIZE) {
+ Log.d(
+ TAG,
+ "the pending command queue is full (${pendingCommands.size}). command not added: $type"
+ )
+ return
+ }
+ Log.d(TAG, "adding command type: $type")
+ val command = CommandInfo(type)
+ Executors.MAIN_EXECUTOR.execute { addCommand(command) }
+ }
+
+ @UiThread
+ fun clearPendingCommands() {
+ Log.d(TAG, "clearing pending commands - size: ${pendingCommands.size}")
+ pendingCommands.clear()
+ }
+
+ @UiThread
+ fun canStartHomeSafely(): Boolean =
+ pendingCommands.isEmpty() || pendingCommands.first().type == TYPE_HOME
+
+ private fun getNextTask(view: RecentsView<*, *>): TaskView? {
+ val runningTaskView = view.runningTaskView
+
+ return if (runningTaskView == null) {
+ view.getTaskViewAt(0)
+ } else {
+ val nextTask = view.nextTaskView
+ nextTask ?: runningTaskView
+ }
+ }
+
+ private fun launchTask(
+ recents: RecentsView<*, *>,
+ taskView: TaskView?,
+ command: CommandInfo
+ ): Boolean {
+ var callbackList: RunnableList? = null
+ if (taskView != null) {
+ waitForToggleCommandComplete = true
+ taskView.isEndQuickSwitchCuj = true
+ callbackList = taskView.launchTasks()
+ }
+
+ if (callbackList != null) {
+ callbackList.add {
+ Log.d(TAG, "launching task callback: $command")
+ scheduleNextTask(command)
+ waitForToggleCommandComplete = false
+ }
+ Log.d(TAG, "launching task - waiting for callback: $command")
+ return false
+ } else {
+ recents.startHome()
+ waitForToggleCommandComplete = false
+ return true
+ }
+ }
+
+ /**
+ * Executes the task and returns true if next task can be executed. If false, then the next task
+ * is deferred until [.scheduleNextTask] is called
+ */
+ private fun executeCommand(command: CommandInfo): Boolean {
+ if (waitForToggleCommandComplete && command.type == TYPE_TOGGLE) {
+ Log.d(TAG, "executeCommand: $command - waiting for toggle command complete")
+ return true
+ }
+ val activityInterface: BaseActivityInterface<*, *> =
+ overviewComponentObserver.activityInterface
+
+ val visibleRecentsView: RecentsView<*, *>? =
+ activityInterface.getVisibleRecentsView<RecentsView<*, *>>()
+ val createdRecentsView: RecentsView<*, *>?
+
+ Log.d(TAG, "executeCommand: $command - visibleRecentsView: $visibleRecentsView")
+ if (visibleRecentsView == null) {
+ val activity = activityInterface.getCreatedContainer() as? RecentsViewContainer
+ createdRecentsView = activity?.getOverviewPanel()
+ val deviceProfile = activity?.getDeviceProfile()
+ val uiController = activityInterface.getTaskbarController()
+ val allowQuickSwitch =
+ FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get() &&
+ uiController != null &&
+ deviceProfile != null &&
+ (deviceProfile.isTablet || deviceProfile.isTwoPanels)
+
+ when (command.type) {
+ TYPE_HIDE -> {
+ if (!allowQuickSwitch) return true
+ keyboardTaskFocusIndex = uiController!!.launchFocusedTask()
+ if (keyboardTaskFocusIndex == -1) return true
+ }
+ TYPE_KEYBOARD_INPUT ->
+ if (allowQuickSwitch) {
+ uiController!!.openQuickSwitchView()
+ return true
+ } else {
+ keyboardTaskFocusIndex = 0
+ }
+ TYPE_HOME -> {
+ ActiveGestureLog.INSTANCE.addLog(
+ "OverviewCommandHelper.executeCommand(TYPE_HOME)"
+ )
+ // Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
+ // we should still call it on main thread because launcher is waiting for
+ // ActivityTaskManager to resume it. Also calling startActivity() on bg thread
+ // could potentially delay resuming launcher. See b/348668521 for more details.
+ touchInteractionService.startActivity(overviewComponentObserver.homeIntent)
+ return true
+ }
+ TYPE_SHOW ->
+ // When Recents is not currently visible, the command's type is
+ // TYPE_SHOW
+ // when overview is triggered via the keyboard overview button or Action+Tab
+ // keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button
+ // nav is TYPE_TOGGLE.
+ keyboardTaskFocusIndex = 0
+ else -> {}
+ }
+ } else {
+ createdRecentsView = visibleRecentsView
+ when (command.type) {
+ TYPE_SHOW -> return true // already visible
+ TYPE_KEYBOARD_INPUT,
+ TYPE_HIDE -> {
+ if (visibleRecentsView.isHandlingTouch) return true
+
+ keyboardTaskFocusIndex = PagedView.INVALID_PAGE
+ val currentPage = visibleRecentsView.nextPage
+ val taskView = visibleRecentsView.getTaskViewAt(currentPage)
+ return launchTask(visibleRecentsView, taskView, command)
+ }
+ TYPE_TOGGLE ->
+ return launchTask(visibleRecentsView, getNextTask(visibleRecentsView), command)
+ TYPE_HOME -> {
+ visibleRecentsView.startHome()
+ return true
+ }
+ }
+ }
+
+ createdRecentsView?.setKeyboardTaskFocusIndex(keyboardTaskFocusIndex)
+ // Handle recents view focus when launching from home
+ val animatorListener: Animator.AnimatorListener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator) {
+ super.onAnimationStart(animation)
+ updateRecentsViewFocus(command)
+ logShowOverviewFrom(command.type)
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ Log.d(TAG, "switching to Overview state - onAnimationEnd: $command")
+ super.onAnimationEnd(animation)
+ onRecentsViewFocusUpdated(command)
+ scheduleNextTask(command)
+ }
+ }
+ if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
+ Log.d(TAG, "switching to Overview state - waiting: $command")
+ // If successfully switched, wait until animation finishes
+ return false
+ }
+
+ val activity = activityInterface.getCreatedContainer()
+ if (activity != null) {
+ InteractionJankMonitorWrapper.begin(activity.rootView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
+ }
+
+ val gestureState =
+ touchInteractionService.createGestureState(
+ GestureState.DEFAULT_STATE,
+ GestureState.TrackpadGestureType.NONE
+ )
+ gestureState.isHandlingAtomicEvent = true
+ val interactionHandler =
+ touchInteractionService.swipeUpHandlerFactory.newHandler(
+ gestureState,
+ command.createTime
+ )
+ interactionHandler.setGestureEndCallback {
+ onTransitionComplete(command, interactionHandler)
+ }
+ interactionHandler.initWhenReady("OverviewCommandHelper: command.type=${command.type}")
+
+ val recentAnimListener: RecentsAnimationCallbacks.RecentsAnimationListener =
+ object : RecentsAnimationCallbacks.RecentsAnimationListener {
+ override fun onRecentsAnimationStart(
+ controller: RecentsAnimationController,
+ targets: RecentsAnimationTargets
+ ) {
+ updateRecentsViewFocus(command)
+ logShowOverviewFrom(command.type)
+ activityInterface.runOnInitBackgroundStateUI {
+ interactionHandler.onGestureEnded(0f, PointF())
+ }
+ command.removeListener(this)
+ }
+
+ override fun onRecentsAnimationCanceled(
+ thumbnailDatas: HashMap<Int, ThumbnailData>
+ ) {
+ interactionHandler.onGestureCancelled()
+ command.removeListener(this)
+
+ activityInterface.getCreatedContainer() ?: return
+ createdRecentsView?.onRecentsAnimationComplete()
+ }
+ }
+
+ // TODO(b/361768912): Dead code. Remove or update after this bug is fixed.
+ // if (visibleRecentsView != null) {
+ // visibleRecentsView.moveRunningTaskToFront();
+ // }
+
+ if (taskAnimationManager.isRecentsAnimationRunning) {
+ command.setAnimationCallbacks(
+ taskAnimationManager.continueRecentsAnimation(gestureState)
+ )
+ command.addListener(interactionHandler)
+ taskAnimationManager.notifyRecentsAnimationState(interactionHandler)
+ interactionHandler.onGestureStarted(true /*isLikelyToStartNewTask*/)
+
+ command.addListener(recentAnimListener)
+ taskAnimationManager.notifyRecentsAnimationState(recentAnimListener)
+ } else {
+ val intent =
+ Intent(interactionHandler.launchIntent)
+ .putExtra(ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID, gestureState.gestureId)
+ command.setAnimationCallbacks(
+ taskAnimationManager.startRecentsAnimation(gestureState, intent, interactionHandler)
+ )
+ interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/)
+ command.addListener(recentAnimListener)
+ }
+ Trace.beginAsyncSection(TRANSITION_NAME, 0)
+ Log.d(TAG, "switching via recents animation - onGestureStarted: $command")
+ return false
+ }
+
+ private fun onTransitionComplete(command: CommandInfo, handler: AbsSwipeUpHandler<*, *, *>) {
+ Log.d(TAG, "switching via recents animation - onTransitionComplete: $command")
+ command.removeListener(handler)
+ Trace.endAsyncSection(TRANSITION_NAME, 0)
+ onRecentsViewFocusUpdated(command)
+ scheduleNextTask(command)
+ }
+
+ private fun updateRecentsViewFocus(command: CommandInfo) {
+ val recentsView: RecentsView<*, *> =
+ overviewComponentObserver.activityInterface.getVisibleRecentsView() ?: return
+ if (
+ command.type != TYPE_KEYBOARD_INPUT &&
+ command.type != TYPE_HIDE &&
+ command.type != TYPE_SHOW
+ ) {
+ return
+ }
+
+ // When the overview is launched via alt tab (command type is TYPE_KEYBOARD_INPUT),
+ // the touch mode somehow is not change to false by the Android framework.
+ // The subsequent tab to go through tasks in overview can only be dispatched to
+ // focuses views, while focus can only be requested in
+ // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
+ // here we launch overview with live tile.
+ recentsView.viewRootImpl.touchModeChanged(false)
+ // Ensure that recents view has focus so that it receives the followup key inputs
+ if (requestFocus(recentsView.getTaskViewAt(keyboardTaskFocusIndex))) return
+ if (requestFocus(recentsView.nextTaskView)) return
+ if (requestFocus(recentsView.getTaskViewAt(0))) return
+ requestFocus(recentsView)
+ }
+
+ private fun onRecentsViewFocusUpdated(command: CommandInfo) {
+ val recentsView: RecentsView<*, *> =
+ overviewComponentObserver.activityInterface.getVisibleRecentsView() ?: return
+ if (command.type != TYPE_HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) {
+ return
+ }
+ recentsView.setKeyboardTaskFocusIndex(PagedView.INVALID_PAGE)
+ recentsView.currentPage = keyboardTaskFocusIndex
+ keyboardTaskFocusIndex = PagedView.INVALID_PAGE
+ }
+
+ private fun requestFocus(taskView: View?): Boolean {
+ if (taskView == null) return false
+ taskView.post {
+ taskView.requestFocus()
+ taskView.requestAccessibilityFocus()
+ }
+ return true
+ }
+
+ private fun logShowOverviewFrom(commandType: Int) {
+ val activityInterface: BaseActivityInterface<*, *> =
+ overviewComponentObserver.activityInterface
+ val container = activityInterface.getCreatedContainer() as? RecentsViewContainer ?: return
+ val event =
+ when (commandType) {
+ TYPE_SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
+ TYPE_HIDE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH
+ TYPE_TOGGLE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON
+ else -> return
+ }
+ StatsLogManager.newInstance(container.asContext())
+ .logger()
+ .withContainerInfo(
+ LauncherAtom.ContainerInfo.newBuilder()
+ .setTaskSwitcherContainer(
+ LauncherAtom.TaskSwitcherContainer.getDefaultInstance()
+ )
+ .build()
+ )
+ .log(event)
+ }
+
+ fun dump(pw: PrintWriter) {
+ pw.println("OverviewCommandHelper:")
+ pw.println(" pendingCommands=${pendingCommands.size}")
+ if (pendingCommands.isNotEmpty()) {
+ pw.println(" pendingCommandType=${pendingCommands.first().type}")
+ }
+ pw.println(" mKeyboardTaskFocusIndex=$keyboardTaskFocusIndex")
+ pw.println(" mWaitForToggleCommandComplete=$waitForToggleCommandComplete")
+ }
+
+ private data class CommandInfo(
+ val type: Int,
+ val createTime: Long = SystemClock.elapsedRealtime(),
+ private var animationCallbacks: RecentsAnimationCallbacks? = null
+ ) {
+ fun setAnimationCallbacks(recentsAnimationCallbacks: RecentsAnimationCallbacks) {
+ this.animationCallbacks = recentsAnimationCallbacks
+ }
+
+ fun addListener(listener: RecentsAnimationCallbacks.RecentsAnimationListener) {
+ animationCallbacks?.addListener(listener)
+ }
+
+ fun removeListener(listener: RecentsAnimationCallbacks.RecentsAnimationListener?) {
+ animationCallbacks?.removeListener(listener)
+ }
+ }
+
+ companion object {
+ private const val TAG = "OverviewCommandHelper"
+
+ const val TYPE_SHOW: Int = 1
+ const val TYPE_KEYBOARD_INPUT: Int = 2
+ const val TYPE_HIDE: Int = 3
+ const val TYPE_TOGGLE: Int = 4
+ const val TYPE_HOME: Int = 5
+
+ /**
+ * Use case for needing a queue is double tapping recents button in 3 button nav. Size of 2
+ * should be enough. We'll toss in one more because we're kind hearted.
+ */
+ private const val MAX_QUEUE_SIZE = 3
+
+ private const val TRANSITION_NAME = "Transition:toOverview"
+ }
+}
diff --git a/res/drawable/add_item_dialog_background.xml b/res/drawable/add_item_dialog_background.xml
index be4765a..39af989 100644
--- a/res/drawable/add_item_dialog_background.xml
+++ b/res/drawable/add_item_dialog_background.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
- <solid android:color="?attr/materialColorSurfaceContainerHighest" />
+ <solid android:color="?attr/widgetPickerPrimarySurfaceColor" />
<corners
android:topLeftRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="?android:attr/dialogCornerRadius" />
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 3edf1f2..7bec768 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -773,6 +773,7 @@
addAnimationStartListeners(anim);
// Because t=0 has the folder match the folder icon, we can skip the
// first frame and have the same movement one frame earlier.
+ Log.d("b/311077782", "Folder.animateOpen");
anim.setCurrentPlayTime(Math.min(getSingleFrameMs(getContext()), anim.getTotalDuration()));
anim.start();
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index 551c2d8..59d1d00 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -192,22 +192,18 @@
}
private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
- final Exception stackTrace = new Exception();
// Queue the item up for adding if launcher has not loaded properly yet
MODEL_EXECUTOR.post(() -> {
Pair<ItemInfo, Object> itemInfo = info.getItemInfo(mContext);
if (itemInfo == null) {
FileLog.d(LOG,
- "Adding PendingInstallShortcutInfo with no attached info to queue.",
- stackTrace);
+ "Adding PendingInstallShortcutInfo with no attached info to queue.");
} else {
FileLog.d(LOG,
- "Adding PendingInstallShortcutInfo to queue. Attached info: "
- + itemInfo.first,
- stackTrace);
+ "Adding PendingInstallShortcutInfo to queue."
+ + " Attached info: " + itemInfo.first);
}
-
addToQueue(info);
});
flushInstallQueue();
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index c117be4..856c294 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -25,6 +25,7 @@
import android.content.pm.PackageInstaller.SessionInfo;
import android.os.Build;
import android.os.UserHandle;
+import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -32,6 +33,7 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.PackageUserKey;
import java.lang.ref.WeakReference;
@@ -41,6 +43,8 @@
@WorkerThread
public class InstallSessionTracker extends PackageInstaller.SessionCallback {
+ public static final String TAG = "InstallSessionTracker";
+
// Lazily initialized
private SparseArray<PackageUserKey> mActiveSessions = null;
@@ -75,6 +79,11 @@
}
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
if (sessionInfo != null) {
+ FileLog.d(TAG, "onCreated: Install session created for"
+ + " appPackageName=" + sessionInfo.getAppPackageName()
+ + ", sessionId=" + sessionInfo.getSessionId()
+ + ", appIcon=" + sessionInfo.getAppIcon()
+ + ", appLabel=" + sessionInfo.getAppLabel());
callback.onInstallSessionCreated(PackageInstallInfo.fromInstallingState(sessionInfo));
}
@@ -102,6 +111,10 @@
activeSessions.remove(sessionId);
if (key != null && key.mPackageName != null) {
+ FileLog.d(TAG, "onFinished: active install session finished for"
+ + " appPackageName=" + key.mPackageName
+ + ", sessionId=" + sessionId
+ + ", success=" + success);
String packageName = key.mPackageName;
PackageInstallInfo info = PackageInstallInfo.fromState(
success ? STATUS_INSTALLED : STATUS_FAILED,
@@ -141,6 +154,11 @@
}
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId, helper, callback);
if (sessionInfo != null) {
+ Log.d(TAG, "onBadgingChanged: badging info changed for"
+ + " appPackageName=" + sessionInfo.getAppPackageName()
+ + ", sessionId=" + sessionInfo.getSessionId()
+ + ", appIcon=" + sessionInfo.getAppIcon()
+ + ", appLabel=" + sessionInfo.getAppLabel());
helper.tryQueuePromiseAppIcon(sessionInfo);
}
}