Merge "Add running apps icons to taskbar for desktop environment." into tm-qpr-dev

This commit is contained in:
Winson Chung
2022-07-28 22:14:42 +00:00
committed by Android (Google) Code Review
10 changed files with 402 additions and 6 deletions
@@ -0,0 +1,153 @@
/*
* Copyright (C) 2022 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;
import android.content.ComponentName;
import android.util.SparseArray;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.quickstep.RecentsModel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Provides recent apps functionality specifically in a desktop environment.
*/
public class DesktopTaskbarRecentAppsController extends TaskbarRecentAppsController {
private final TaskbarActivityContext mContext;
private ArrayList<ItemInfo> mRunningApps = new ArrayList<>();
private AppInfo[] mApps;
public DesktopTaskbarRecentAppsController(TaskbarActivityContext context) {
mContext = context;
}
@Override
protected void onDestroy() {
super.onDestroy();
mApps = null;
}
@Override
protected void setApps(AppInfo[] apps) {
mApps = apps;
}
@Override
protected boolean isEnabled() {
return true;
}
/**
* Set mRunningApps to hold currently running applications using the list of currently running
* tasks. Filtering is also done to ignore applications that are already on the taskbar in the
* original hotseat.
*/
@Override
protected void updateRunningApps(SparseArray<ItemInfo> hotseatItems) {
ArrayList<AppInfo> runningApps = getRunningAppsFromTasks();
ArrayList<ItemInfo> filteredRunningApps = new ArrayList<>();
for (AppInfo runningApp : runningApps) {
boolean shouldAddOnTaskbar = true;
for (int i = 0; i < hotseatItems.size(); i++) {
if (hotseatItems.keyAt(i) >= mControllers.taskbarActivityContext.getDeviceProfile()
.numShownHotseatIcons) {
break;
}
if (hotseatItems.valueAt(i).getTargetPackage()
.equals(runningApp.getTargetPackage())) {
shouldAddOnTaskbar = false;
break;
}
}
if (shouldAddOnTaskbar) {
filteredRunningApps.add(new WorkspaceItemInfo(runningApp));
}
}
mRunningApps = filteredRunningApps;
mControllers.taskbarViewController.commitRunningAppsToUI();
}
/**
* Returns a copy of hotseatItems with the addition of currently running applications.
*/
@Override
protected ItemInfo[] updateHotseatItemInfos(ItemInfo[] hotseatItemInfos) {
// hotseatItemInfos.length would be 0 if deviceProfile.numShownHotseatIcons is 0, so we
// don't want to show anything in the hotseat
if (hotseatItemInfos.length == 0) return hotseatItemInfos;
int runningAppsIndex = 0;
ItemInfo[] newHotseatItemsInfo = Arrays.copyOf(
hotseatItemInfos, hotseatItemInfos.length + mRunningApps.size());
for (int i = hotseatItemInfos.length; i < newHotseatItemsInfo.length; i++) {
newHotseatItemsInfo[i] = mRunningApps.get(runningAppsIndex);
runningAppsIndex++;
}
return newHotseatItemsInfo;
}
/**
* Returns a list of running applications from the list of currently running tasks.
*/
private ArrayList<AppInfo> getRunningAppsFromTasks() {
ArrayList<ActivityManager.RunningTaskInfo> tasks =
RecentsModel.INSTANCE.get(mContext).getRunningTasks();
ArrayList<AppInfo> runningApps = new ArrayList<>();
// early return if apps is empty, since we would have no AppInfo to compare
if (mApps == null) {
return runningApps;
}
Set<String> seenPackages = new HashSet<>();
for (ActivityManager.RunningTaskInfo taskInfo : tasks) {
if (taskInfo.realActivity == null) continue;
// If a different task for the same package has already been handled, skip this one
String taskPackage = taskInfo.realActivity.getPackageName();
if (seenPackages.contains(taskPackage)) continue;
// Otherwise, get the corresponding AppInfo and add it to the list
seenPackages.add(taskPackage);
AppInfo app = getAppInfo(taskInfo.realActivity);
if (app == null) continue;
runningApps.add(app);
}
return runningApps;
}
/**
* Retrieves the corresponding AppInfo for the activity.
*/
private AppInfo getAppInfo(ComponentName activity) {
String packageName = activity.getPackageName();
for (AppInfo app : mApps) {
if (!packageName.equals(app.getTargetPackage())) {
continue;
}
return app;
}
return null;
}
}
@@ -32,11 +32,14 @@ public class DesktopTaskbarUIController extends TaskbarUIController {
@Override
protected void init(TaskbarControllers taskbarControllers) {
super.init(taskbarControllers);
mLauncher.getHotseat().setIconsAlpha(0f);
mControllers.taskbarViewController.updateRunningApps();
}
@Override
protected void onDestroy() {
super.onDestroy();
mLauncher.getHotseat().setIconsAlpha(1f);
}
@@ -173,13 +173,15 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
final boolean isDesktopMode = getPackageManager().hasSystemFeature(FEATURE_PC);
// Construct controllers.
mControllers = new TaskbarControllers(this,
new TaskbarDragController(this),
buttonController,
getPackageManager().hasSystemFeature(FEATURE_PC)
? new DesktopNavbarButtonsViewController(this, navButtonsView) :
new NavbarButtonsViewController(this, navButtonsView),
isDesktopMode
? new DesktopNavbarButtonsViewController(this, navButtonsView)
: new NavbarButtonsViewController(this, navButtonsView),
new RotationButtonController(this,
c.getColor(R.color.taskbar_nav_icon_light_color),
c.getColor(R.color.taskbar_nav_icon_dark_color),
@@ -202,7 +204,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
new TaskbarForceVisibleImmersiveController(this),
new TaskbarAllAppsController(this, dp),
new TaskbarInsetsController(this),
new VoiceInteractionWindowController(this));
new VoiceInteractionWindowController(this),
isDesktopMode
? new DesktopTaskbarRecentAppsController(this)
: TaskbarRecentAppsController.DEFAULT);
}
public void init(@NonNull TaskbarSharedState sharedState) {
@@ -53,6 +53,7 @@ public class TaskbarControllers {
public final TaskbarAllAppsController taskbarAllAppsController;
public final TaskbarInsetsController taskbarInsetsController;
public final VoiceInteractionWindowController voiceInteractionWindowController;
public final TaskbarRecentAppsController taskbarRecentAppsController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@@ -82,7 +83,8 @@ public class TaskbarControllers {
TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
TaskbarAllAppsController taskbarAllAppsController,
TaskbarInsetsController taskbarInsetsController,
VoiceInteractionWindowController voiceInteractionWindowController) {
VoiceInteractionWindowController voiceInteractionWindowController,
TaskbarRecentAppsController taskbarRecentAppsController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -102,6 +104,7 @@ public class TaskbarControllers {
this.taskbarAllAppsController = taskbarAllAppsController;
this.taskbarInsetsController = taskbarInsetsController;
this.voiceInteractionWindowController = voiceInteractionWindowController;
this.taskbarRecentAppsController = taskbarRecentAppsController;
}
/**
@@ -130,6 +133,7 @@ public class TaskbarControllers {
navButtonController.init(this);
taskbarInsetsController.init(this);
voiceInteractionWindowController.init(this);
taskbarRecentAppsController.init(this);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -178,6 +182,7 @@ public class TaskbarControllers {
navButtonController.onDestroy();
taskbarInsetsController.onDestroy();
voiceInteractionWindowController.onDestroy();
taskbarRecentAppsController.onDestroy();
mControllersToLog = null;
}
@@ -29,6 +29,7 @@ import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.quickstep.RecentsModel;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -42,7 +43,7 @@ import java.util.function.Predicate;
* Launcher model Callbacks for rendering taskbar.
*/
public class TaskbarModelCallbacks implements
BgDataModel.Callbacks, LauncherBindableItemsContainer {
BgDataModel.Callbacks, LauncherBindableItemsContainer, RecentsModel.RunningTasksListener {
private final SparseArray<ItemInfo> mHotseatItems = new SparseArray<>();
private List<ItemInfo> mPredictedItems = Collections.emptyList();
@@ -61,6 +62,16 @@ public class TaskbarModelCallbacks implements
public void init(TaskbarControllers controllers) {
mControllers = controllers;
if (mControllers.taskbarRecentAppsController.isEnabled()) {
RecentsModel.INSTANCE.get(mContext).registerRunningTasksListener(this);
}
}
/**
* Unregisters listeners in this class.
*/
public void unregisterListeners() {
RecentsModel.INSTANCE.get(mContext).unregisterRunningTasksListener();
}
@Override
@@ -185,6 +196,8 @@ public class TaskbarModelCallbacks implements
isHotseatEmpty = false;
}
}
hotseatItemInfos = mControllers.taskbarRecentAppsController
.updateHotseatItemInfos(hotseatItemInfos);
mContainer.updateHotseatItems(hotseatItemInfos);
final boolean finalIsHotseatEmpty = isHotseatEmpty;
@@ -195,6 +208,21 @@ public class TaskbarModelCallbacks implements
});
}
@Override
public void onRunningTasksChanged() {
updateRunningApps();
}
/** Called when there's a change in running apps to update the UI. */
public void commitRunningAppsToUI() {
commitItemsToUI();
}
/** Call TaskbarRecentAppsController to update running apps with mHotseatItems. */
public void updateRunningApps() {
mControllers.taskbarRecentAppsController.updateRunningApps(mHotseatItems);
}
@Override
public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
mControllers.taskbarPopupController.setDeepShortcutMap(deepShortcutMapCopy);
@@ -203,6 +231,7 @@ public class TaskbarModelCallbacks implements
@Override
public void bindAllApplications(AppInfo[] apps, int flags) {
mControllers.taskbarAllAppsController.setApps(apps, flags);
mControllers.taskbarRecentAppsController.setApps(apps);
}
protected void dumpLogs(String prefix, PrintWriter pw) {
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2022 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.util.SparseArray;
import androidx.annotation.CallSuper;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
/**
* Base class for providing recent apps functionality
*/
public class TaskbarRecentAppsController {
public static final TaskbarRecentAppsController DEFAULT = new TaskbarRecentAppsController();
// Initialized in init.
protected TaskbarControllers mControllers;
@CallSuper
protected void init(TaskbarControllers taskbarControllers) {
mControllers = taskbarControllers;
}
@CallSuper
protected void onDestroy() {
mControllers = null;
}
/** Stores the current {@link AppInfo} instances, no-op except in desktop environment. */
protected void setApps(AppInfo[] apps) { }
/**
* Indicates whether recent apps functionality is enabled, should return false except in
* desktop environment.
*/
protected boolean isEnabled() {
return false;
}
/** Called to update hotseatItems, no-op except in desktop environment. */
protected ItemInfo[] updateHotseatItemInfos(ItemInfo[] hotseatItems) {
return hotseatItems;
}
/** Called to update the list of currently running apps, no-op except in desktop environment. */
protected void updateRunningApps(SparseArray<ItemInfo> hotseatItems) { }
}
@@ -126,6 +126,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
public void onDestroy() {
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
mModelCallbacks.unregisterListeners();
}
public boolean areIconsVisible() {
@@ -389,6 +390,16 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
mModelCallbacks.dumpLogs(prefix + "\t", pw);
}
/** Called when there's a change in running apps to update the UI. */
public void commitRunningAppsToUI() {
mModelCallbacks.commitRunningAppsToUI();
}
/** Call TaskbarModelCallbacks to update running apps. */
public void updateRunningApps() {
mModelCallbacks.updateRunningApps();
}
/**
* Callbacks for {@link TaskbarView} to interact with its controller.
*/
@@ -62,6 +62,10 @@ public class RecentTasksList {
private TaskLoadResult mResultsBg = INVALID_RESULT;
private TaskLoadResult mResultsUi = INVALID_RESULT;
private RecentsModel.RunningTasksListener mRunningTasksListener;
// Tasks are stored in order of least recently launched to most recently launched.
private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks;
public RecentTasksList(LooperExecutor mainThreadExecutor,
KeyguardManagerCompat keyguardManager, SystemUiProxy sysUiProxy) {
mMainThreadExecutor = mainThreadExecutor;
@@ -73,7 +77,26 @@ public class RecentTasksList {
public void onRecentTasksChanged() throws RemoteException {
mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
}
@Override
public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
mMainThreadExecutor.execute(() -> {
RecentTasksList.this.onRunningTaskAppeared(taskInfo);
});
}
@Override
public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
mMainThreadExecutor.execute(() -> {
RecentTasksList.this.onRunningTaskVanished(taskInfo);
});
}
});
// We may receive onRunningTaskAppeared events later for tasks which have already been
// included in the list returned by mSysUiProxy.getRunningTasks(), or may receive
// onRunningTaskVanished for tasks not included in the returned list. These cases will be
// addressed when the tasks are added to/removed from mRunningTasks.
initRunningTasks(mSysUiProxy.getRunningTasks(Integer.MAX_VALUE));
}
@VisibleForTesting
@@ -154,6 +177,59 @@ public class RecentTasksList {
mChangeId++;
}
/**
* Registers a listener for running tasks
*/
public void registerRunningTasksListener(RecentsModel.RunningTasksListener listener) {
mRunningTasksListener = listener;
}
/**
* Removes the previously registered running tasks listener
*/
public void unregisterRunningTasksListener() {
mRunningTasksListener = null;
}
private void initRunningTasks(ArrayList<ActivityManager.RunningTaskInfo> runningTasks) {
// Tasks are retrieved in order of most recently launched/used to least recently launched.
mRunningTasks = new ArrayList<>(runningTasks);
Collections.reverse(mRunningTasks);
}
/**
* Gets the set of running tasks.
*/
public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() {
return mRunningTasks;
}
private void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
// Make sure this task is not already in the list
for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
if (taskInfo.taskId == existingTask.taskId) {
return;
}
}
mRunningTasks.add(taskInfo);
if (mRunningTasksListener != null) {
mRunningTasksListener.onRunningTasksChanged();
}
}
private void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
// Find the task from the list of running tasks, if it exists
for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
if (existingTask.taskId != taskInfo.taskId) continue;
mRunningTasks.remove(existingTask);
if (mRunningTasksListener != null) {
mRunningTasksListener.onRunningTasksChanged();
}
return;
}
}
/**
* Loads and creates a list of all the recent tasks.
*/
@@ -236,4 +236,35 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener
writer.println(prefix + "RecentsModel:");
mTaskList.dump(" ", writer);
}
/**
* Registers a listener for running tasks
*/
public void registerRunningTasksListener(RunningTasksListener listener) {
mTaskList.registerRunningTasksListener(listener);
}
/**
* Removes the previously registered running tasks listener
*/
public void unregisterRunningTasksListener() {
mTaskList.unregisterRunningTasksListener();
}
/**
* Gets the set of running tasks.
*/
public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() {
return mTaskList.getRunningTasks();
}
/**
* Listener for receiving running tasks changes
*/
public interface RunningTasksListener {
/**
* Called when there's a change to running tasks
*/
void onRunningTasksChanged();
}
}
@@ -20,12 +20,14 @@ import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -108,12 +110,14 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
private boolean mLastNavButtonAnimate;
private boolean mHasNavButtonAlphaBeenSet = false;
private Runnable mPendingSetNavButtonAlpha = null;
private Context mContext;
// TODO(141886704): Find a way to remove this
private int mLastSystemUiStateFlags;
public SystemUiProxy(Context context) {
DisplayController.INSTANCE.get(context).addChangeListener(this);
mContext = context;
}
@Override
@@ -868,4 +872,20 @@ public class SystemUiProxy implements ISystemUiProxy, DisplayController.DisplayI
}
return new ArrayList<>();
}
/**
* Gets the set of running tasks.
*/
public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
if (mRecentTasks != null
&& mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
try {
return new ArrayList<ActivityManager.RunningTaskInfo>(
Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
} catch (RemoteException e) {
Log.w(TAG, "Failed call getRunningTasks", e);
}
}
return new ArrayList<>();
}
}