Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 568e0bff authored by Gustav Sennton's avatar Gustav Sennton Committed by Android (Google) Code Review
Browse files

Merge "Show running apps in Taskbar when in Desktop Mode" into main

parents 3c8762f8 0b33b172
Loading
Loading
Loading
Loading
+46 −1
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@ import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.DesktopAppSelectView;
import com.android.wm.shell.desktopmode.IDesktopTaskListener;

import java.util.HashSet;
import java.util.Set;

/**
 * Controls the visibility of the workspace and the resumed / paused state when desktop mode
 * is enabled.
@@ -48,6 +51,7 @@ public class DesktopVisibilityController {
    private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean(
            "persist.wm.debug.desktop_stashing", false);
    private final Launcher mLauncher;
    private final Set<DesktopVisibilityListener> mDesktopVisibilityListeners = new HashSet<>();

    private int mVisibleDesktopTasksCount;
    private boolean mInOverviewState;
@@ -127,6 +131,16 @@ public class DesktopVisibilityController {
        return mVisibleDesktopTasksCount;
    }

    /** Registers a listener for Desktop Mode visibility updates. */
    public void registerDesktopVisibilityListener(DesktopVisibilityListener listener) {
        mDesktopVisibilityListeners.add(listener);
    }

    /** Removes a previously registered Desktop Mode visibility listener. */
    public void unregisterDesktopVisibilityListener(DesktopVisibilityListener listener) {
        mDesktopVisibilityListeners.remove(listener);
    }

    /**
     * Sets the number of desktop windows that are visible and updates launcher visibility based on
     * it.
@@ -140,7 +154,12 @@ public class DesktopVisibilityController {
        if (visibleTasksCount != mVisibleDesktopTasksCount) {
            final boolean wasVisible = mVisibleDesktopTasksCount > 0;
            final boolean isVisible = visibleTasksCount > 0;
            final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisible();
            mVisibleDesktopTasksCount = visibleTasksCount;
            final boolean areDesktopTasksVisibleNow = areDesktopTasksVisible();
            if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
                notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
            }

            if (!enableDesktopWindowingWallpaperActivity() && wasVisible != isVisible) {
                // TODO: b/333533253 - Remove after flag rollout
@@ -179,15 +198,22 @@ public class DesktopVisibilityController {
                    + " currentValue=" + mInOverviewState);
        }
        if (overviewStateEnabled != mInOverviewState) {
            final boolean wereDesktopTasksVisibleBefore = areDesktopTasksVisible();
            mInOverviewState = overviewStateEnabled;
            final boolean areDesktopTasksVisibleNow = areDesktopTasksVisible();
            if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
                notifyDesktopVisibilityListeners(areDesktopTasksVisibleNow);
            }

            if (enableDesktopWindowingWallpaperActivity()) {
                return;
            }
            // TODO: b/333533253 - Clean up after flag rollout

            if (mInOverviewState) {
                setLauncherViewsVisibility(View.VISIBLE);
                markLauncherResumed();
            } else if (areDesktopTasksVisible() && !mGestureInProgress) {
            } else if (areDesktopTasksVisibleNow && !mGestureInProgress) {
                // Switching out of overview state and gesture finished.
                // If desktop tasks are still visible, hide launcher again.
                setLauncherViewsVisibility(View.INVISIBLE);
@@ -196,6 +222,15 @@ public class DesktopVisibilityController {
        }
    }

    private void notifyDesktopVisibilityListeners(boolean areDesktopTasksVisible) {
        if (DEBUG) {
            Log.d(TAG, "notifyDesktopVisibilityListeners: visible=" + areDesktopTasksVisible);
        }
        for (DesktopVisibilityListener listener : mDesktopVisibilityListeners) {
            listener.onDesktopVisibilityChanged(areDesktopTasksVisible);
        }
    }

    /**
     * TODO: b/333533253 - Remove after flag rollout
     */
@@ -359,4 +394,14 @@ public class DesktopVisibilityController {
        mSelectAppToast.hide();
        mSelectAppToast = null;
    }

    /** A listener for when the user enters/exits Desktop Mode. */
    public interface DesktopVisibilityListener {
        /**
         * Callback for when the user enters or exits Desktop Mode
         *
         * @param visible whether Desktop Mode is now visible
         */
        void onDesktopVisibilityChanged(boolean visible);
    }
}
+139 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.launcher3.taskbar

import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration
import android.util.Log
import android.util.SparseArray
import androidx.annotation.VisibleForTesting
import androidx.core.util.valueIterator
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.quickstep.RecentsModel
import kotlin.collections.filterNotNull

/**
 * Shows running apps when in Desktop Mode.
 *
 * Users can enter and exit Desktop Mode at run-time, meaning this class falls back to the default
 * recent-apps behaviour when outside of Desktop Mode.
 *
 * This class should only be used if
 * [com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps] is enabled.
 */
class DesktopTaskbarRunningAppsController(
    private val recentsModel: RecentsModel,
    private val desktopVisibilityController: DesktopVisibilityController?,
) : TaskbarRecentAppsController() {

    private var apps: Array<AppInfo>? = null
    private var allRunningDesktopAppInfos: List<AppInfo>? = null
    private var runningDesktopAppInfosExceptHotseatItems: List<ItemInfo>? = null

    private val isInDesktopMode: Boolean
        get() = desktopVisibilityController?.areDesktopTasksVisible() ?: false

    override fun onDestroy() {
        super.onDestroy()
        apps = null
    }

    @VisibleForTesting
    public override fun setApps(apps: Array<AppInfo>?) {
        this.apps = apps
    }

    override fun isEnabled() = true

    @VisibleForTesting
    public override fun updateHotseatItemInfos(hotseatItems: Array<ItemInfo>?): Array<ItemInfo>? {
        val actualHotseatItems = hotseatItems ?: return super.updateHotseatItemInfos(null)
        if (!isInDesktopMode) {
            Log.d(TAG, "updateHotseatItemInfos: not in Desktop Mode")
            return hotseatItems
        }
        val newHotseatItemInfos =
            actualHotseatItems
                // Ignore predicted apps - we show running apps instead
                .filter { itemInfo -> !itemInfo.isPredictedItem }
                .toMutableList()
        val runningDesktopAppInfos =
            runningDesktopAppInfosExceptHotseatItems ?: return newHotseatItemInfos.toTypedArray()
        newHotseatItemInfos.addAll(runningDesktopAppInfos)
        return newHotseatItemInfos.toTypedArray()
    }

    @VisibleForTesting
    public override fun updateRunningApps(hotseatItems: SparseArray<ItemInfo>?) {
        if (!isInDesktopMode) {
            Log.d(TAG, "updateRunningApps: not in Desktop Mode")
            mControllers.taskbarViewController.commitRunningAppsToUI()
            return
        }
        val allRunningDesktopAppInfos = getRunningDesktopAppInfos()
        this.allRunningDesktopAppInfos = allRunningDesktopAppInfos
        runningDesktopAppInfosExceptHotseatItems =
            hotseatItems?.let {
                getRunningDesktopAppInfosExceptHotseatApps(allRunningDesktopAppInfos, it.toList())
            }

        mControllers.taskbarViewController.commitRunningAppsToUI()
    }

    private fun getRunningDesktopAppInfosExceptHotseatApps(
        allRunningDesktopAppInfos: List<AppInfo>,
        hotseatItems: List<ItemInfo>
    ): List<ItemInfo> {
        val hotseatPackages = hotseatItems.map { it.targetPackage }
        return allRunningDesktopAppInfos
            .filter { appInfo -> !hotseatPackages.contains(appInfo.targetPackage) }
            .map { WorkspaceItemInfo(it) }
    }

    private fun getRunningDesktopAppInfos(): List<AppInfo> {
        return getAppInfosFromRunningTasks(
            recentsModel.runningTasks
                .filter { taskInfo: RunningTaskInfo ->
                    taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
                }
                .toList()
        )
    }

    // TODO(b/335398876) fetch app icons from Tasks instead of AppInfos
    private fun getAppInfosFromRunningTasks(tasks: List<RunningTaskInfo>): List<AppInfo> {
        // Early return if apps is empty, since we then have no AppInfo to compare to
        if (apps == null) {
            return emptyList()
        }
        val packageNames = tasks.map { it.realActivity?.packageName }.distinct().filterNotNull()
        return packageNames
            .map { packageName -> apps?.find { app -> packageName == app.targetPackage } }
            .filterNotNull()
    }

    private fun <E> SparseArray<E>.toList(): List<E> {
        return valueIterator().asSequence().toList()
    }

    companion object {
        private const val TAG = "TabletDesktopTaskbarRunningAppsController"
    }
}
+19 −5
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;

import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
@@ -127,7 +129,9 @@ import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.NavHandle;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -244,7 +248,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {

        mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);

        final boolean isDesktopMode = getPackageManager().hasSystemFeature(FEATURE_PC);
        final boolean isPcMode = getPackageManager().hasSystemFeature(FEATURE_PC);

        // If Bubble bar is present, TaskbarControllers depends on it so build it first.
        Optional<BubbleControllers> bubbleControllersOptional = Optional.empty();
@@ -276,7 +280,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
        mControllers = new TaskbarControllers(this,
                new TaskbarDragController(this),
                buttonController,
                isDesktopMode
                isPcMode
                        ? new DesktopNavbarButtonsViewController(this, mNavigationBarPanelContext,
                                navButtonsView)
                        : new NavbarButtonsViewController(this, mNavigationBarPanelContext,
@@ -301,9 +305,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
                new VoiceInteractionWindowController(this),
                new TaskbarTranslationController(this),
                new TaskbarSpringOnStashController(this),
                isDesktopMode
                        ? new DesktopTaskbarRecentAppsController(this)
                        : TaskbarRecentAppsController.DEFAULT,
                createTaskbarRecentAppsController(isPcMode),
                new TaskbarEduTooltipController(this),
                new KeyboardQuickSwitchController(),
                new TaskbarPinningController(this),
@@ -312,6 +314,18 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
        mLauncherPrefs = LauncherPrefs.get(this);
    }

    private TaskbarRecentAppsController createTaskbarRecentAppsController(boolean isPcMode) {
        if (isPcMode) return new DesktopTaskbarRecentAppsController(this);
        // TODO(b/335401172): unify DesktopMode checks in Launcher
        final boolean showRunningAppsInDesktopMode = enableDesktopWindowingMode()
                && enableDesktopWindowingTaskbarRunningApps();
        return showRunningAppsInDesktopMode
                        ? new DesktopTaskbarRunningAppsController(
                                RecentsModel.INSTANCE.get(this),
                                LauncherActivityInterface.INSTANCE.getDesktopVisibilityController())
                        : TaskbarRecentAppsController.DEFAULT;
    }

    /** Updates {@link DeviceProfile} instances for any Taskbar windows. */
    public void updateDeviceProfile(DeviceProfile launcherDp) {
        applyDeviceProfile(launcherDp);
+30 −0
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
 */
package com.android.launcher3.taskbar;

import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;

import android.util.SparseArray;
import android.view.View;

@@ -26,6 +29,7 @@ import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
@@ -33,6 +37,7 @@ import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RecentsModel;

import java.io.PrintWriter;
@@ -62,6 +67,8 @@ public class TaskbarModelCallbacks implements
    // Used to defer any UI updates during the SUW unstash animation.
    private boolean mDeferUpdatesForSUW;
    private Runnable mDeferredUpdates;
    private DesktopVisibilityController.DesktopVisibilityListener mDesktopVisibilityListener =
            visible -> updateRunningApps();

    public TaskbarModelCallbacks(
            TaskbarActivityContext context, TaskbarView container) {
@@ -73,6 +80,15 @@ public class TaskbarModelCallbacks implements
        mControllers = controllers;
        if (mControllers.taskbarRecentAppsController.isEnabled()) {
            RecentsModel.INSTANCE.get(mContext).registerRunningTasksListener(this);

            if (shouldShowRunningAppsInDesktopMode()) {
                DesktopVisibilityController desktopVisibilityController =
                        LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
                if (desktopVisibilityController != null) {
                    desktopVisibilityController.registerDesktopVisibilityListener(
                            mDesktopVisibilityListener);
                }
            }
        }
    }

@@ -81,6 +97,20 @@ public class TaskbarModelCallbacks implements
     */
    public void unregisterListeners() {
        RecentsModel.INSTANCE.get(mContext).unregisterRunningTasksListener();

        if (shouldShowRunningAppsInDesktopMode()) {
            DesktopVisibilityController desktopVisibilityController =
                    LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
            if (desktopVisibilityController != null) {
                desktopVisibilityController.unregisterDesktopVisibilityListener(
                        mDesktopVisibilityListener);
            }
        }
    }

    private boolean shouldShowRunningAppsInDesktopMode() {
        // TODO(b/335401172): unify DesktopMode checks in Launcher
        return enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps();
    }

    @Override
+10 −3
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package com.android.quickstep;

import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.content.pm.PackageManager.FEATURE_PC;

import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -23,6 +24,8 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING;
import static com.android.quickstep.util.LogUtils.splitFailureMessage;
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;

import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -32,7 +35,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.Point;
import android.graphics.Rect;
@@ -1366,8 +1368,7 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
     * Gets the set of running tasks.
     */
    public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
        if (mRecentTasks != null
                && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
        if (mRecentTasks != null && shouldEnableRunningTasksForDesktopMode()) {
            try {
                return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
            } catch (RemoteException e) {
@@ -1377,6 +1378,12 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
        return new ArrayList<>();
    }

    private boolean shouldEnableRunningTasksForDesktopMode() {
        // TODO(b/335401172): unify DesktopMode checks in Launcher
        return (enableDesktopWindowingMode() && enableDesktopWindowingTaskbarRunningApps())
                || mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
    }

    private boolean handleMessageAsync(Message msg) {
        switch (msg.what) {
            case MSG_SET_SHELF_HEIGHT:
Loading